Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthroughThis pull request restructures the application from a plugin-centric to a developer-portal architecture, introducing new providers, API clients, pages, and routing. It removes old plugin management features, adds proposal management, updates storage mechanisms, and integrates third-party crypto data sources. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 20
Note
Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
src/styles/_global.scss (1)
1-6:⚠️ Potential issue | 🟠 MajorAvoid global
min-widthandoverflow-x: hiddenonhtml.This blocks small screens (mobile, split‑screen) and can hide legitimate overflow content. Consider scoping width constraints to a desktop-only layout container and handling small viewports with media queries instead of hiding overflow globally.
Suggested direction (scoped + responsive)
-html { +html { font-family: "Brockmann", sans-serif; font-size: 14px; font-weight: 500; - min-width: 480px; - overflow-x: hidden; } + +/* Example: constrain only the app container */ +#root { + min-width: 480px; +} + +@media (max-width: 479px) { + `#root` { + min-width: 100%; + } +}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/styles/_global.scss` around lines 1 - 6, Remove the global layout constraints applied to the html selector (the min-width: 480px and overflow-x: hidden) and instead apply them to a desktop-scoped container (e.g., add a .app-container or .layout-wrapper) and enforce the width constraint via a desktop-only media query; update the CSS by deleting the min-width and overflow-x rules from the html block and adding equivalent rules to .app-container with `@media` (min-width: 480px) { .app-container { min-width: 480px; overflow-x: hidden; } } so small viewports are not blocked and overflow is handled only for desktop layouts.src/Routes.tsx (1)
49-111:⚠️ Potential issue | 🟠 Major
createBrowserRouteris recreated on every render — this resets router state.
createBrowserRouteris called directly inside theRoutescomponent body without memoization. Every re-render creates a new router instance, causing React Router to remount the entire tree and lose navigation state, scroll position, and pending navigations. React Router warns against this.Move the router creation outside the component, or at minimum wrap it in
useMemo/useRef:Proposed fix (minimal — useMemo)
export const Routes = () => { - const router = createBrowserRouter([ + const router = useMemo(() => createBrowserRouter([ { path: routeTree.root.path, ... }, ... - ]); + ]), []); return <RouterProvider router={router} />; };However, ideally the router should be defined at module level or in a parent that doesn't re-render, since
ProtectedRouteuses hooks (useApp) that can only work inside the router context — not at definition time. Verify whether the current approach triggers observable issues with navigation state loss.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Routes.tsx` around lines 49 - 111, The Routes component recreates the router on every render because createBrowserRouter is called inside Routes; move router creation out of the component or memoize it so the router instance is stable. Specifically, ensure the createBrowserRouter call that builds routes (including children that reference ProtectedRoute, DefaultLayout, AuthLayout, etc.) is created once (module-level) or wrapped in useMemo/useRef inside Routes so RouterProvider receives a stable router; take care not to execute component-hook-using code at module scope (ProtectedRoute must remain a component reference only), so keep route elements as component JSX but ensure the router object itself is reused.src/utils/routes.ts (1)
1-55:⚠️ Potential issue | 🔴 Critical
pluginCreateandpluginUpdateroutes are defined inrouteTreebut not wired inRoutes.tsx.The
routeTreeobject defines both routes (lines 23 and 32-35), but neither is included in the router configuration. Pages link topluginUpdate(e.g.,Plugins.tsxline 112 andPluginEarnings.tsxline 256) which will generate broken links. Navigating to/plugins/createor/plugins/:pluginIdwill hit theerrorElementor render nothing.Add route handlers for both paths to
Routes.tsx:
pluginCreate(path:/plugins/create)pluginUpdate(path:/plugins/:pluginId)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/utils/routes.ts` around lines 1 - 55, routeTree defines pluginCreate and pluginUpdate but Routes.tsx never registers them, causing broken links; open Routes.tsx and add route entries using routeTree.pluginCreate.path ("/plugins/create") and routeTree.pluginUpdate.path ("/plugins/:pluginId") to the router configuration, importing and using the appropriate page components (e.g., PluginCreatePage / PluginUpdatePage or whatever components render the plugin creation and plugin detail/edit views in your codebase) so the routes are handled instead of hitting the errorElement.
🟡 Minor comments (16)
index.html-17-23 (1)
17-23:⚠️ Potential issue | 🟡 MinorAlign
og:urlwith the canonical URL (trailing slash).
Right now the canonical uses a trailing slash butog:urldoesn’t. For SEO consistency, keep them identical.Suggested fix
- <meta property="og:url" content="https://developer.vultisig.com" /> + <meta property="og:url" content="https://developer.vultisig.com/" />🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@index.html` around lines 17 - 23, The meta tag values are inconsistent: the <link rel="canonical"> uses a trailing slash but the meta property "og:url" does not; update the meta property "og:url" value to include the trailing slash (i.e., "https://developer.vultisig.com/") so it exactly matches the canonical URL for SEO consistency.src/toolkits/InputDigits.tsx-10-11 (1)
10-11:⚠️ Potential issue | 🟡 MinorRegex collapses only one dot per invocation — pasted values with 3+ dots are not fully sanitized
/(\..*)\./gis greedy: on each invocation it removes only the last dot in the matched span, leaving multiple dots in the result for pasted inputs with ≥ 3 dots.Example traces:
"1.2.3.4.5"→ one match".2.3.4."→"1.2.3.45"(3 dots remain)"1.234.567.890"→ one match".234.567."→"1.234.567890"(2 dots remain)Per-keystroke input is unaffected (at most 2 dots can accumulate), but paste from formatted numbers (e.g. currency strings) would be mishandled.
🔧 Proposed fix — keep only the first decimal point
- .replace(/(\..*)\./g, "$1"); + .replace(/\.(?=.*\.)/g, "");
/\.(?=.*\.)/gremoves every dot that has at least one more dot after it, keeping only the last (or equivalently, to keep the first dot, swap the lookahead direction).Alternatively, split/join approach to keep the first dot:
- const value = e.target.value - .replace(/[^0-9.]/g, "") - .replace(/(\..*)\./g, "$1"); + const digits = e.target.value.replace(/[^0-9.]/g, "").split("."); + const value = + digits.length > 1 + ? digits[0] + "." + digits.slice(1).join("") + : digits[0];🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/toolkits/InputDigits.tsx` around lines 10 - 11, The current sanitization in InputDigits.tsx uses .replace(/(\..*)\./g, "$1") which only collapses one dot per invocation and fails on pasted values with 3+ dots; update the second replace to remove every dot except the first by using a regex with lookahead (e.g., replace the .replace(/(\..*)\./g, "$1") call with a pattern that strips all dots that have another dot after them, such as /\.(?=.*\.)/g) so pasted strings keep only the first decimal point (alternatively implement a split/join that keeps the first dot).src/toolkits/Spin.tsx-1-1 (1)
1-1:⚠️ Potential issue | 🟡 MinorRemove the stray BOM/hidden character from the import line.
Hidden characters can break linting or tooling in some environments.🧹 Suggested fix (retype the import line)
-import { Spin as DefaultSpin, SpinProps } from "antd"; +import { Spin as DefaultSpin, SpinProps } from "antd";🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/toolkits/Spin.tsx` at line 1, The import line for the Spin component contains a stray BOM/hidden character; retype or replace the line that declares "import { Spin as DefaultSpin, SpinProps } from \"antd\";" (the import of DefaultSpin/SpinProps) to remove the invisible character so linters and tooling stop failing—ensure the file begins with a normal ASCII "import" token and save.src/utils/styled.ts-119-123 (1)
119-123:⚠️ Potential issue | 🟡 MinorHSLA annotations don't match the actual token values.
Lines 119, 123, and 160 have HSLA comments that don't align with the ColorToken constructor arguments:
- Line 119: H=208 but comment says 209
- Line 123: L=27 but comment says 24
- Line 160: S=81 and L=13 but comment says 63% and 79%
These mismatches can mislead future design adjustments. Align the comments to match the actual values.
✏️ Suggested comment alignment
- neutral300: new ColorToken(208, 24, 67), //hsla(209, 24%, 67%, 1) + neutral300: new ColorToken(208, 24, 67), //hsla(208, 24%, 67%, 1) ... - neutral700: new ColorToken(225, 7, 27), //hsla(225, 7%, 24%, 1) + neutral700: new ColorToken(225, 7, 27), //hsla(225, 7%, 27%, 1) ... - bgSuccess: new ColorToken(169, 81, 13), //hsla(169, 63%, 79%, 1) + bgSuccess: new ColorToken(169, 81, 13), //hsla(169, 81%, 13%, 1)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/utils/styled.ts` around lines 119 - 123, Update the HSLA inline comments so they match the actual ColorToken constructor arguments: for each ColorToken instance (e.g., neutral300, neutral700 and the ColorToken whose comment currently reads "hsla(...63%, 79%)"), compute the HSLA values from the constructor args (h, s, l) and replace the incorrect comment values so the hsla(...) annotation reflects the exact hue, saturation% and lightness% used by the ColorToken constructor; verify neutral300's hue is 208, neutral700's lightness is 27, and fix the mismatched token comment that shows "63% / 79%" to reflect S=81% and L=13% as per its constructor.src/pages/ProjectManagement.tsx-60-103 (1)
60-103:⚠️ Potential issue | 🟡 MinorConsider enabling Enter-key submission for better form UX.
The button is outside the form and manually triggers
form.submit()on click, which prevents users from submitting via Enter key. While this is Ant Design's recommended approach for external buttons, it bypasses standard form submission behavior.To preserve Enter-key submission while keeping the button outside the form, use the native HTML
formattribute instead:Suggested adjustment
- <Form + <Form + id="projectForm" autoComplete="off" form={form} layout="vertical" onFinish={handleFinish} requiredMark={false} >- <Stack as={Button} onClick={form.submit} $style={{ width: "300px" }}> + <Stack as={Button} htmlType="submit" form="projectForm" $style={{ width: "300px" }}> Continue </Stack>Alternatively, move the button inside the form and remove the
onClickhandler to rely on native submission.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/pages/ProjectManagement.tsx` around lines 60 - 103, The external Continue button prevents Enter-key submission; either move the button inside the Form so native submission triggers on Enter (remove the manual form.submit handler) or keep it outside but add the native HTML form association by setting the button’s form attribute to the Form instance’s id and making the AntD <Form> have matching id so pressing Enter triggers handleFinish; locate the Form component (Form, form variable, onFinish={handleFinish}) and the external Stack acting as the Button (Stack as={Button} onClick={form.submit}) and update accordingly.src/pages/NotFound.tsx-1-31 (1)
1-31:⚠️ Potential issue | 🟡 Minor“Back Home” can navigate backward instead of home.
useGoBackignores the provided path when location state exists, so the button may go back rather than to/. If the intent is always home, useuseNavigate(or rename the button to “Go Back”).✅ Option to always navigate home
-import { useGoBack } from "@/hooks/useGoBack"; +import { useNavigate } from "react-router-dom"; @@ - const goBack = useGoBack(); + const navigate = useNavigate(); @@ - <Button onClick={() => goBack(routeTree.root.path)}> + <Button onClick={() => navigate(routeTree.root.path, { replace: true })}> Back Home </Button>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/pages/NotFound.tsx` around lines 1 - 31, The "Back Home" button uses useGoBack (via useGoBack()) which can navigate backward instead of to root when location state exists; update NotFoundPage to always navigate to home by replacing useGoBack with React Router's useNavigate and call navigate(routeTree.root.path) in the Button onClick (or alternatively rename the Button to "Go Back" if you want back behavior to remain); ensure references to useGoBack in NotFoundPage are removed and routeTree.root.path is used with useNavigate so the button consistently lands on home.src/hooks/useQueries.ts-22-25 (1)
22-25:⚠️ Potential issue | 🟡 MinorAvoid lowercasing IDs for case-sensitive chains.
Lowercasingidin the query key can collapse distinct case-sensitive token identifiers into the same cache entry. Normalize only for EVM chains or keep the raw id for non‑EVM chains.💡 Suggested fix
- const queryKey = ["tokens", chain.toLowerCase(), id.toLowerCase()]; + const normalizedId = chain in evmChains ? id.toLowerCase() : id; + const queryKey = ["tokens", chain.toLowerCase(), normalizedId];🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/hooks/useQueries.ts` around lines 22 - 25, getTokenData currently lowercases id when building queryKey (const queryKey = ["tokens", chain.toLowerCase(), id.toLowerCase()]) which can collapse distinct case‑sensitive token ids; update getTokenData to only normalize id for EVM chains (or use raw id for non‑EVM) by checking Chain (e.g., if chain is in the EVM list then .toLowerCase() the id, otherwise use id as‑is) and ensure the same logic is used when creating queryKey and when calling queryClient.refetchQueries so cache keys remain consistent..env.example-1-2 (1)
1-2:⚠️ Potential issue | 🟡 MinorAdd a trailing newline to satisfy dotenv-linter.
Static analysis flagged a missing ending blank line.
🧹 Proposed fix
VITE_DEVELOPER_PORTAL_URL=http://localhost:8080 VITE_VULTISIG_SERVER=http://localhost:3000 +🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.env.example around lines 1 - 2, Add a trailing newline character at the end of the .env.example file so the last line ("VITE_VULTISIG_SERVER=http://localhost:3000") ends with a newline; this satisfies dotenv-linter's requirement for a final blank line and prevents the linter from reporting a missing ending newline.src/toolkits/Divider.tsx-38-49 (1)
38-49:⚠️ Potential issue | 🟡 MinorSwap left/right placement logic for divider lines.
leftcurrently hides the right line and shows the left;rightdoes the opposite. Swap the conditions to match expected placement.🛠️ Proposed fix
$after={{ backgroundColor, - content: placement !== "left" ? "" : "none", + content: placement !== "right" ? "" : "none", height, width, }} $before={{ backgroundColor, - content: placement !== "right" ? "" : "none", + content: placement !== "left" ? "" : "none", height, width, }}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/toolkits/Divider.tsx` around lines 38 - 49, The placement conditions for the divider pseudo-elements are inverted: in Divider.tsx update the $after and $before content checks so the right line is hidden when placement === "right" and the left line is hidden when placement === "left" — specifically in the component where $after and $before props are set, change the $after content condition to check placement !== "right" (so it shows unless placement is "right") and change the $before content condition to check placement !== "left" (so it shows unless placement is "left"); keep the rest of the styling (backgroundColor, height, width) unchanged.src/pages/PluginEarnings.tsx-116-144 (1)
116-144:⚠️ Potential issue | 🟡 MinorFix dataIndex to match the actual Transaction field.
The column's
dataIndex: "statusOnchain"references a field that doesn't exist in the Transaction type. The Transaction object only has astatusfield. ChangedataIndexto"status"to align with the field the render function correctly accesses.🔧 Fix
- dataIndex: "statusOnchain", - key: "statusOnchain", + dataIndex: "status", + key: "status", title: "Status",🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/pages/PluginEarnings.tsx` around lines 116 - 144, The column config uses dataIndex: "statusOnchain" which doesn't exist on Transaction; update the column's dataIndex to "status" so it matches the Transaction field accessed in the render callback (and also update the column key from "statusOnchain" to "status" for consistency with the dataIndex and to avoid mismatches in the column definition inside PluginEarnings.tsx).src/pages/Dashboard.tsx-46-54 (1)
46-54:⚠️ Potential issue | 🟡 MinorRevenue and user metrics are hardcoded.
"$2.3k"and"2.8k"are static strings, not derived from any data source. If these are placeholders, consider adding aTODOcomment or fetching real data. As-is, the dashboard displays misleading numbers.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/pages/Dashboard.tsx` around lines 46 - 54, The items array in Dashboard.tsx currently uses hardcoded strings ("$2.3k", "2.8k") for revenue and users, which should be replaced with real data or explicitly marked as placeholders: update the items definition (where DollarIcon and PeopleCopyIcon are used) to read dynamic values (e.g., use props/state/fetched variables like revenue and totalUsers) or, if temporary, add a clear TODO comment next to those entries indicating they are placeholders; ensure the Total Plugins value continues to use plugins.length and that any new variables are defined/fetched earlier in the component (or passed in as props) before building items.src/pages/ProposalManagement.tsx-393-395 (1)
393-395:⚠️ Potential issue | 🟡 MinorEmail field has a URL placeholder instead of an email example.
The placeholder reads
"https://your-plugin.example.com"— this is copied from the server endpoint field. It should be an email address.Proposed fix
- <Input placeholder="https://your-plugin.example.com" /> + <Input placeholder="you@example.com" />🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/pages/ProposalManagement.tsx` around lines 393 - 395, The email Input inside the Form.Item in ProposalManagement.tsx uses the wrong placeholder (a URL); update the placeholder for that Input component to a realistic email example (e.g., "name@example.com") so the Input's placeholder matches the intended email field. Locate the Input element rendered inside the Form.Item for the email field and replace the URL placeholder string with an email example.src/pages/Earnings.tsx-201-219 (1)
201-219:⚠️ Potential issue | 🟡 MinorStats are hard-coded
Values like “$2,3k” and “1.7K” will drift from real data and can mislead users. Consider deriving them from
earningsor clearly marking them as placeholders.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/pages/Earnings.tsx` around lines 201 - 219, The stats array in the Earnings component currently uses hard-coded values ("$2,3k", "1.7K") which can become stale; update the stats construction (the stats constant) to compute label values from the existing earnings data (use the earnings variable/prop or the data source used elsewhere in this file)—for example derive totalRevenue, revenueGrowth, and totalTransactions from earnings summary fields and format them consistently, or if real data is not yet available mark each value clearly as a placeholder (e.g. append "—placeholder") so consumers know they are not real; keep the same color and icon fields (CoinsAddIcon, LineChartOneIcon, NewspaperIcon) and ensure any formatting utilities used for currency/abbrev are reused or centralized.src/pages/Earnings.tsx-122-125 (1)
122-125:⚠️ Potential issue | 🟡 MinorStatus column dataIndex/key mismatch
dataIndex: "statusOnchain"doesn’t exist onTransaction, while the renderer usesstatus. This breaks built-in sorting/filtering and makes the column inconsistent.🔧 Suggested fix
- dataIndex: "statusOnchain", - key: "statusOnchain", + dataIndex: "status", + key: "status",🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/pages/Earnings.tsx` around lines 122 - 125, The column config uses dataIndex and key "statusOnchain" which doesn't exist on Transaction while the cell renderer expects "status"; update the column definition in the Earnings table (the column object with title "Status") to use dataIndex: "status" and key: "status" so built-in sorting/filtering and rendering align with the Transaction type, or alternatively adjust the Transaction model to expose a "statusOnchain" field if that name is preferred—ensure the renderer, dataIndex, and key all match the same property name.src/pages/Earnings.tsx-64-80 (1)
64-80:⚠️ Potential issue | 🟡 MinorAmount formatting mixes fiat and token units
You multiply by
baseValue(fiat conversion) but format withfeeAsset.decimalsand appendfeeAsset.symbol, producing values like “$123.0000 ETH” and excessive precision. Please align the unit: either show fiat (2–4 decimals, no token symbol) or show token amount (no fiat conversion).🧭 One possible fiat-only formatting option
- currency, - feeAsset.decimals, - )} ${feeAsset.symbol}`}</Stack> + currency, + 2, + )}`}</Stack>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/pages/Earnings.tsx` around lines 64 - 80, The current render in Earnings.tsx mixes fiat and token units by multiplying amount by baseValue but formatting with feeAsset.decimals and appending feeAsset.symbol; decide on fiat-only: keep the multiplication by baseValue, stop using feeAsset.decimals for formatting, call toValueFormat with a fiat-appropriate precision (e.g., 2–4 decimals) and currency, and remove the appended feeAsset.symbol; alternatively, if you want token amounts, remove the baseValue multiplication and format using feeAsset.decimals and append feeAsset.symbol — update the render (the anonymous render for dataIndex "amount") accordingly.src/pages/Earnings.tsx-168-178 (1)
168-178:⚠️ Potential issue | 🟡 MinorAdd
rel="noopener noreferrer"when opening external links in a new tabExternal explorer links should include
rel="noopener noreferrer"to preventwindow.openeraccess from the opened page.Suggested fix
to={explorerUrl} target="_blank" + rel="noopener noreferrer"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/pages/Earnings.tsx` around lines 168 - 178, The external explorer link uses target="_blank" on the HStack rendered as Link (the element with props to={explorerUrl} and target="_blank"); add rel="noopener noreferrer" to that HStack/Link element to prevent window.opener access when opening in a new tab (ensure the rel prop is passed through the Link component so the final anchor has rel="noopener noreferrer").
🧹 Nitpick comments (15)
src/icons/BuildingsIcon.tsx (1)
3-17: LGTM — consider defaultingaria-hidden="true"for decorative useThe component is well-structured: correct typing, proper
{...props}spread after defaults so callers can override, andstroke="currentColor"/width="1em"follow idiomatic icon patterns.One optional improvement: purely decorative icons should be hidden from screen readers. Without a default
aria-hidden="true", every call site must remember to pass the prop. Spreading it before{...props}keeps it overridable:♿ Optional: default aria-hidden for decorative icon usage
export const BuildingsIcon: FC<SVGProps<SVGSVGElement>> = (props) => ( <svg + aria-hidden="true" fill="none" height="1em"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/icons/BuildingsIcon.tsx` around lines 3 - 17, Add a default aria-hidden="true" to the BuildingsIcon SVG element so the decorative icon is hidden from screen readers, keeping the existing {...props} spread after the attributes so callers can still override it; update the SVG element inside the BuildingsIcon component to include aria-hidden="true" alongside the current attributes (fill, height, stroke, etc.) while leaving the component name and prop typing unchanged.src/icons/LoaderIcon.tsx (1)
3-17: Consider defaultingaria-hidden="true"for this decorative icon.As a loader glyph with no text content or
<title>, the SVG will be announced as an unlabelled image by screen readers whenaria-hiddenis not explicitly set by the consumer. Callers can still override it via{...props}, but a safe default prevents accidental inaccessible states.♿ Proposed change
-export const LoaderIcon: FC<SVGProps<SVGSVGElement>> = (props) => ( +export const LoaderIcon: FC<SVGProps<SVGSVGElement>> = ({ "aria-hidden": ariaHidden = true, ...props }) => ( <svg + aria-hidden={ariaHidden} fill="none" height="1em"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/icons/LoaderIcon.tsx` around lines 3 - 17, The LoaderIcon SVG should default to aria-hidden="true" to avoid being announced by screen readers; update the <svg> in the LoaderIcon component to include aria-hidden="true" and ensure the props spread ({...props}) remains so callers can override if needed (place the explicit aria-hidden attribute before the {...props} spread on the <svg> element).src/assets/logo.json (2)
1-1: Consider externalizing the embedded PNG to reduce bundle weight.The
assetsentry has"e":1with the full image stored as adata:image/png;base64,…URI (~270×275 px). Base64 encoding adds ~33% overhead over the raw binary, and the image is parsed/decoded on every page that imports this JSON. Two lighter alternatives:
- External reference: Set
"e":0,"u":"<path-to-assets>/","p":"logo-image.png". The image is fetched and cached separately by the browser.- dotLottie format (
.lottie): Bundles both animation JSON and image in a zip container, avoiding base64 entirely.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/assets/logo.json` at line 1, The assets array currently embeds a large PNG as a base64 data URI (asset object with "id":"0", "e":1 and "p":"data:image/png;base64,..."), which increases bundle size; change that asset to reference an external file or a .lottie bundle instead: set "e":0, provide a "u" base path and set "p" to the filename (e.g. "logo-image.png") so the browser can fetch/cache the raw PNG, or convert the animation into a .lottie package and replace the embedded image with the packaged asset and appropriate refId updates (update the asset object with id "0" and any refId usages in layers to match).
1-1: Single-line JSON makes git diffs unreadable.The entire animation is serialised onto one line, so any future edit produces an unintelligible diff. If this file is ever hand-edited or re-exported, consider pretty-printing it (e.g.,
jq . logo.json > logo.formatted.json) and committing the formatted version. A.prettierignoreor similar can be updated to exclude it from auto-formatting if needed.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/assets/logo.json` at line 1, The logo.json file is minified into a single line which makes git diffs unreadable; reformat it into a pretty-printed JSON (e.g., using jq . or a JSON formatter) and commit the formatted file replacing the current src/assets/logo.json, and optionally add src/assets/logo.json to .prettierignore or update project formatting rules so exporters don't re-minify it in the future; locate the file by its name "logo.json" (contains top-level keys like "layers" and "assets" and version "v":"5.7.0") and ensure the committed file remains human-readable.src/toolkits/InputDigits.tsx (1)
1-1: Remove the UTF-8 BOM characterThe file starts with a byte-order mark (
). While TypeScript parsers tolerate it, it can cause subtle issues in some toolchains (bundlers, diff tools, string comparisons of imported paths). Save the file without BOM.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/toolkits/InputDigits.tsx` at line 1, The file contains a UTF-8 BOM at the start (before the import statement "import { Input, InputProps } from \"antd\"") which can break some toolchains; remove the BOM by re-saving the src/toolkits/InputDigits.tsx file without a byte‑order mark (e.g., use your editor's "Save without BOM" or run a small script to strip the leading U+FEFF) so the import line and rest of the file start with the ASCII characters only.src/utils/styled.ts (1)
177-177: Consider removing the commented-out export.Leaving a commented public type can turn into dead code drift; either remove it or add a brief rationale if it’s intentionally parked for later.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/utils/styled.ts` at line 177, Remove the commented-out public type export to avoid dead code drift: delete the line "//export type ThemeColorKeys = keyof DefaultTheme;" from src/utils/styled.ts, or if you intend to keep it, replace it with a short rationale comment above the symbol (e.g., "// kept intentionally for future use: maps theme keys to DefaultTheme") or re-enable it by uncommenting "export type ThemeColorKeys = keyof DefaultTheme;" so the intent is explicit; reference the ThemeColorKeys and DefaultTheme identifiers when making the change.src/hooks/useResizeObserver.ts (1)
8-25: Rebind the observer if the ref target can change.Right now the effect depends only on the ref object, so if the same ref is attached to a different element (conditional render), the observer stays on the old node and size updates stop. Consider keying the effect to the current element.
🔧 Suggested update
- }, [ref]); + }, [ref.current]);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/hooks/useResizeObserver.ts` around lines 8 - 25, The effect in useLayoutEffect registers a ResizeObserver for ref.current but only depends on the ref object, so if ref.current changes the old observer remains attached; update the effect to key off the current element (e.g., capture const element = ref.current and include element in the dependency array) and ensure the cleanup disconnects the observer for the previous element before observing the new one; keep the existing updateSize and setSize logic and the ResizeObserver creation/observer.observe(element) but make sure observer.disconnect() runs for the previous observer when element changes or on unmount.src/pages/Plugins.tsx (2)
20-275: Significant code duplication betweenPlugins.tsxandProposals.tsx.The stats section (grid of 3 stat cards), header/subtitle layout,
StatePropsshape, andfetchDatapattern are nearly identical across both pages. Consider extracting shared components:
- A
PageHeadercomponent for the title + subtitle + action button pattern.- A
StatsGridcomponent for the 3-column stat cards.- A shared
useFetchPluginsAndProposalshook for the data-fetching logic.This would reduce the maintenance burden as these pages evolve.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/pages/Plugins.tsx` around lines 20 - 275, The PluginsPage duplicates header, stats grid, state shape and fetch logic found in Proposals.tsx; extract a reusable PageHeader component (title, subtitle, action Button usage), a StatsGrid component (consumes stats array and renders the 3 stat cards), and move the state/fetch logic into a hook useFetchPluginsAndProposals that returns {loading, plugins, proposals, refresh}; refactor PluginsPage to replace StateProps, the fetchData useEffectEvent, the stats array and header JSX with imports of PageHeader, StatsGrid and the new hook (useFetchPluginsAndProposals) so both PluginsPage and Proposals page can reuse them.
159-166: Sequential API calls — usePromise.allfor parallel fetching.Same issue flagged in
Proposals.tsx. These independent requests should be parallelized:Proposed fix
const fetchData = useEffectEvent(async () => { setState((prev) => ({ ...prev, loading: true })); - const plugins = await getPlugins(); - const proposals = await getProposals(); + const [plugins, proposals] = await Promise.all([ + getPlugins(), + getProposals(), + ]); setState((prev) => ({ ...prev, loading: false, plugins, proposals })); });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/pages/Plugins.tsx` around lines 159 - 166, The effect handler fetchData currently awaits getPlugins() and then getProposals() sequentially; change it to run both requests in parallel (e.g., Promise.all) inside fetchData so plugins and proposals are fetched concurrently, set loading true before the parallel call and false after both results are received, and update state with both results at once (keep the same state keys); reference the fetchData useEffectEvent and the getPlugins/getProposals calls when making the change.src/pages/Proposals.tsx (2)
187-201: Remove commented-out code.This block is dead code. If the Transactions feature is planned, track it in an issue rather than leaving commented-out JSX in the codebase.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/pages/Proposals.tsx` around lines 187 - 201, Remove the commented-out JSX block for the Transactions shortcut (the Tooltip/HStack/NewspaperIcon link that references routeTree.pluginEarnings and pluginId) from the Proposals component so no dead/commented code remains; if the feature is intended for future work, open/associate an issue instead of leaving the Tooltip/HStack/NewspaperIcon block commented out.
207-214: Sequentialawaitcalls — consider parallel fetching.
getPlugins()andgetProposals()are independent requests executed sequentially. UsingPromise.allwould cut the loading time roughly in half:Proposed fix
const fetchData = useEffectEvent(async () => { setState((prev) => ({ ...prev, loading: true })); - const plugins = await getPlugins(); - const proposals = await getProposals(); + const [plugins, proposals] = await Promise.all([ + getPlugins(), + getProposals(), + ]); setState((prev) => ({ ...prev, loading: false, plugins, proposals })); });The same pattern applies to
Plugins.tsx(Lines 159-166) andDashboard.tsx(if it fetches multiple resources in the future).🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/pages/Proposals.tsx` around lines 207 - 214, fetchData currently calls getPlugins() and getProposals() sequentially, which slows loading; change fetchData (the async passed to useEffectEvent) to run them in parallel using Promise.all (await Promise.all([getPlugins(), getProposals()])), then destructure the results and call setState once to update loading, plugins, and proposals; ensure you preserve existing error handling and loading toggles around the parallel call in fetchData.src/Routes.tsx (1)
26-41: Potential naming collision: componentSetCurrentRouteand helpersetCurrentRoute.The component and the helper function differ only in casing. While JavaScript distinguishes them, this can confuse readers and tools (e.g., auto-import). Consider renaming the helper — e.g.,
withCurrentRouteorwrapCurrentRoute— to make the intent clearer at a glance.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Routes.tsx` around lines 26 - 41, Rename the helper function setCurrentRoute to a clearer distinct name (e.g., withCurrentRoute or wrapCurrentRoute) to avoid collision with the SetCurrentRoute component; update the function declaration for setCurrentRoute and all its call sites to the new name, and keep the SetCurrentRoute component unchanged (symbols: SetCurrentRoute component, previously named setCurrentRoute helper) so imports/auto-imports and tooling no longer confuse the two.src/pages/ProposalManagement.tsx (1)
310-324: AddvalidateDebounceto debounce asyncpluginIdvalidation.The custom validator calls
validatePluginId(value)on every keystroke, triggering network requests during user input. Ant Design v6.3.0 supports thevalidateDebounceprop onForm.Itemto delay validation:Example using validateDebounce
hasFeedback + validateDebounce={500} >🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/pages/ProposalManagement.tsx` around lines 310 - 324, The inline async validator that calls validatePluginId on every keystroke should be debounced by adding Ant Design's validateDebounce prop to the Form.Item that contains this validator; update the Form.Item wrapping the validator (the one that defines the async validator calling validatePluginId) to include validateDebounce={300} (or another suitable ms) so validatePluginId is only invoked after the user pauses typing, leaving the existing async validator logic intact.src/utils/functions.ts (1)
207-222: Remove the commented-outtoKebabCaseblockDead commented code adds noise; consider deleting it and relying on git history if it’s needed later.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/utils/functions.ts` around lines 207 - 222, Remove the dead commented-out implementation of toKebabCase in this file: delete the entire commented block that references toKebabCase, toKebab, isObject, and isArray so the file contains only active code (rely on git history to restore if needed); ensure no other references or duplicated implementations remain elsewhere that would be affected by removing this comment.src/pages/Earnings.tsx (1)
321-321: Wire up the existing loading state
state.loadingis set but unused;Tablesupports aloadingprop to show progress and avoid blank flashes.♻️ Suggested fix
- <Table<Transaction> columns={columns} dataSource={earnings} rowKey="id" /> + <Table<Transaction> + columns={columns} + dataSource={earnings} + rowKey="id" + loading={state.loading} + />🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/pages/Earnings.tsx` at line 321, The Table is not using the existing loading state so the UI can flash; pass the component's loading flag into the Table by setting its loading prop to the existing state.loading (i.e., update the Table<Transaction> usage that currently references columns, dataSource={earnings}, rowKey="id" to also include loading={state.loading}) so the Table shows a spinner while earnings are being fetched.
ℹ️ Review info
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (45)
package-lock.jsonis excluded by!**/package-lock.jsonpublic/images/auth.jpgis excluded by!**/*.jpgpublic/images/avatar.pngis excluded by!**/*.pngpublic/images/failure-banner.jpgis excluded by!**/*.jpgpublic/images/metatag.pngis excluded by!**/*.pngpublic/images/not-support.jpgis excluded by!**/*.jpgpublic/images/success-banner.jpgis excluded by!**/*.jpgpublic/images/vulti.svgis excluded by!**/*.svgpublic/tokens/akash.svgis excluded by!**/*.svgpublic/tokens/arbitrum.svgis excluded by!**/*.svgpublic/tokens/avalanche.svgis excluded by!**/*.svgpublic/tokens/base.svgis excluded by!**/*.svgpublic/tokens/bitcoin-cash.svgis excluded by!**/*.svgpublic/tokens/bitcoin.svgis excluded by!**/*.svgpublic/tokens/blast.svgis excluded by!**/*.svgpublic/tokens/bsc.svgis excluded by!**/*.svgpublic/tokens/cardano.svgis excluded by!**/*.svgpublic/tokens/cosmos.svgis excluded by!**/*.svgpublic/tokens/cronoschain.svgis excluded by!**/*.svgpublic/tokens/dash.svgis excluded by!**/*.svgpublic/tokens/dogecoin.svgis excluded by!**/*.svgpublic/tokens/dydx.svgis excluded by!**/*.svgpublic/tokens/ethereum.svgis excluded by!**/*.svgpublic/tokens/hyperliquid.svgis excluded by!**/*.svgpublic/tokens/kuji.svgis excluded by!**/*.svgpublic/tokens/litecoin.svgis excluded by!**/*.svgpublic/tokens/mantle.svgis excluded by!**/*.svgpublic/tokens/mayachain.svgis excluded by!**/*.svgpublic/tokens/noble.svgis excluded by!**/*.svgpublic/tokens/optimism.svgis excluded by!**/*.svgpublic/tokens/osmosis.svgis excluded by!**/*.svgpublic/tokens/polkadot.svgis excluded by!**/*.svgpublic/tokens/polygon.svgis excluded by!**/*.svgpublic/tokens/ripple.svgis excluded by!**/*.svgpublic/tokens/sei.svgis excluded by!**/*.svgpublic/tokens/solana.svgis excluded by!**/*.svgpublic/tokens/sui.svgis excluded by!**/*.svgpublic/tokens/terra.svgis excluded by!**/*.svgpublic/tokens/terraclassic.svgis excluded by!**/*.svgpublic/tokens/thorchain.svgis excluded by!**/*.svgpublic/tokens/ton.svgis excluded by!**/*.svgpublic/tokens/tron.svgis excluded by!**/*.svgpublic/tokens/zcash.svgis excluded by!**/*.svgpublic/tokens/zksync.svgis excluded by!**/*.svgpublic/wallet-core.wasmis excluded by!**/*.wasm
📒 Files selected for processing (118)
.coderabbit.yaml.env.example.gitignoreeslint.config.jsindex.htmlknip.jsonpackage.jsonsrc/App.tsxsrc/Routes.tsxsrc/api/plugins.tssrc/api/portal.tssrc/api/third-party/client.tssrc/api/third-party/crypto.tssrc/assets/logo.jsonsrc/components/CurrencyModal.tsxsrc/components/DateView.tsxsrc/components/MiddleTruncate.tsxsrc/components/StatusModal.tsxsrc/context/App.tsxsrc/context/Core.tsxsrc/data/mockData.tssrc/hooks/useAntd.tssrc/hooks/useApp.tssrc/hooks/useExtension.tsxsrc/hooks/useFilterParams.tssrc/hooks/useGoBack.tssrc/hooks/useQueries.tssrc/hooks/useResizeObserver.tssrc/icons/AnalyticsIcon.tsxsrc/icons/ArrowBoxLeftIcon.tsxsrc/icons/ArrowBoxRightIcon.tsxsrc/icons/ArrowLeftIcon.tsxsrc/icons/BoxIcon.tsxsrc/icons/BrainIcon.tsxsrc/icons/BuildingsIcon.tsxsrc/icons/ChartFiveIcon.tsxsrc/icons/ChartSixIcon.tsxsrc/icons/CheckmarkIcon.tsxsrc/icons/CoinsAddIcon.tsxsrc/icons/CrossLargeIcon.tsxsrc/icons/CurrencyDollarIcon.tsxsrc/icons/CuteRobotIcon.tsxsrc/icons/DollarIcon.tsxsrc/icons/DotGridVerticalIcon.tsxsrc/icons/EditIcon.tsxsrc/icons/EmailTwoIcon.tsxsrc/icons/FortuneTellerBallIcon.tsxsrc/icons/ImagesFiveIcon.tsxsrc/icons/InboxEmptyIcon.tsxsrc/icons/LineChartOneIcon.tsxsrc/icons/LiveFullIcon.tsxsrc/icons/LoaderIcon.tsxsrc/icons/MacbookIcon.tsxsrc/icons/NewspaperIcon.tsxsrc/icons/PencilLineIcon.tsxsrc/icons/PeopleAddIcon.tsxsrc/icons/PeopleAddedIcon.tsxsrc/icons/PeopleCopyIcon.tsxsrc/icons/PluginIcon.tsxsrc/icons/PlusLargeIcon.tsxsrc/icons/PlusSmallIcon.tsxsrc/icons/PuzzleIcon.tsxsrc/icons/SquareArrowOutTopLeftIcon.tsxsrc/icons/SquareGridCircleIcon.tsxsrc/icons/StarIcon.tsxsrc/icons/ToolboxIcon.tsxsrc/icons/TrashCanIcon.tsxsrc/icons/WalletIcon.tsxsrc/icons/ZapIcon.tsxsrc/layouts/Auth.tsxsrc/layouts/Default.tsxsrc/main.tsxsrc/pages/AcceptInvite.tsxsrc/pages/Connect.tsxsrc/pages/Dashboard.tsxsrc/pages/Earnings.tsxsrc/pages/InternalError.tsxsrc/pages/NewPlugin.tsxsrc/pages/NotFound.tsxsrc/pages/PluginEarnings.tsxsrc/pages/PluginEdit.tsxsrc/pages/PluginMembers.tsxsrc/pages/Plugins.tsxsrc/pages/ProjectCategories.tsxsrc/pages/ProjectManagement.tsxsrc/pages/ProposalManagement.tsxsrc/pages/Proposals.tsxsrc/providers/Antd.tsxsrc/providers/App.tsxsrc/storage/constants.tssrc/storage/currency.tssrc/storage/state/del.tssrc/storage/theme.tsxsrc/storage/token.tssrc/storage/vaultId.tssrc/storage/vaults.tssrc/styled.d.tssrc/styles/_global.scsssrc/toolkits/Button.tsxsrc/toolkits/Divider.tsxsrc/toolkits/InputDigits.tsxsrc/toolkits/Rate.tsxsrc/toolkits/Spin.tsxsrc/toolkits/Stack.tssrc/utils/chain.tssrc/utils/constants.tssrc/utils/currency.tssrc/utils/eip712.tssrc/utils/extension.tssrc/utils/functions.tssrc/utils/routes.tssrc/utils/styled.tssrc/utils/theme.tssrc/utils/types.tssrc/vite-env.d.tstsconfig.node.jsonvercel.jsonvite.config.ts
💤 Files with no reviewable changes (16)
- src/icons/BoxIcon.tsx
- src/icons/PluginIcon.tsx
- src/icons/ZapIcon.tsx
- src/pages/AcceptInvite.tsx
- src/utils/eip712.ts
- src/hooks/useExtension.tsx
- src/storage/state/del.ts
- src/icons/MacbookIcon.tsx
- src/pages/NewPlugin.tsx
- src/storage/token.ts
- src/icons/EditIcon.tsx
- src/data/mockData.ts
- src/components/StatusModal.tsx
- src/storage/vaultId.ts
- src/api/plugins.ts
- src/pages/PluginEdit.tsx
| { | ||
| "entry": ["src/styled.d.ts"], | ||
| "ignore": [], | ||
| "ignoreDependencies": [] | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# First, check the repository structure and find package.json
echo "=== Repository structure ==="
git ls-files | head -20
echo ""
echo "=== Package.json contents ==="
if [ -f "package.json" ]; then
cat package.json
fi
echo ""
echo "=== Find index.html ==="
fd -a 'index\.html$'
echo ""
echo "=== Find TypeScript/TSX entrypoints ==="
fd -a '(main|index)\.(ts|tsx|js|jsx)$' src
echo ""
echo "=== Check for src/styled.d.ts ==="
if [ -f "src/styled.d.ts" ]; then
head -20 src/styled.d.ts
fiRepository: vultisig/developer-portal
Length of output: 3002
Update Knip entry to point to actual application root, not type definitions.
The src/styled.d.ts file contains only TypeScript type definitions for styled-components and cannot provide Knip with dependency usage data. The actual application entry is src/main.tsx, which should be configured in Knip to properly analyze unused dependencies. Update the entry to ["src/main.tsx"] (or use Vite's default index.html if Knip supports it).
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@knip.json` around lines 1 - 5, Update the Knip configuration in knip.json so
the "entry" points to the application root (replace "src/styled.d.ts") with the
actual app entry "src/main.tsx" (or Vite's index.html if supported) to ensure
Knip analyzes the real application code instead of type definitions; edit the
"entry" array in knip.json accordingly and remove or keep "src/styled.d.ts" only
if it's needed elsewhere.
| export const createProposal = async (proposal: Proposal): Promise<void> => { | ||
| return apiClient.post("/plugin-proposals", toSnakeCase(proposal)); | ||
| }; | ||
|
|
||
| export const delAuthToken = async (token: string): Promise<void> => { | ||
| const { token_id } = jwtDecode<{ token_id: string }>(token); | ||
|
|
||
| return apiClient.del(`/auth/tokens/${token_id}`); | ||
| }; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let's understand the file structure and locate apiClient definition
fd -t f "api" | head -20Repository: vultisig/developer-portal
Length of output: 90
🏁 Script executed:
# Search for apiClient usage and its definition
rg -n "apiClient" src/api/portal.ts -B2 -A2Repository: vultisig/developer-portal
Length of output: 1997
🏁 Script executed:
# Find where apiClient is created/imported
rg -n "apiClient\s*=|import.*apiClient" src/api --type ts --type tsxRepository: vultisig/developer-portal
Length of output: 96
🏁 Script executed:
# Check for axios instance definition
rg -n "AxiosInstance|axios\.create|new.*Axios" src/ --type ts --type tsxRepository: vultisig/developer-portal
Length of output: 96
🏁 Script executed:
# Look for del method definitions or aliases
rg -n "\.del\s*=|del\s*:" src/ --type ts --type tsxRepository: vultisig/developer-portal
Length of output: 96
🏁 Script executed:
# Find the client file
fd -type f "client" src/apiRepository: vultisig/developer-portal
Length of output: 239
🏁 Script executed:
# Read the client.ts file to see how apiClient is defined
cat src/api/client.tsRepository: vultisig/developer-portal
Length of output: 3014
🏁 Script executed:
# Alternative search for apiClient definition without type filters
rg -n "export.*apiClient|apiClient.*=" src/apiRepository: vultisig/developer-portal
Length of output: 199
🏁 Script executed:
# Check if there's a custom 'del' method defined anywhere
rg -n "\.del\s*=" src/Repository: vultisig/developer-portal
Length of output: 51
🏁 Script executed:
# Verify axios API methods - check if 'del' is being added as a custom method
rg -n "delete|del" src/api/client.tsRepository: vultisig/developer-portal
Length of output: 51
🏁 Script executed:
# Check portal.ts file completely to see all uses of apiClient
cat src/api/portal.tsRepository: vultisig/developer-portal
Length of output: 2326
apiClient.del doesn't exist—Axios uses delete, not del, and return types don't match declarations
The apiClient is a standard Axios instance created via axios.create() with no custom method aliases. Axios exposes a delete method, not del, so calls to apiClient.del() on lines 20 and 27 will fail at runtime. Additionally, both createProposal (line 14) and delAuthToken (line 20) return Axios responses but declare return type Promise<void>—they should either await the request and return void, or update the return type annotations to match AxiosResponse.
🔧 Suggested fix
export const createProposal = async (proposal: Proposal): Promise<void> => {
- return apiClient.post("/plugin-proposals", toSnakeCase(proposal));
+ await apiClient.post("/plugin-proposals", toSnakeCase(proposal));
};
export const delAuthToken = async (token: string): Promise<void> => {
const { token_id } = jwtDecode<{ token_id: string }>(token);
- return apiClient.del(`/auth/tokens/${token_id}`);
+ await apiClient.delete(`/auth/tokens/${token_id}`);
};
export const delMember = async (
pluginId: string,
address: string,
): Promise<string> => {
- const { message } = await apiClient.del<{ message: string }>(
+ const { message } = await apiClient.delete<{ message: string }>(
`/plugins/${pluginId}/team/${encodeURIComponent(address)}`,
);
return message;
};🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/api/portal.ts` around lines 13 - 21, The calls to apiClient.del in
delAuthToken are invalid (Axios uses delete) and both createProposal and
delAuthToken declare Promise<void> but actually return Axios responses; update
createProposal and delAuthToken so they either await the requests and return
nothing or change their return types to match Axios (e.g.,
Promise<AxiosResponse>). Concretely: in delAuthToken replace apiClient.del with
apiClient.delete (and keep the URL built from token_id via jwtDecode in
delAuthToken), and for both createProposal (which calls apiClient.post with
toSnakeCase(proposal)) and delAuthToken decide one approach—preferably await the
apiClient call inside createProposal and delAuthToken and let the functions
return void; if you choose to return the response instead, change the function
signatures to return Promise<AxiosResponse> and import AxiosResponse.
| if (contract) { | ||
| url = `${vultiApiUrl}/coingeicko/api/v3/simple/token_price/${platform}?contract_addresses=${contract}&vs_currencies=usd`; | ||
| } else { | ||
| if (chain === chains.BSC) { | ||
| platform = "binancecoin"; | ||
| } else if (chain === chains.Polygon) { | ||
| platform = "polygon-ecosystem-token"; | ||
| } | ||
|
|
||
| url = `${vultiApiUrl}/coingeicko/api/v3/simple/price?ids=${platform}&vs_currencies=usd`; | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
rg -n "coingeicko|coingecko" --type ts --type tsx --type js --type jsxRepository: vultisig/developer-portal
Length of output: 96
🏁 Script executed:
# Also search for backend configuration or API documentation
find . -type f \( -name "*.ts" -o -name "*.tsx" -o -name "*.js" -o -name "*.jsx" -o -name "*.md" -o -name "*.json" \) | head -50 | xargs rg -l "vulti|api.*coinge" 2>/dev/null | head -20Repository: vultisig/developer-portal
Length of output: 135
🏁 Script executed:
rg -n "coingeicko|coingecko"Repository: vultisig/developer-portal
Length of output: 359
🏁 Script executed:
# View the full crypto.ts file to understand context
wc -l src/api/third-party/crypto.tsRepository: vultisig/developer-portal
Length of output: 102
🏁 Script executed:
cat -n src/api/third-party/crypto.tsRepository: vultisig/developer-portal
Length of output: 5251
🏁 Script executed:
# Look for environment or config files
find . -type f \( -name ".env*" -o -name "*config*" -o -name "*backend*" \) 2>/dev/null | head -20Repository: vultisig/developer-portal
Length of output: 210
🏁 Script executed:
# Check for any comments or documentation mentioning coingeicko
rg -n "coingeicko" -B 2 -A 2Repository: vultisig/developer-portal
Length of output: 718
🏁 Script executed:
# Search for any backend documentation or API setup
find . -type f -name "*.md" | xargs rg -l "api\|backend\|endpoint" 2>/dev/nullRepository: vultisig/developer-portal
Length of output: 53
🏁 Script executed:
cat -n .env.exampleRepository: vultisig/developer-portal
Length of output: 173
🏁 Script executed:
# Search for vultiApiUrl definition
rg -n "vultiApiUrl" -B 2 -A 2Repository: vultisig/developer-portal
Length of output: 4406
Fix CoinGecko proxy path typo: coingeicko → coingecko
The endpoint uses coingeicko in both calls (lines 134 and 142), but the correct spelling is coingecko. This typo causes requests to 404, which are silently caught and return 0, breaking price fetching.
Fix
- url = `${vultiApiUrl}/coingeicko/api/v3/simple/token_price/${platform}?contract_addresses=${contract}&vs_currencies=usd`;
+ url = `${vultiApiUrl}/coingecko/api/v3/simple/token_price/${platform}?contract_addresses=${contract}&vs_currencies=usd`;- url = `${vultiApiUrl}/coingeicko/api/v3/simple/price?ids=${platform}&vs_currencies=usd`;
+ url = `${vultiApiUrl}/coingecko/api/v3/simple/price?ids=${platform}&vs_currencies=usd`;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (contract) { | |
| url = `${vultiApiUrl}/coingeicko/api/v3/simple/token_price/${platform}?contract_addresses=${contract}&vs_currencies=usd`; | |
| } else { | |
| if (chain === chains.BSC) { | |
| platform = "binancecoin"; | |
| } else if (chain === chains.Polygon) { | |
| platform = "polygon-ecosystem-token"; | |
| } | |
| url = `${vultiApiUrl}/coingeicko/api/v3/simple/price?ids=${platform}&vs_currencies=usd`; | |
| } | |
| if (contract) { | |
| url = `${vultiApiUrl}/coingecko/api/v3/simple/token_price/${platform}?contract_addresses=${contract}&vs_currencies=usd`; | |
| } else { | |
| if (chain === chains.BSC) { | |
| platform = "binancecoin"; | |
| } else if (chain === chains.Polygon) { | |
| platform = "polygon-ecosystem-token"; | |
| } | |
| url = `${vultiApiUrl}/coingecko/api/v3/simple/price?ids=${platform}&vs_currencies=usd`; | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/api/third-party/crypto.ts` around lines 133 - 143, Replace the misspelled
CoinGecko proxy path "coingeicko" with the correct "coingecko" in the URL
construction inside the token price logic: update both URL templates that use
vultiApiUrl (the one for contract price and the one for platform price) so the
path reads "/coingecko/api/..." instead of "/coingeicko/api/..."; verify this
change in the function building the URL where variables contract, platform,
chain and constants chains.BSC / chains.Polygon are used.
| <HStack | ||
| key={key} | ||
| onClick={() => handleSelect(key)} | ||
| $style={{ | ||
| alignItems: "center", | ||
| cursor: "pointer", | ||
| justifyContent: "space-between", | ||
| lineHeight: "32px", | ||
| ...(key === currency | ||
| ? { color: colors.accentFour.toHex() } | ||
| : {}), | ||
| }} | ||
| $hover={{ color: colors.accentFour.toHex() }} | ||
| > | ||
| <Stack as="span">{currencySymbols[key]}</Stack> | ||
| <Stack as="span">{key.toUpperCase()}</Stack> | ||
| </HStack> |
There was a problem hiding this comment.
Make currency rows keyboard-accessible.
Clickable div rows are not keyboard-navigable. Use a semantic button (or add role="button", tabIndex=0, and key handlers) so users can select with the keyboard.
♿ Suggested approach (semantic button)
- <HStack
+ <HStack
+ as="button"
key={key}
onClick={() => handleSelect(key)}
$style={{
alignItems: "center",
cursor: "pointer",
justifyContent: "space-between",
lineHeight: "32px",
+ background: "none",
+ border: "none",
+ padding: 0,
...(key === currency
? { color: colors.accentFour.toHex() }
: {}),
}}
$hover={{ color: colors.accentFour.toHex() }}
>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <HStack | |
| key={key} | |
| onClick={() => handleSelect(key)} | |
| $style={{ | |
| alignItems: "center", | |
| cursor: "pointer", | |
| justifyContent: "space-between", | |
| lineHeight: "32px", | |
| ...(key === currency | |
| ? { color: colors.accentFour.toHex() } | |
| : {}), | |
| }} | |
| $hover={{ color: colors.accentFour.toHex() }} | |
| > | |
| <Stack as="span">{currencySymbols[key]}</Stack> | |
| <Stack as="span">{key.toUpperCase()}</Stack> | |
| </HStack> | |
| <HStack | |
| as="button" | |
| key={key} | |
| onClick={() => handleSelect(key)} | |
| $style={{ | |
| alignItems: "center", | |
| cursor: "pointer", | |
| justifyContent: "space-between", | |
| lineHeight: "32px", | |
| background: "none", | |
| border: "none", | |
| padding: 0, | |
| ...(key === currency | |
| ? { color: colors.accentFour.toHex() } | |
| : {}), | |
| }} | |
| $hover={{ color: colors.accentFour.toHex() }} | |
| > | |
| <Stack as="span">{currencySymbols[key]}</Stack> | |
| <Stack as="span">{key.toUpperCase()}</Stack> | |
| </HStack> |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/components/CurrencyModal.tsx` around lines 40 - 56, The currency row
HStack items are not keyboard-accessible; update the HStack usage (the element
rendering each currency row where key, handleSelect, currencySymbols and
handleSelect are used) to be a semantic interactive control: either render it as
a native button (preferred) or add role="button", tabIndex={0}, and key handlers
for Enter/Space that call handleSelect(key). Ensure the same visual styles
remain and that onClick still calls handleSelect(key) so mouse and keyboard
behave identically.
| <Stack | ||
| as={Link} | ||
| to={routeTree.root.path} | ||
| $style={{ | ||
| color: colors.info.toHex(), | ||
| transition: "color 0.2s ease-in-out", | ||
| }} | ||
| $hover={{ color: colors.info.lighten(10).toHex() }} | ||
| > | ||
| See all plugins | ||
| </Stack> | ||
| </HStack> |
There was a problem hiding this comment.
"See all plugins" links to root (/) — circular navigation.
routeTree.root.path is /, which is the Dashboard page itself. This should link to the plugins list page.
Proposed fix
<Stack
as={Link}
- to={routeTree.root.path}
+ to={routeTree.plugins.path}
$style={{Similarly, "See all earnings" on Line 418 also links to routeTree.root.path and should link to routeTree.earnings.path.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <Stack | |
| as={Link} | |
| to={routeTree.root.path} | |
| $style={{ | |
| color: colors.info.toHex(), | |
| transition: "color 0.2s ease-in-out", | |
| }} | |
| $hover={{ color: colors.info.lighten(10).toHex() }} | |
| > | |
| See all plugins | |
| </Stack> | |
| </HStack> | |
| <Stack | |
| as={Link} | |
| to={routeTree.plugins.path} | |
| $style={{ | |
| color: colors.info.toHex(), | |
| transition: "color 0.2s ease-in-out", | |
| }} | |
| $hover={{ color: colors.info.lighten(10).toHex() }} | |
| > | |
| See all plugins | |
| </Stack> | |
| </HStack> |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/pages/Dashboard.tsx` around lines 220 - 231, The "See all plugins" Link
is pointing to routeTree.root.path (the Dashboard) causing circular navigation;
update the Link target in the Stack used as Link (the element using "as={Link}"
and "to={routeTree.root.path}") to point to the plugins list route (use
routeTree.plugins.path or the correct plugins route symbol from routeTree). Also
fix the similar issue for the "See all earnings" Link (the other Stack/Link at
the earnings section) to use routeTree.earnings.path instead of
routeTree.root.path so both links navigate to the intended pages.
| const useStyles = createStyles(({ css, cssVar, prefixCls }) => ({ | ||
| modal: css` | ||
| .${prefixCls}-modal-close { | ||
| background-color: ${cssVar.colorBgContainerDisabled}; | ||
| inset-inline-end: 24px; | ||
| top: 18px; | ||
| } | ||
|
|
||
| .${prefixCls}-modal-container { | ||
| display: flex; | ||
| flex-direction: column; | ||
| gap: 20px; | ||
| padding: 24px; | ||
| } | ||
|
|
||
| .${prefixCls}-modal-footer { | ||
| display: flex; | ||
| gap: 8px; | ||
| justify-content: center; | ||
| } | ||
|
|
||
| .${prefixCls}-modal-header { | ||
| padding-right: ${cssVar.controlHeight}; | ||
| } | ||
|
|
||
| .${prefixCls}-modal-title { | ||
| overflow: hidden; | ||
| text-overflow: ellipsis; | ||
| white-space: nowrap; | ||
| } | ||
| `, | ||
| table: css` | ||
| .${prefixCls}-table-container { | ||
| overflow: hidden; | ||
| } | ||
|
|
||
| .${prefixCls}-table-content table { | ||
| border-spacing: 0 12px; | ||
| margin: -12px 0; | ||
| } | ||
|
|
||
| .${prefixCls}-table-tbody > tr > td { | ||
| border-top: 1px solid ${cssVar.colorBorder}; | ||
|
|
||
| &:first-child { | ||
| border-inline-start: 1px solid ${cssVar.colorBorder}; | ||
| border-start-start-radius: ${cssVar.borderRadius}; | ||
| border-end-start-radius: ${cssVar.borderRadius}; | ||
| } | ||
|
|
||
| &:last-child { | ||
| border-inline-end: 1px solid ${cssVar.colorBorder}; | ||
| border-start-end-radius: ${cssVar.borderRadius}; | ||
| border-end-end-radius: ${cssVar.borderRadius}; | ||
| } | ||
| } | ||
|
|
||
| .${prefixCls}-table-thead > tr > th { | ||
| border: none; | ||
|
|
||
| &:first-child { | ||
| border-end-start-radius: ${cssVar.borderRadius}; | ||
| } | ||
|
|
||
| &:last-child { | ||
| border-end-end-radius: ${cssVar.borderRadius}; | ||
| } | ||
| } | ||
| `, | ||
| upload: css` | ||
| .${prefixCls}-upload { | ||
| &.${prefixCls}-upload-select { | ||
| overflow: hidden; | ||
| } | ||
| } | ||
|
|
||
| .${prefixCls}-upload-drag { | ||
| .${prefixCls}-upload { | ||
| overflow: hidden; | ||
| padding: 0; | ||
| } | ||
|
|
||
| .${prefixCls}-upload-drag-container { | ||
| display: block; | ||
| } | ||
| } | ||
| `, | ||
| })); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
find . -name "package.json" -o -name "package-lock.json" | head -5Repository: vultisig/developer-portal
Length of output: 103
🏁 Script executed:
rg "antd-style" -A 2 -B 2 | head -30Repository: vultisig/developer-portal
Length of output: 1888
🌐 Web query:
antd-style v4.1.0 createStyles cssVar parameter documentation
💡 Result:
In antd-style v4.1.0, createStyles does not have a cssVar parameter. The CSS-variable switch is controlled by Ant Design v6 via ConfigProvider’s theme.cssVar option (which antd-style consumes through antd’s theming). [1]
Ant Design theme.cssVar (what you’re looking for)
Ant Design’s ThemeConfig supports:
theme.cssVar: boolean | { prefix?: string; key?: string }prefix: CSS variable prefix (defaults toant)key: unique key for the theme (defaults to React 18useId) [1]
Example:
import { ConfigProvider } from 'antd';
<ConfigProvider
theme={{
cssVar: { prefix: 'my', key: 'brandA' }, // or cssVar: true
}}
>
<App />
</ConfigProvider>Why this applies to antd-style v4.1.0
antd-style@4.1.0 targets antd >= 6 (peer dependency), where theme.cssVar exists. [2]
Sources
[1] Ant Design v6 “Customize Theme” → ThemeConfig.cssVar API (prefix/key) https://ant-design.antgroup.com/docs/react/customize-theme-variable
[2] antd-style package.json v4.1.0 (peerDependencies: antd >= 6.0.0) https://app.unpkg.com/antd-style@4.1.0/files/package.json
🏁 Script executed:
cat package.json | grep -A 2 "antd-style"Repository: vultisig/developer-portal
Length of output: 145
🏁 Script executed:
cat -n src/providers/Antd.tsx | sed -n '1,120p'Repository: vultisig/developer-portal
Length of output: 4795
Remove cssVar parameter from createStyles callback—it is not supported in antd-style v4.1.0.
The createStyles function in antd-style v4.1.0 only accepts css and prefixCls parameters; cssVar is not available. To access CSS variables from Ant Design's theme, use CSS custom properties directly (e.g., var(--ant-color-bg-container-disabled)) or configure theme.cssVar in the ConfigProvider and reference tokens through the token object from useToken() hook.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/providers/Antd.tsx` around lines 113 - 200, The createStyles callback
used in useStyles currently declares a third parameter cssVar which is not
supported by antd-style v4.1.0; remove cssVar from the createStyles signature
and replace all occurrences of cssVar.* (e.g., cssVar.colorBgContainerDisabled,
cssVar.colorBorder, cssVar.borderRadius, cssVar.controlHeight) with the
appropriate CSS custom properties (for example
var(--ant-color-bg-container-disabled), var(--ant-color-border),
var(--ant-border-radius-base), var(--ant-control-height-base)) or refactor to
read tokens via useToken()/ConfigProvider.theme.cssVar and pass those tokens
into the styles; ensure the createStyles call and all references inside the
modal, table, and upload style blocks use only css and prefixCls (or token
variables) so no cssVar parameter remains.
| export const setVaults = (vaults: (Vault & AuthToken)[]) => { | ||
| setState(storageKeys.vaults, vaults); |
There was a problem hiding this comment.
Avoid persisting access/refresh tokens in localStorage.
Storing AuthToken in localStorage exposes tokens to XSS and devtools extraction. Prefer HttpOnly cookies or keep tokens in memory and persist only non‑sensitive vault metadata.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/storage/vaults.ts` around lines 12 - 13, The setVaults function is
persisting sensitive AuthToken data to storageKeys.vaults; change it to never
write access/refresh tokens (or whole AuthToken) to persistent storage—strip
token fields from each (Vault & AuthToken) entry before calling
setState(storageKeys.vaults, ...). Locate setVaults and ensure you only persist
non-sensitive vault metadata (e.g., id/name/flags) and keep actual tokens in
memory or migrate to an HttpOnly cookie flow; update any callers that rely on
persisted tokens to read them from the new in-memory/token-cookie location
instead of storageKeys.vaults.
| .replace(/[^0-9.]/g, "") | ||
| .replace(/(\..*)\./g, "$1"); | ||
|
|
||
| onChange({ ...e, target: { ...e.target, value } }); |
There was a problem hiding this comment.
{ ...e.target, value } drops all HTMLInputElement prototype-defined properties
HTMLInputElement properties (name, type, form, checked, etc.) live on the prototype, not as own enumerable properties, so { ...e.target } produces a near-empty plain object. The resulting synthetic target will only carry value, silently breaking any consumer that reads e.target.name (react-hook-form's register, Formik's handleChange, etc.).
Directly assigning to the native value setter and forwarding the original event avoids the spread entirely:
🔧 Proposed fix
- onChange({ ...e, target: { ...e.target, value } });
+ e.target.value = value;
+ onChange(e);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| onChange({ ...e, target: { ...e.target, value } }); | |
| e.target.value = value; | |
| onChange(e); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/toolkits/InputDigits.tsx` at line 13, The current onChange call in
InputDigits.tsx spreads e.target into a plain object which loses prototype
properties (so consumers expecting e.target.name, type, etc. break); instead,
set the new value directly on the original event target (e.target.value = value)
and forward the original event to onChange (e.g., call onChange(e) or
onChange?.(e)) so the native HTMLInputElement prototype properties remain intact
and downstream consumers (react-hook-form/Formik) continue to work; update the
handler where onChange({ ...e, target: { ...e.target, value } }) is used to
mutate e.target.value and pass the original event through.
| return css` | ||
| ${defaultPropertiesToString(props)} | ||
| ${$media?.lg && | ||
| css` | ||
| @media (min-width: ${screenLG}px) { | ||
| ${defaultPropertiesToString($media.lg)} | ||
| } | ||
| `} | ||
| ${$media?.md && | ||
| css` | ||
| @media (min-width: ${screenMD}px) { | ||
| ${defaultPropertiesToString($media.md)} | ||
| } | ||
| `} |
There was a problem hiding this comment.
Breakpoint order makes md override lg on wide screens.
Because md is defined after lg, both apply at widths ≥ screenLG, and the md styles win. Reorder to md → lg → xl so larger breakpoints override smaller ones.
🩹 Reorder media blocks
- ${$media?.lg &&
- css`
- `@media` (min-width: ${screenLG}px) {
- ${defaultPropertiesToString($media.lg)}
- }
- `}
- ${$media?.md &&
- css`
- `@media` (min-width: ${screenMD}px) {
- ${defaultPropertiesToString($media.md)}
- }
- `}
+ ${$media?.md &&
+ css`
+ `@media` (min-width: ${screenMD}px) {
+ ${defaultPropertiesToString($media.md)}
+ }
+ `}
+ ${$media?.lg &&
+ css`
+ `@media` (min-width: ${screenLG}px) {
+ ${defaultPropertiesToString($media.lg)}
+ }
+ `}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| return css` | |
| ${defaultPropertiesToString(props)} | |
| ${$media?.lg && | |
| css` | |
| @media (min-width: ${screenLG}px) { | |
| ${defaultPropertiesToString($media.lg)} | |
| } | |
| `} | |
| ${$media?.md && | |
| css` | |
| @media (min-width: ${screenMD}px) { | |
| ${defaultPropertiesToString($media.md)} | |
| } | |
| `} | |
| return css` | |
| ${defaultPropertiesToString(props)} | |
| ${$media?.md && | |
| css` | |
| `@media` (min-width: ${screenMD}px) { | |
| ${defaultPropertiesToString($media.md)} | |
| } | |
| `} | |
| ${$media?.lg && | |
| css` | |
| `@media` (min-width: ${screenLG}px) { | |
| ${defaultPropertiesToString($media.lg)} | |
| } | |
| `} |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/toolkits/Stack.ts` around lines 45 - 58, In Stack.ts the responsive CSS
blocks are ordered so $media.lg is emitted before $media.md, causing md rules to
override lg at wide widths; update the return css template in the Stack
component to emit media blocks in ascending breakpoint order (md → lg → xl),
i.e., move the ${$media?.md && ...} block so it appears before the ${$media?.lg
&& ...} block (and ensure any ${$media?.xl && ...} comes last), keeping the same
use of defaultPropertiesToString, screenMD, screenLG, and the $media object.
| export const personalSign = async ( | ||
| address: string, | ||
| message: string, | ||
| type: "connect" | "policy", | ||
| pluginId?: string | ||
| appId?: string, | ||
| ) => { | ||
| await isAvailable(); | ||
|
|
||
| const signature = await window.vultisig.plugin.request< | ||
| string | { error?: string } | ||
| >({ | ||
| method: "personal_sign", | ||
| params: [message, address, type, ...(pluginId ? [pluginId] : [])], | ||
| params: [message, address, ...(appId ? ["policy", appId] : ["connect"])], |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
rg -nP --type=ts --type=tsx '\bpersonalSign\s*\('Repository: vultisig/developer-portal
Length of output: 96
🏁 Script executed:
rg -nP --type=ts '\bpersonalSign\s*\('Repository: vultisig/developer-portal
Length of output: 372
🏁 Script executed:
sed -n '65,80p' src/providers/Core.tsxRepository: vultisig/developer-portal
Length of output: 526
🏁 Script executed:
sed -n '125,140p' src/providers/Core.tsxRepository: vultisig/developer-portal
Length of output: 671
🏁 Script executed:
sed -n '58,72p' src/providers/App.tsxRepository: vultisig/developer-portal
Length of output: 538
🏁 Script executed:
rg -n 'extensionAPI.*=|extensionAPI:' src/providers/App.tsx | head -20Repository: vultisig/developer-portal
Length of output: 174
🏁 Script executed:
sed -n '1,40p' src/providers/App.tsxRepository: vultisig/developer-portal
Length of output: 1541
Remove the "connect" argument from personalSign calls in Core.tsx.
The two calls at lines 71 and 131 pass "connect" as the appId parameter, which results in params = [message, address, "policy", "connect"]. However, the function logic indicates "connect" should only appear when appId is omitted entirely (producing the fallback params = [message, address, "connect"]). Either pass an actual policy ID if this is policy-scoped signing, or omit the argument entirely (as correctly done in App.tsx:64).
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/utils/extension.ts` around lines 66 - 75, The personalSign function
builds params as [message, address, "policy", appId] when appId is provided and
[message, address, "connect"] when omitted; calls in Core.tsx are incorrectly
passing the literal string "connect" as the appId which produces [message,
address, "policy", "connect"]. Update the two invocations in Core.tsx that call
personalSign(..., "connect") to either pass a real policy ID when policy-scoped
signing is intended or remove the third argument entirely to fall back to the
correct [message, address, "connect"] behavior; locate the calls by searching
for personalSign usages in Core.tsx and adjust accordingly.
Summary by CodeRabbit
New Features
Bug Fixes & Improvements
Style