-
Notifications
You must be signed in to change notification settings - Fork 4
feat(PE-8734): marketplace #903
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Conversation
📝 WalkthroughWalkthroughAdds a Marketplace feature and wiring: new pages, hooks, modals, components, global-state fields, and React Query integrations; threads marketplace identifiers through ArNS flows; removes many Jest/Playwright tests and test bootstraps; updates CI workflows, dependencies, and build tooling. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant U as User
participant V as ViewListingPage
participant Q as ReactQuery
participant M as ArNSMarketplaceWrite
participant AO as AO Network
U->>V: Open listing page
V->>Q: fetch order + domain info (useMarketplaceOrder, useDomainInfo)
Q-->>V: return order, price, domain
V->>U: render listing
U->>V: Click "Buy"
V->>Q: validate wallet & signer
V->>M: buyFixedPriceANT(antId, price)
M->>AO: submit transaction
AO-->>M: confirm tx (txId)
M-->>V: return success
V->>Q: invalidate marketplace queries
V->>U: show success + link
sequenceDiagram
autonumber
participant U as User
participant D as DomainsTable
participant L as ListNameForSaleModal
participant M as ArNSMarketplaceWrite
participant Q as ReactQuery
U->>D: Click "List for Sale"
D->>L: open modal (domain, antId)
U->>L: configure price & expiration
U->>L: confirm listing
L->>M: listNameForSale(antId, config)
M->>AO: submit transaction
AO-->>M: confirm tx
M-->>L: success
L->>Q: invalidate marketplace queries
L->>D: emit success
sequenceDiagram
autonumber
participant U as User
participant N as NotificationMenu
participant W as ContinueWorkflowModal
participant M as ArNSMarketplaceWrite
participant AO as AO Network
participant Q as ReactQuery
N->>N: detect interrupted workflows (useInterruptedWorkflows)
N->>U: show interrupt notification
U->>N: click "Continue"
N->>W: open modal with workflow details
W->>W: analyze workflow type (TRANSFER or PUSH_INTENT)
U->>W: confirm continue
W->>M: execute transfer or pushIntent
M->>AO: submit transaction
AO-->>M: confirm tx
M-->>W: success
W->>Q: refresh queries
W->>U: show success, close
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120+ minutes Possibly related PRs
Suggested labels
Suggested reviewers
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
📜 Recent review detailsConfiguration used: defaults Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Visit the preview URL for this PR (updated for commit 1788b5e): https://arns-portal--pr903-pe-8734-marketplace-x3nib7bq.web.app (expires Tue, 30 Dec 2025 15:47:59 GMT) 🔥 via Firebase Hosting GitHub Action 🌎 Sign: 1f59769442cb89cf2a92042a342e51fe94047b94 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 16
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 (2)
src/state/contexts/ArNSState.tsx (1)
71-82: AddmarketplaceProcessIdto the useEffect dependency array.The
marketplaceProcessIdis passed todispatchArNSUpdateon line 77 but is missing from the dependency array on line 82. This violates the React exhaustive-deps rule and will prevent the effect from re-running whenmarketplaceProcessIdchanges, potentially causing stale or incorrect ArNS state.Apply this diff to fix the missing dependency:
- }, [walletAddress, aoNetwork, arioProcessId, hyperbeamUrl]); + }, [walletAddress, aoNetwork, arioProcessId, hyperbeamUrl, marketplaceProcessId, antRegistryProcessId]);Note: I've also added
antRegistryProcessIdwhich appears to have the same issue (used on line 78 but not in dependencies).src/components/data-display/tables/DomainsTable.tsx (1)
518-524: Missing dependencies in useEffect.The effect uses
marketplaceProcessId,latestAntVersion, andwalletAddressinside the callback (lines 479, 472, 462), but these aren't in the dependency array. This could lead to stale table data when these values change.}, [ domainData, loading, loadingArnsState, primaryNameData, dispatchArNSState, + marketplaceProcessId, + latestAntVersion?.moduleId, + walletAddress, ]);
🟡 Minor comments (13)
src/components/pages/Settings/devtools/ANTTools.tsx-225-229 (1)
225-229: Fix text color for visibility.The
text-blackclass will render the message invisible against the darkbg-metallic-greybackground. Use a visible color liketext-greyortext-whitefor consistency with other status messages.{!walletAddress && ( - <span className="text-black text-xs"> + <span className="text-grey text-xs"> Connect wallet to spawn ANT </span> )}src/components/inputs/DateTimePicker/DateTimePicker.tsx-21-23 (1)
21-23: Consider initializing time picker to 1 hour from now instead of noon.The default time value of '12:00' might be in the past if the component is opened later in the day, which could cause confusion when the enforcement logic adjusts it.
Consider initializing to 1 hour from now:
+ const getDefaultTime = () => { + const oneHourFromNow = new Date(Date.now() + 60 * 60 * 1000); + return format(oneHourFromNow, 'HH:mm'); + }; + const [timeValue, setTimeValue] = useState<string>( - value ? format(value, 'HH:mm') : '12:00', + value ? format(value, 'HH:mm') : getDefaultTime(), );src/components/cards/MarketplaceOrderInfoCard/MarketplaceOrderInfoCard.tsx-81-81 (1)
81-81: Remove debug console.log before merge.Debug logging should not be present in production code.
- console.log(config);README.md-11-12 (1)
11-12: README uses incorrect package manager commands.The README (lines 11-12, 18, 24) contains
pnpmcommands, but the project usesyarnas evidenced byyarn.lockbeing committed andpackage.jsonspecifying"packageManager": "yarn@1.22.22". The CI workflows (.github/workflows/pr.yml, .github/workflows/staging_deploy.yml) correctly useyarn install --frozen-lockfile. Update the README to use yarn commands consistently with the project setup.src/state/actions/dispatchArIOInteraction.ts-23-27 (1)
23-27: Remove unused import NETWORK_DEFAULTS.The import of
NETWORK_DEFAULTSis not referenced anywhere in this file.Apply this diff:
import { APP_NAME, - NETWORK_DEFAULTS, WRITE_OPTIONS, } from '@src/utils/constants';src/components/modals/ManageMarketplaceARIOModal/panels/WithdrawPanel.tsx-8-8 (1)
8-8: Unused import.
connectis imported from@permaweb/aoconnectbut never used in this file.-import { connect } from '@permaweb/aoconnect';src/hooks/useMarketplaceUserAssets.ts-19-25 (1)
19-25: Query key contains an object which may cause cache misses.
aoNetwork.ARIOis included in the query key, but it's an object. React Query uses referential equality for objects in query keys. IfaoNetwork.ARIOis recreated on each render (even with the same values), it will cause unnecessary cache invalidations and refetches.Consider using a stable identifier instead:
queryKey: [ 'marketplace-user-assets', address, marketplaceProcessId, - aoNetwork.ARIO, + aoNetwork.ARIO.CU_URL, // or another stable primitive identifier ],src/components/modals/ContinueWorkflowModal/ContinueWorkflowModal.tsx-349-352 (1)
349-352: Success message text is incorrect for PUSH_INTENT workflow type.The success message always states "The ANT has been transferred to the marketplace with the correct intent ID", but for
PUSH_INTENT, the action is pushing an intent resolution, not transferring the ANT. The correct message is computed at line 227-230 but not used in this success state display.Consider using a dynamic message or differentiating the displayed text based on the workflow type.
src/components/data-display/tables/MarketplaceListingsTable.tsx-338-340 (1)
338-340: Missing dependencyantToNameMapincolumnsuseMemo.The
columnsdefinition usesantToNameMapat line 166, but it's not included in the dependency array. This could lead to stale data being displayed when ArNS records update.], - [arioTicker], + [arioTicker, antToNameMap], );src/components/pages/ViewListing/ViewListing.tsx-61-128 (1)
61-128: Missing validation: user should not be able to buy their own listing.The
handleBuyfunction validates wallet and order data but doesn't check if the buyer is the seller. This could lead to a confusing UX or unexpected behavior if a user attempts to purchase their own listing.Consider adding a check:
if (!name) { throw new Error('Domain name not found'); } + if (orderData.creator === walletAddress.toString()) { + throw new Error('Cannot purchase your own listing'); + } const marketplaceContract = new ArNSMarketplaceWrite({src/components/modals/ListNameForSaleModal/ListNameForSaleModal.tsx-52-54 (1)
52-54: Misleading comment.The comment says "Default to 1 hour from now (current day)" but the expiration date panel (FixedPricePanel line 247) enforces a minimum of 1 day from now. This default will fail validation when the user clicks Next without changing the date.
const [expirationDate, setExpirationDate] = useState<Date | undefined>( - new Date(Date.now() + 60 * 60 * 1000), // Default to 1 hour from now (current day) + new Date(Date.now() + 24 * 60 * 60 * 1000), // Default to 1 day from now (matches minDate) );src/components/data-display/tables/DomainsTable.tsx-206-252 (1)
206-252: Missing loading state handling in MarketplaceActionIcon.The component doesn't handle the loading state from
useMarketplaceOrder. Whenorderis undefined but still loading, the logic falls through to the error state tooltip (line 221-223), which could show a false error flash while data is being fetched.function MarketplaceActionIcon({ domainName, processId, }: { domainName: string; processId: string }) { const { hasIntent } = useANTIntent(processId); - const { data: order, error: orderError } = useMarketplaceOrder({ + const { data: order, error: orderError, isLoading: orderLoading } = useMarketplaceOrder({ antId: processId, }); + // Show loading state while fetching order + if (orderLoading) { + return <Activity className="w-[18px] h-[18px] text-grey animate-pulse" />; + } + // If there's an intent but no order (or order fetch failed), show Activity icon const hasOrder = order && !orderError;src/components/modals/ListNameForSaleModal/panels/ConfirmListingPanel.tsx-117-134 (1)
117-134: Potential null safety issue with walletAddress.If
walletAddressis undefined, the 'Current Owner' field will display"undefined...undefined". Add a fallback or guard.const orderSummary: Record<string, ReactNode> = { 'Listing Type': getListingTypeLabel(), 'Domain Name': domainName, 'Current Owner': - walletAddress?.toString().slice(0, 8) + - '...' + - walletAddress?.toString().slice(-8), + walletAddress + ? `${walletAddress.toString().slice(0, 8)}...${walletAddress.toString().slice(-8)}` + : 'Unknown', Expires: expirationDate
🧹 Nitpick comments (37)
src/components/pages/Settings/devtools/ANTTools.tsx (3)
31-36: Consider removingantStatefrom dependencies to allow manual clearing.Including
antStatein the dependency array means if a user clears the textarea, it will immediately reset to the default state. If intentional for devtools convenience, consider adding a "Reset to Default" button instead for clearer UX.React.useEffect(() => { - if (walletAddress && !antState) { + if (walletAddress) { const defaultState = createAntStateForOwner(walletAddress.toString()); setAntState(JSON.stringify(defaultState, null, 2)); } - }, [walletAddress, antState]); + }, [walletAddress]);
43-47: Remove unreachable code.The check at lines 45-47 is unreachable because
canSpawn(line 44) already includeswallet?.contractSignerin its condition. IfcanSpawnis false, the function returns early; if true,wallet.contractSigneris guaranteed to exist.async function handleSpawnANT() { if (!canSpawn) return; - if (!wallet?.contractSigner) { - throw new Error('No Wander Signer found'); - } setIsSpawning(true);
177-183: Consider adding real-time JSON validation.Unlike the Module ID input which shows immediate validation feedback, the JSON textarea only validates on spawn attempt. For devtools UX, consider adding a visual indicator (border color change or error message) when JSON is malformed.
src/components/inputs/DateTimePicker/DateTimePicker.tsx (6)
6-12: Export theDateTimePickerPropsinterface for reusability.The interface is not exported, which limits its reusability in other parts of the codebase that might need to reference these prop types.
Apply this diff:
-interface DateTimePickerProps { +export interface DateTimePickerProps { value?: Date; onChange: (date: Date | undefined) => void; placeholder?: string; minDate?: Date; className?: string; }
77-89: Consider the implications of returningfalsewhenselectedDateis undefined.When no date is selected,
isTimeDisabledreturnsfalsefor all times, meaning all time options appear enabled. This is somewhat inconsistent with the component's behavior, though in practice the time picker is only used after a date is selected.Consider adding a comment to clarify this design decision:
const isTimeDisabled = ( hours: number, minutes: number, selectedDate?: Date, ) => { + // When no date is selected, allow all times (will be validated on date selection) if (!selectedDate) return false; const testDateTime = new Date(selectedDate); testDateTime.setHours(hours, minutes, 0, 0); const oneHourFromNow = new Date(Date.now() + 60 * 60 * 1000); return testDateTime < oneHourFromNow; };
92-197: Move Calendar component outside to prevent recreation on every render.The
Calendarcomponent is defined insideDateTimePicker, causing it to be recreated on every render. This is inefficient and can impact performance.Move the Calendar component outside the DateTimePicker function:
+// Calendar component for date selection +const Calendar = ({ + selected, + onSelect, + isDateDisabled, +}: { + selected?: Date; + onSelect: (date: Date | undefined) => void; + isDateDisabled: (date: Date) => boolean; +}) => { + const [currentMonth, setCurrentMonth] = useState(selected || new Date()); + // ... rest of Calendar implementation +}; + function DateTimePicker({ value, onChange, placeholder = 'Select date and time', className = '', }: DateTimePickerProps) { // ... component implementation - - // Simple calendar component - const Calendar = ({ - selected, - onSelect, - }: { selected?: Date; onSelect: (date: Date | undefined) => void }) => { - // ... Calendar implementation - };Note: You'll need to pass
isDateDisabledas a prop to the Calendar component.
152-156: Consider constraining calendar navigation to valid date range.The calendar allows navigating to any month, but dates are only valid for today to 30 days from now. Users might navigate to months where all dates are disabled, which could be confusing.
Add constraints to the navigation:
const navigateMonth = (direction: 'prev' | 'next') => { const newDate = new Date(currentMonth); newDate.setMonth(newDate.getMonth() + (direction === 'next' ? 1 : -1)); - setCurrentMonth(newDate); + + // Prevent navigating to months outside the valid range + const today = new Date(); + const maxDate = new Date(); + maxDate.setDate(maxDate.getDate() + 30); + + if (newDate >= today && newDate <= maxDate) { + setCurrentMonth(newDate); + } };
162-178: Improve accessibility with ARIA attributes and labels.The calendar navigation buttons use arrow symbols without ARIA labels, which may not be clear for screen reader users.
Add ARIA attributes to improve accessibility:
<button type="button" className="p-1 hover:bg-grey rounded text-white" onClick={() => navigateMonth('prev')} + aria-label="Previous month" > ← </button> <h3 className="text-white font-medium"> {format(currentMonth, 'MMMM yyyy')} </h3> <button type="button" className="p-1 hover:bg-grey rounded text-white" onClick={() => navigateMonth('next')} + aria-label="Next month" > → </button>Similarly, consider adding
aria-labelto the hour and minute selects at Lines 239 and 268.
227-227: Reconsider the extremely high z-index value.The z-index of 99999 is very high and might cause stacking context issues or conflicts with other UI elements (e.g., modals, tooltips).
Consider using a more reasonable z-index value that fits within your application's z-index scale:
<Popover.Content className="w-80 rounded-lg border border-dark-grey bg-metallic-grey shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2" - style={{ zIndex: 99999 }} + style={{ zIndex: 1000 }} sideOffset={8} align="start" >If this high value is necessary due to other elements in your application, consider defining z-index values in a centralized constants file.
src/components/modals/ant-management/UpgradeDomainForMarketplaceModal/UpgradeDomainForMarketplaceModal.tsx (1)
65-138: Consider improving error handling in upgradeDomain.The
.catch()on line 99-101 swallows the error and only pushes tofailedUpgrades. This means theawaitresolves successfully even when the upgrade fails, which could be confusing. Also,failedUpgradesis an array but only ever contains at most one element.Consider either:
- Using a boolean flag instead of an array
- Re-throwing after recording the failure, or using try/catch instead
- const failedUpgrades = []; + let upgradeFailed = false; await dispatchANTInteraction({ // ... payload }).catch(() => { - failedUpgrades.push(processId); + upgradeFailed = true; }); - if (failedUpgrades.length) { + if (upgradeFailed) { eventEmitter.emit('error', {src/components/indicators/InterruptedWorkflowIndicator/InterruptedWorkflowIndicator.css (1)
11-14: Move transition property to base class for smooth hover-out animation.The
transitionproperty is currently only applied during hover, which means the element will snap back instantly when hover ends. Moving it to the base.interrupted-workflow-indicatorclass ensures smooth transitions in both directions.Apply this diff:
.interrupted-workflow-indicator { position: relative; display: inline-flex; align-items: center; justify-content: center; cursor: pointer; + transition: transform 0.2s ease-in-out; } .interrupted-workflow-indicator:hover { transform: scale(1.1); - transition: transform 0.2s ease-in-out; }src/components/modals/ManageMarketplaceARIOModal/ManageMarketplaceARIOModal.tsx (2)
29-31: Add accessibility label to the close button.The close button should have an accessible label for screen readers.
- <button onClick={onClose}> + <button onClick={onClose} aria-label="Close modal"> <XIcon className="size-5" /> </button>
37-59: Consider using Tabs.Trigger for better accessibility.The custom tab buttons work, but using Radix UI's
Tabs.Triggerwould provide built-in keyboard navigation and ARIA attributes. The current implementation duplicates tab state management.If keeping custom buttons, add ARIA attributes:
<button + role="tab" + aria-selected={activeTab === 'deposit'} className={`flex-1 flex items-center justify-center gap-2 px-4 py-2 rounded-md text-sm font-medium transition-all ${ activeTab === 'deposit' ? 'bg-white text-black' : 'text-grey hover:text-white' }`} onClick={() => setActiveTab('deposit')} >src/components/indicators/InterruptedWorkflowIndicator/InterruptedWorkflowIndicator.tsx (1)
24-30: Consider extracting tooltip style colors to avoid duplication.The tooltip style colors here (
#fef2f2,#fecaca,#991b1b) duplicate values fromRedPingAnimation.css(lines 67-69). Consider using CSS classes instead of inline styles for consistency and easier maintenance.<Tooltip - tooltipOverrides={{ - overlayInnerStyle: { - backgroundColor: '#fef2f2', - border: '1px solid #fecaca', - color: '#991b1b', - }, - }} + tooltipOverrides={{ + overlayClassName: 'interrupted-workflow-tooltip', + }} message={src/components/pages/Settings/devtools/MarketplaceTools.tsx (2)
33-39: Redundant state update in handleReset.Line 38 sets
newVersionmanually, but theuseEffecton lines 19-21 will sync it automatically whenminimumANTVersionForMarketplacechanges. This redundancy is harmless but unnecessary.const handleReset = () => { dispatchGlobalState({ type: 'setMinimumANTVersionForMarketplace', payload: DEFAULT_MIN_MARKETPLACE_ANT_VERSION, }); - setNewVersion(DEFAULT_MIN_MARKETPLACE_ANT_VERSION.toString()); };
23-31: Input validation accepts decimal values that get truncated.
parseIntwill truncate decimal inputs (e.g., "25.7" becomes 25). Consider using a stricter validation or informing users that only integers are accepted.const handleVersionUpdate = () => { const version = parseInt(newVersion, 10); - if (!isNaN(version) && version > 0) { + if (!isNaN(version) && version > 0 && Number.isInteger(Number(newVersion))) { dispatchGlobalState({ type: 'setMinimumANTVersionForMarketplace', payload: version, }); + } else { + // Optionally show validation error to user } };src/components/modals/ManageMarketplaceARIOModal/panels/WithdrawPanel.tsx (3)
273-285: Silent input clamping may confuse users.When the user types a value exceeding the balance, line 278 silently clamps it to
currentBalance. This could be unexpected - users might not realize their input was modified. Consider showing a visual indication or allowing the invalid input with an error message.onChange={(e) => { const val = e.target.value; if (isValidCustomFormat(val)) { const numVal = Number(val); - const clampedVal = Math.min(numVal, currentBalance); - setCustomValue(clampedVal.toString()); + setCustomValue(val); setPercentageSelected(undefined); setCustomValueError( - isValidCustomAmount(clampedVal.toString()) || '', + isValidCustomAmount(val) || '', ); setError(''); } }}
145-147: Consider simplifying the amount conversion.
amountInMARIO.valueOf().toString()could potentially be simplified if the SDK accepts the mARIO type directly, though this depends on the SDK's API.#!/bin/bash # Check the withdrawArIO method signature in the SDK rg -n "withdrawArIO" --type=ts -C 3 node_modules/@ar.io/sdk || echo "SDK not in node_modules, check docs"@ar.io/sdk ArNSMarketplaceWrite withdrawArIO method signature
149-157: Hardcoded sleep duration for blockchain confirmation.The 3-second sleep is a magic number and may not be sufficient for all network conditions. Consider making this configurable or adding a comment explaining the rationale.
- // Wait a bit for cranking to complete - await sleep(3000); + // Wait for the AO network to process the withdrawal (cranking) + // 3 seconds is typically sufficient for normal network conditions + const WITHDRAWAL_CONFIRMATION_DELAY_MS = 3000; + await sleep(WITHDRAWAL_CONFIRMATION_DELAY_MS);src/components/pages/ViewListing/ViewListing.tsx (3)
259-269: Potential runtime error ifdomainInfo?.processIdis undefined.
ArweaveTransactionIDconstructor throws an error if passed an invalid or undefined value. WhiledomainInfoshould exist at this point due to the earlier guard (line 131), accessingprocessIddirectly without a fallback could cause issues if the component re-renders in an unexpected state.Consider a defensive check or ensure the guard covers this case:
- <ArweaveID - id={new ArweaveTransactionID(domainInfo?.processId)} + <ArweaveID + id={new ArweaveTransactionID(domainInfo?.processId ?? '')}Alternatively, wrap this section with
{domainInfo?.processId && (...)}.
274-282: Type assertionas anybypasses type safety.The cast
(domainInfo.arnsRecord as any)?.endTimestampsuggests a type mismatch. This could hide issues if the record structure changes.Consider defining a proper type guard or extending the type definition to include
endTimestampfor lease records:// Define a type guard or use conditional typing const leaseEndTimestamp = domainInfo?.arnsRecord?.type === 'lease' ? (domainInfo.arnsRecord as { endTimestamp?: number }).endTimestamp : undefined;
386-406: Buy button is always enabled when not processing, even without a connected wallet.The button doesn't visually indicate that a wallet connection is required. While
handleBuywill throw an error, a better UX would disable the button or show a connect wallet prompt.<button className={`w-full font-semibold py-3 px-6 rounded transition-colors flex items-center justify-center gap-2 mt-8 ${ - isBuying - ? 'bg-grey text-white cursor-not-allowed' - : 'bg-primary hover:bg-warning text-black' + isBuying || !wallet?.contractSigner + ? 'bg-grey text-white cursor-not-allowed' + : 'bg-primary hover:bg-warning text-black' }`} onClick={handleBuy} - disabled={isBuying} + disabled={isBuying || !wallet?.contractSigner} > {isBuying ? ( <> <Loader size={20} /> Processing... </> + ) : !wallet?.contractSigner ? ( + <>Connect Wallet to Buy</> ) : ( <> <ShoppingCart className="w-5 h-5" /> Buy Now </> )} </button>src/components/modals/ContinueWorkflowModal/ContinueWorkflowModal.tsx (2)
8-8: Unused import:connectfrom@permaweb/aoconnect.The
connectfunction is imported but never used in this file.-import { connect } from '@permaweb/aoconnect';
187-188: Type assertionas anybypasses type safety forarioContract.This cast appears in multiple places in the PR. Consider creating a properly typed interface or fixing the underlying type mismatch.
src/components/data-display/tables/MarketplaceListingsTable.tsx (1)
101-105: Early return may cause unnecessary re-renders or flash of empty state.Returning an empty array when
arnsRecordsis falsy happens afterordersDatais checked. IfarnsRecordstakes longer to load, users might see an empty table briefly even when orders exist. Consider combining this check with the loading state or showing a more informative message.src/hooks/useANTIntent.ts (2)
3-3: Unused import:useDomainInfo.This import is declared but never used in the hook.
-import useDomainInfo from './useDomainInfo';
48-63:isInterruptedWorkflowandhasIntentare semantically identical.Both values are derived from
!!antIntent, making them redundant. The comment at lines 48-50 suggests that any existing intent indicates an interrupted workflow, but this naming may be misleading since not all intents represent interrupted states (some might be in-progress but not interrupted).If the intent is that "interrupted" means the intent exists but wasn't processed, consider adding more specific logic based on
antIntent.statusto differentiate active vs interrupted intents.// Check if this is an interrupted workflow - // An interrupted workflow occurs when: - // There's an intent for the ANT - const isInterruptedWorkflow = !!antIntent; + // An interrupted workflow occurs when the intent exists but is in a pending/failed state + const isInterruptedWorkflow = antIntent && antIntent.status !== 'completed';If the current behavior is intentional, consider removing one of the redundant fields or documenting why both are needed.
src/components/layout/Navbar/NotificationMenu/NotificationMenu.tsx (1)
159-163: Optional: trivial memoization.Memoizing
interruptedWorkflows.lengthprovides minimal benefit since array length access is O(1). You could simply useinterruptedWorkflows.lengthdirectly in thenotificationsuseMemo.src/components/modals/ListNameForSaleModal/panels/FixedPricePanel.tsx (1)
259-264: Unexpected navigation:window.history.back()in modal.Using
window.history.back()for the Cancel button may cause unexpected navigation if the user opened this modal from a different route or via deep linking. Consider adding anonCancelprop callback to allow the parent to control this behavior.src/components/data-display/tables/DomainsTable.tsx (2)
288-371: Optional: extract shared ContinueWorkflowModal logic.Both
RoleDisplayandInterruptedWorkflowActioncomponents manage their ownshowContinueWorkflowModalstate and renderContinueWorkflowModal. Consider extracting a shared hook or wrapper component to reduce duplication.
869-938: Consider extracting IIFE to a named component.The inline IIFE for marketplace action logic (lines 869-938) is quite complex. Extracting this to a named component like
<MarketplaceListingAction domainName={...} processId={...} ... />would improve readability and testability.src/hooks/useMarketplaceOrders.ts (2)
59-64:totalCountmay be misleading.
totalCount: allOrders.lengthreturns the number of fetched items, not the actual total count of orders in the marketplace. If the API provides a total count, it should be surfaced; otherwise, consider renaming tofetchedCountto avoid confusion.return { items: allOrders, hasMore, nextCursor: cursor, - totalCount: allOrders.length, + fetchedCount: allOrders.length, + // If the API provides totalCount, add: totalCount: result.totalCount, };
36-44: Pagination logic could be clearer.The limit calculation
Math.min(limit, (filters?.limit || limit) - allOrders.length)is complex. Since the while condition already guards against exceedingfilters.limit, simplifying to justlimitfor the per-page size would be clearer, with the outer loop handling the total constraint.- while (hasMore && (!filters?.limit || allOrders.length < filters.limit)) { - const result: any = await marketplaceContract.getPaginatedOrders({ - cursor, - limit: Math.min(limit, (filters?.limit || limit) - allOrders.length), + const targetLimit = filters?.limit ?? Infinity; + while (hasMore && allOrders.length < targetLimit) { + const remaining = targetLimit - allOrders.length; + const result: any = await marketplaceContract.getPaginatedOrders({ + cursor, + limit: Math.min(limit, remaining),src/components/modals/ListNameForSaleModal/ListNameForSaleModal.tsx (1)
122-136: Query reset predicates may be overly broad.The predicate
key.startsWith('marketplace')could reset unrelated queries if other features use similar prefixes. Consider using exact query key matching or a more specific pattern.src/state/actions/dispatchArNSUpdate.ts (1)
124-124: Remove debug logging before merge.This console.log appears to be debug output that should be removed or converted to conditional debug logging before merging to develop.
- console.log('marketplaceUserDomains', userDomains);src/components/modals/ManageMarketplaceARIOModal/panels/DepositPanel.tsx (1)
298-311: Input clamping may silently truncate user input.When the user enters a value greater than
MAX_ARIO_DEPOSIT, the input is silently clamped to the max. Consider showing immediate feedback or preventing the input rather than auto-clamping, which could confuse users.onChange={(e) => { const val = e.target.value; if (isValidCustomFormat(val)) { const numVal = Number(val); - const clampedVal = Math.min(numVal, MAX_ARIO_DEPOSIT); - setCustomValue(clampedVal.toString()); + setCustomValue(val); setButtonSelected(undefined); setCustomValueError( - isValidCustomAmount(clampedVal.toString()) || '', + isValidCustomAmount(val) || '', ); setError(''); } }}src/hooks/useInterruptedWorkflows.ts (1)
15-22: Consider stronger typing for domains parameter.The
domains: Record<string, any>parameter could be more strictly typed to match the actual domain record structure (e.g.,Record<string, AoArNSNameData>), improving type safety and IDE support.+import { AoArNSNameData } from '@ar.io/sdk'; + export function useInterruptedWorkflows( ants: Record<string, ANTProcessData>, - domains: Record<string, any>, + domains: Record<string, AoArNSNameData>, ): {
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (2)
src/components/inputs/text/ValidationInput/__snapshots__/ValidationInput.test.tsx.snapis excluded by!**/*.snapyarn.lockis excluded by!**/yarn.lock,!**/*.lock
📒 Files selected for processing (76)
.github/workflows/build_and_test.yml(2 hunks).github/workflows/pr.yml(1 hunks).github/workflows/production.yml(2 hunks).github/workflows/staging_deploy.yml(1 hunks)README.md(1 hunks)__mocks__/react-router-dom.ts(0 hunks)babel.config.cjs(0 hunks)biome.json(2 hunks)jest-setup.ts(0 hunks)jest.config.json(0 hunks)package.json(3 hunks)playwright.config.ts(0 hunks)src/App.tsx(3 hunks)src/components/animations/RedPingAnimation.css(1 hunks)src/components/cards/MarketplaceOrderInfoCard/MarketplaceOrderInfoCard.tsx(1 hunks)src/components/cards/MarketplaceOrderInfoCard/index.ts(1 hunks)src/components/cards/NavMenuCard/NavMenuCard.tsx(7 hunks)src/components/data-display/tables/DomainsTable.tsx(9 hunks)src/components/data-display/tables/MarketplaceListingsTable.tsx(1 hunks)src/components/indicators/InterruptedWorkflowIndicator/InterruptedWorkflowIndicator.css(1 hunks)src/components/indicators/InterruptedWorkflowIndicator/InterruptedWorkflowIndicator.tsx(1 hunks)src/components/inputs/DateTimePicker/DateTimePicker.tsx(1 hunks)src/components/inputs/buttons/ConnectButton/ConnectButton.test.tsx(0 hunks)src/components/inputs/text/ValidationInput/ValidationInput.test.tsx(0 hunks)src/components/layout/Navbar/NavBarLink/NavBarLink.test.tsx(0 hunks)src/components/layout/Navbar/NavGroup/NavGroup.test.tsx(0 hunks)src/components/layout/Navbar/NotificationMenu/NotificationMenu.tsx(2 hunks)src/components/layout/Notifications/Notifications.tsx(3 hunks)src/components/modals/ContinueWorkflowModal/ContinueWorkflowModal.tsx(1 hunks)src/components/modals/ListNameForSaleModal/ListNameForSaleModal.tsx(1 hunks)src/components/modals/ListNameForSaleModal/panels/ConfirmListingPanel.tsx(1 hunks)src/components/modals/ListNameForSaleModal/panels/FixedPricePanel.tsx(1 hunks)src/components/modals/LogoUploadModal/__tests__/LogoUploadModal.test.tsx(0 hunks)src/components/modals/ManageMarketplaceARIOModal/ManageMarketplaceARIOModal.tsx(1 hunks)src/components/modals/ManageMarketplaceARIOModal/index.ts(1 hunks)src/components/modals/ManageMarketplaceARIOModal/panels/DepositPanel.tsx(1 hunks)src/components/modals/ManageMarketplaceARIOModal/panels/WithdrawPanel.tsx(1 hunks)src/components/modals/ant-management/UpgradeDomainForMarketplaceModal/UpgradeDomainForMarketplaceModal.tsx(1 hunks)src/components/modals/ant-management/UpgradeDomainForMarketplaceModal/index.ts(1 hunks)src/components/modals/ant-management/UpgradeDomainForMarketplaceModal/styles.css(1 hunks)src/components/modals/index.ts(1 hunks)src/components/pages/Manage/Manage.tsx(2 hunks)src/components/pages/Marketplace/Marketplace.tsx(1 hunks)src/components/pages/Register/Checkout.tsx(2 hunks)src/components/pages/Settings/NetworkSettings.tsx(7 hunks)src/components/pages/Settings/devtools/ANTTools.tsx(2 hunks)src/components/pages/Settings/devtools/DevTools.tsx(2 hunks)src/components/pages/Settings/devtools/MarketplaceTools.tsx(1 hunks)src/components/pages/Settings/settingsConfig.ts(2 hunks)src/components/pages/ViewListing/ViewListing.tsx(1 hunks)src/components/pages/index.ts(2 hunks)src/hooks/index.ts(1 hunks)src/hooks/useANTIntent.ts(1 hunks)src/hooks/useInterruptedWorkflows.ts(1 hunks)src/hooks/useMarketplaceInfo.ts(1 hunks)src/hooks/useMarketplaceOrder.tsx(1 hunks)src/hooks/useMarketplaceOrders.ts(1 hunks)src/hooks/useMarketplaceUserAssets.ts(1 hunks)src/state/actions/dispatchArIOInteraction.ts(2 hunks)src/state/actions/dispatchArNSUpdate.ts(6 hunks)src/state/contexts/ArNSState.tsx(2 hunks)src/state/contexts/GlobalState.tsx(8 hunks)src/state/reducers/GlobalReducer.ts(3 hunks)src/utils/constants.ts(2 hunks)src/utils/imageUtils.test.ts(0 hunks)src/utils/routes.tsx(3 hunks)src/utils/searchUtils/searchUtils.test.ts(0 hunks)src/utils/searchUtils/searchUtils.ts(1 hunks)src/utils/sentry.ts(1 hunks)tests/common/fixtures/TestRecords.ts(0 hunks)tests/common/mocks/ArweaveCompositeDataProviderMock.ts(0 hunks)tests/common/mocks/svg.js(0 hunks)tests/playwright/mobile.spec.ts(0 hunks)tests/playwright/setup.ts(0 hunks)tests/unit/HomeSearch.test.tsx(0 hunks)vite.config.ts(0 hunks)
💤 Files with no reviewable changes (19)
- tests/common/mocks/svg.js
- vite.config.ts
- tests/common/fixtures/TestRecords.ts
- playwright.config.ts
- tests/playwright/mobile.spec.ts
- src/components/modals/LogoUploadModal/tests/LogoUploadModal.test.tsx
- mocks/react-router-dom.ts
- babel.config.cjs
- src/components/inputs/text/ValidationInput/ValidationInput.test.tsx
- src/components/layout/Navbar/NavGroup/NavGroup.test.tsx
- src/utils/searchUtils/searchUtils.test.ts
- src/utils/imageUtils.test.ts
- tests/unit/HomeSearch.test.tsx
- jest.config.json
- src/components/layout/Navbar/NavBarLink/NavBarLink.test.tsx
- tests/common/mocks/ArweaveCompositeDataProviderMock.ts
- src/components/inputs/buttons/ConnectButton/ConnectButton.test.tsx
- tests/playwright/setup.ts
- jest-setup.ts
🧰 Additional context used
🧠 Learnings (5)
📚 Learning: 2025-12-11T05:20:18.825Z
Learnt from: vilenarios
Repo: ar-io/arns-react PR: 902
File: src/components/modals/turbo/panels/crypto/CryptoConfirmation.tsx:536-540
Timestamp: 2025-12-11T05:20:18.825Z
Learning: Guideline: For tokens on Base network that use 6 decimals (e.g., base-ario ERC-20), convert amount to smallest units using a 1e6 multiplier. Use Math.round(amount * 1e6) when computing the on-chain value. Do not assume 18 decimals. Apply this conversion consistently in code paths that perform payments in CryptoConfirmation.tsx or similar components, and document that the base-ario/USDC-like tokens use 6 decimals to avoid mispricing or transfer errors.
Applied to files:
src/components/pages/Settings/devtools/MarketplaceTools.tsxsrc/components/pages/Manage/Manage.tsxsrc/components/pages/Register/Checkout.tsxsrc/components/modals/ContinueWorkflowModal/ContinueWorkflowModal.tsxsrc/components/modals/ant-management/UpgradeDomainForMarketplaceModal/UpgradeDomainForMarketplaceModal.tsxsrc/components/layout/Notifications/Notifications.tsxsrc/components/pages/ViewListing/ViewListing.tsxsrc/components/modals/ManageMarketplaceARIOModal/panels/WithdrawPanel.tsxsrc/components/data-display/tables/DomainsTable.tsxsrc/components/pages/Settings/devtools/DevTools.tsxsrc/components/pages/Settings/devtools/ANTTools.tsxsrc/components/data-display/tables/MarketplaceListingsTable.tsxsrc/components/modals/ListNameForSaleModal/panels/FixedPricePanel.tsxsrc/components/inputs/DateTimePicker/DateTimePicker.tsxsrc/components/modals/ManageMarketplaceARIOModal/ManageMarketplaceARIOModal.tsxsrc/components/cards/NavMenuCard/NavMenuCard.tsxsrc/components/pages/Marketplace/Marketplace.tsxsrc/components/modals/ManageMarketplaceARIOModal/panels/DepositPanel.tsxsrc/components/cards/MarketplaceOrderInfoCard/MarketplaceOrderInfoCard.tsxsrc/components/indicators/InterruptedWorkflowIndicator/InterruptedWorkflowIndicator.tsxsrc/components/modals/ListNameForSaleModal/ListNameForSaleModal.tsxsrc/components/modals/ListNameForSaleModal/panels/ConfirmListingPanel.tsxsrc/components/pages/Settings/NetworkSettings.tsxsrc/components/layout/Navbar/NotificationMenu/NotificationMenu.tsx
📚 Learning: 2025-09-10T16:35:55.727Z
Learnt from: dtfiedler
Repo: ar-io/arns-react PR: 873
File: src/components/modals/ant-management/UpgradeDomainsModal/UpgradeDomainsModal.tsx:0-0
Timestamp: 2025-09-10T16:35:55.727Z
Learning: In arns-react, when calling buildDomainInfoQuery (src/hooks/useDomainInfo.tsx), you must pass arioProcessId along with arioContract, domain, aoNetwork, hyperbeamUrl, and antRegistryProcessId; omitting arioProcessId can lead to "No ANT id or record found" and break upgrades.
Applied to files:
src/components/pages/Manage/Manage.tsxsrc/components/pages/Register/Checkout.tsxsrc/components/modals/ant-management/UpgradeDomainForMarketplaceModal/UpgradeDomainForMarketplaceModal.tsxsrc/components/data-display/tables/DomainsTable.tsxsrc/state/contexts/ArNSState.tsxsrc/state/actions/dispatchArNSUpdate.tssrc/hooks/useMarketplaceOrder.tsxsrc/components/pages/Settings/settingsConfig.tssrc/state/contexts/GlobalState.tsx
📚 Learning: 2025-09-10T16:35:55.727Z
Learnt from: dtfiedler
Repo: ar-io/arns-react PR: 873
File: src/components/modals/ant-management/UpgradeDomainsModal/UpgradeDomainsModal.tsx:0-0
Timestamp: 2025-09-10T16:35:55.727Z
Learning: In arns-react, when calling buildDomainInfoQuery (src/hooks/useDomainInfo.tsx), you must pass arioProcessId along with arioContract, domain, aoNetwork, hyperbeamUrl, and antRegistryProcessId; omitting arioProcessId can lead to “No ANT id or record found” and break upgrades.
Applied to files:
src/components/pages/Manage/Manage.tsxsrc/components/pages/Register/Checkout.tsxsrc/components/modals/ant-management/UpgradeDomainForMarketplaceModal/UpgradeDomainForMarketplaceModal.tsxsrc/components/data-display/tables/DomainsTable.tsxsrc/state/contexts/ArNSState.tsxsrc/state/actions/dispatchArNSUpdate.tssrc/hooks/useMarketplaceOrder.tsxsrc/components/pages/Settings/settingsConfig.tssrc/state/contexts/GlobalState.tsx
📚 Learning: 2025-12-02T17:38:52.889Z
Learnt from: vilenarios
Repo: ar-io/arns-react PR: 899
File: src/services/turbo/BaseTokenPurchaseService.ts:0-0
Timestamp: 2025-12-02T17:38:52.889Z
Learning: In src/services/turbo/BaseTokenPurchaseService.ts, the function executeBaseTokenPurchase intentionally only handles Base token top-up to Turbo credits, not the actual ArNS purchase. The ArNS purchase is handled separately by dispatchArIOInteraction in Checkout.tsx because it manages multiple complex purchase workflows (lease, permabuy, extend, increase undernames, etc.) with their own state management. This separation of concerns is by design.
Applied to files:
src/components/pages/Register/Checkout.tsxsrc/state/contexts/ArNSState.tsxsrc/state/actions/dispatchArNSUpdate.tssrc/state/reducers/GlobalReducer.tssrc/state/contexts/GlobalState.tsx
📚 Learning: 2025-10-22T16:07:22.654Z
Learnt from: atticusofsparta
Repo: ar-io/arns-react PR: 887
File: src/state/actions/dispatchANTInteraction.ts:0-0
Timestamp: 2025-10-22T16:07:22.654Z
Learning: In the ar.io/sdk, ANT.init accepts ContractSigner directly (any signer type) and internally resolves it, while ANT.spawn requires an AoSigner and should be passed createAoSigner(signer).
Applied to files:
src/components/pages/Settings/devtools/ANTTools.tsx
🧬 Code graph analysis (28)
src/utils/searchUtils/searchUtils.ts (1)
src/utils/constants.ts (1)
URL_REGEX(90-99)
src/components/pages/Settings/devtools/MarketplaceTools.tsx (3)
src/hooks/useMarketplaceInfo.ts (1)
useMarketplaceInfo(4-25)src/state/contexts/GlobalState.tsx (1)
useGlobalState(175-176)src/utils/constants.ts (1)
DEFAULT_MIN_MARKETPLACE_ANT_VERSION(29-29)
src/components/pages/Manage/Manage.tsx (1)
src/state/contexts/GlobalState.tsx (1)
useGlobalState(175-176)
src/hooks/useMarketplaceInfo.ts (2)
src/state/contexts/GlobalState.tsx (1)
useGlobalState(175-176)src/state/contexts/WalletState.tsx (1)
useWalletState(49-50)
src/components/layout/Notifications/Notifications.tsx (1)
src/utils/errors.ts (1)
NotificationOnlyError(4-9)
src/components/modals/ManageMarketplaceARIOModal/panels/WithdrawPanel.tsx (8)
src/state/contexts/GlobalState.tsx (1)
useGlobalState(175-176)src/state/contexts/WalletState.tsx (1)
useWalletState(49-50)src/hooks/useArIOPrice.ts (1)
useArIoPrice(9-32)src/hooks/useMarketplaceUserAssets.ts (1)
useMarketplaceUserAssets(44-61)src/utils/common/common.tsx (1)
formatARIOWithCommas(215-219)src/utils/searchUtils/searchUtils.ts (1)
sleep(128-130)src/utils/network.ts (1)
queryClient(39-45)src/components/layout/index.ts (1)
Loader(12-12)
src/components/data-display/tables/MarketplaceListingsTable.tsx (11)
src/state/contexts/GlobalState.tsx (1)
useGlobalState(175-176)src/hooks/useArIOPrice.ts (1)
useArIoPrice(9-32)src/hooks/useArNSRecords.tsx (1)
useArNSRecords(5-40)src/hooks/useMarketplaceOrders.ts (1)
useMarketplaceOrders(72-84)src/utils/searchUtils/searchUtils.ts (1)
decodeDomainToASCII(48-52)src/utils/common/common.tsx (3)
formatForMaxCharCount(134-148)formatARIOWithCommas(215-219)formatVerboseDate(21-36)src/components/data-display/index.tsx (1)
Tooltip(5-5)src/services/arweave/ArweaveTransactionID.ts (1)
ArweaveTransactionID(7-31)src/types.ts (1)
AoAddress(562-562)src/components/icons/index.ts (1)
RefreshIcon(127-127)src/components/layout/index.ts (1)
Loader(12-12)
src/components/modals/ListNameForSaleModal/panels/FixedPricePanel.tsx (4)
src/hooks/useArNSDomainPriceList.tsx (1)
useArNSDomainPriceList(9-83)src/utils/common/common.tsx (1)
formatARIOWithCommas(215-219)src/components/data-display/index.tsx (1)
Tooltip(5-5)src/components/icons/index.ts (1)
InfoIcon(115-115)
src/utils/routes.tsx (1)
src/components/pages/Marketplace/Marketplace.tsx (1)
Marketplace(6-41)
src/components/cards/NavMenuCard/NavMenuCard.tsx (3)
src/state/contexts/GlobalState.tsx (1)
useGlobalState(175-176)src/hooks/useMarketplaceUserAssets.ts (1)
buildMarketplaceUserAssetsQuery(6-42)src/components/modals/index.ts (1)
ManageMarketplaceARIOModal(14-14)
src/state/contexts/ArNSState.tsx (1)
src/state/contexts/GlobalState.tsx (1)
useGlobalState(175-176)
src/state/actions/dispatchArNSUpdate.ts (4)
src/state/reducers/ArNSReducer.ts (1)
ArNSAction(10-30)src/types.ts (1)
AoAddress(562-562)src/utils/network.ts (1)
queryClient(39-45)src/hooks/useMarketplaceUserAssets.ts (1)
buildMarketplaceUserAssetsQuery(6-42)
src/hooks/useMarketplaceOrder.tsx (2)
src/utils/constants.ts (1)
NETWORK_DEFAULTS(144-178)src/state/contexts/GlobalState.tsx (1)
useGlobalState(175-176)
src/hooks/useInterruptedWorkflows.ts (3)
src/state/contexts/ArNSState.tsx (1)
ANTProcessData(16-21)src/hooks/useMarketplaceUserAssets.ts (1)
useMarketplaceUserAssets(44-61)src/state/contexts/WalletState.tsx (1)
useWalletState(49-50)
src/hooks/useMarketplaceUserAssets.ts (3)
src/utils/constants.ts (1)
NETWORK_DEFAULTS(144-178)src/state/contexts/GlobalState.tsx (1)
useGlobalState(175-176)src/state/contexts/WalletState.tsx (1)
useWalletState(49-50)
src/components/pages/Marketplace/Marketplace.tsx (2)
src/state/contexts/GlobalState.tsx (1)
useGlobalState(175-176)src/components/modals/index.ts (1)
ManageMarketplaceARIOModal(14-14)
src/hooks/useMarketplaceOrders.ts (2)
src/utils/constants.ts (1)
NETWORK_DEFAULTS(144-178)src/state/contexts/GlobalState.tsx (1)
useGlobalState(175-176)
src/components/modals/ManageMarketplaceARIOModal/panels/DepositPanel.tsx (7)
src/state/contexts/GlobalState.tsx (1)
useGlobalState(175-176)src/state/contexts/WalletState.tsx (1)
useWalletState(49-50)src/hooks/useArIOPrice.ts (1)
useArIoPrice(9-32)src/hooks/useMarketplaceUserAssets.ts (1)
useMarketplaceUserAssets(44-61)src/utils/searchUtils/searchUtils.ts (1)
sleep(128-130)src/utils/network.ts (1)
queryClient(39-45)src/components/layout/index.ts (1)
Loader(12-12)
src/components/cards/MarketplaceOrderInfoCard/MarketplaceOrderInfoCard.tsx (8)
src/hooks/useDomainInfo.tsx (1)
useDomainInfo(206-255)src/utils/common/common.tsx (2)
formatARIOWithCommas(215-219)formatVerboseDate(21-36)src/components/icons/index.ts (1)
ArNSLogo(72-72)src/components/data-display/AntLogoIcon.tsx (1)
AntLogoIcon(7-57)src/utils/transactionUtils/transactionUtils.tsx (1)
isArweaveTransactionID(40-48)src/utils/constants.ts (1)
DEFAULT_ANT_LOGO(117-117)src/components/data-display/index.tsx (1)
Tooltip(5-5)src/utils/searchUtils/searchUtils.ts (1)
decodeDomainToASCII(48-52)
src/components/indicators/InterruptedWorkflowIndicator/InterruptedWorkflowIndicator.tsx (1)
src/components/data-display/index.tsx (1)
Tooltip(5-5)
src/components/modals/ListNameForSaleModal/ListNameForSaleModal.tsx (3)
src/hooks/useArIOPrice.ts (1)
useArIoPrice(9-32)src/hooks/useANTIntent.ts (1)
useANTIntent(17-75)src/services/arweave/ArweaveTransactionID.ts (1)
ArweaveTransactionID(7-31)
src/components/modals/ListNameForSaleModal/panels/ConfirmListingPanel.tsx (10)
src/state/contexts/WalletState.tsx (1)
useWalletState(49-50)src/hooks/useDomainInfo.tsx (1)
useDomainInfo(206-255)src/utils/transactionUtils/transactionUtils.tsx (1)
isArweaveTransactionID(40-48)src/hooks/useMarketplaceInfo.ts (1)
useMarketplaceInfo(4-25)src/utils/common/common.tsx (1)
formatARIOWithCommas(215-219)src/components/data-display/index.tsx (1)
Tooltip(5-5)src/components/icons/index.ts (2)
InfoIcon(115-115)ArNSLogo(72-72)src/utils/constants.ts (1)
DEFAULT_ANT_LOGO(117-117)src/utils/searchUtils/searchUtils.ts (1)
decodeDomainToASCII(48-52)src/components/inputs/Checkbox.tsx (1)
Checkbox(46-46)
src/App.tsx (1)
src/utils/routes.tsx (1)
Route(27-35)
src/components/pages/Settings/settingsConfig.ts (1)
src/utils/constants.ts (1)
DEFAULT_MIN_MARKETPLACE_ANT_VERSION(29-29)
src/components/pages/Settings/NetworkSettings.tsx (2)
src/utils/searchUtils/searchUtils.ts (1)
isValidNetworkURL(204-241)src/utils/constants.ts (1)
NETWORK_DEFAULTS(144-178)
src/hooks/useANTIntent.ts (2)
src/hooks/useMarketplaceUserAssets.ts (1)
useMarketplaceUserAssets(44-61)src/state/contexts/WalletState.tsx (1)
useWalletState(49-50)
src/components/layout/Navbar/NotificationMenu/NotificationMenu.tsx (4)
src/hooks/useInterruptedWorkflows.ts (1)
useInterruptedWorkflows(15-67)src/utils/searchUtils/searchUtils.ts (1)
getAntsRequiringUpdate(136-162)src/components/data-display/index.tsx (1)
Tooltip(5-5)src/components/icons/index.ts (1)
BellIcon(87-87)
src/state/contexts/GlobalState.tsx (1)
src/utils/constants.ts (2)
MARKETPLACE_PROCESS_ID(103-105)DEFAULT_MIN_MARKETPLACE_ANT_VERSION(29-29)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build_for_deploy
|
|
||
| // set the log level of ar-io-sdk | ||
| Logger.default.setLogLevel('none'); | ||
| Logger.default.setLogLevel('debug'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reconsider enabling debug logging in all environments.
Setting the log level to 'debug' for the ar-io-sdk will apply to all environments, including production. This could negatively impact performance, expose sensitive information in browser consoles, and increase noise in production logs.
Consider making the log level environment-dependent:
-Logger.default.setLogLevel('debug');
+Logger.default.setLogLevel(
+ import.meta.env.MODE === 'production' ? 'none' : 'debug'
+);📝 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.
| Logger.default.setLogLevel('debug'); | |
| Logger.default.setLogLevel( | |
| import.meta.env.MODE === 'production' ? 'none' : 'debug' | |
| ); |
🤖 Prompt for AI Agents
In src/App.tsx around line 26, the code unconditionally sets
Logger.default.setLogLevel('debug'), which forces debug logging in all
environments; change this to be environment-dependent by reading NODE_ENV (or
the app's env var like REACT_APP_ENV), and only set 'debug' when NODE_ENV ===
'development' (or the explicit dev env), otherwise set a less verbose default
such as 'info' or 'warn'; ensure a safe fallback if the env var is missing so
production never defaults to 'debug'.
| import { Tooltip } from '@src/components/data-display'; | ||
| import { AntLogoIcon } from '@src/components/data-display/AntLogoIcon'; | ||
| import { ArNSLogo } from '@src/components/icons'; | ||
| import { Checkbox } from '@src/components/inputs/Checkbox'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Remove unused import.
Checkbox is imported but not used anywhere in this component.
-import { Checkbox } from '@src/components/inputs/Checkbox';📝 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.
| import { Checkbox } from '@src/components/inputs/Checkbox'; |
🤖 Prompt for AI Agents
In src/components/cards/MarketplaceOrderInfoCard/MarketplaceOrderInfoCard.tsx
around line 5, there is an unused import "Checkbox" from
'@src/components/inputs/Checkbox'; remove that import line to eliminate the
unused dependency and resolve the linter warning, and run the project/linter to
ensure no further unused-imports remain.
src/components/data-display/tables/MarketplaceListingsTable.tsx
Outdated
Show resolved
Hide resolved
| <Link | ||
| to={`/marketplace/listing/${name}`} | ||
| className="text-link hover:text-primary-light transition-colors font-medium" | ||
| > | ||
| {formatForMaxCharCount(decodedName, 30)} | ||
| </Link> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Inconsistent URL path for listing links.
Line 157 uses /marketplace/listing/${name} while line 125 uses /marketplace/names/${name}. These should be consistent to avoid routing issues.
Verify which path is correct and update for consistency:
<Link
- to={`/marketplace/listing/${name}`}
+ to={`/marketplace/names/${name}`}
className="text-link hover:text-primary-light transition-colors font-medium"
>📝 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.
| <Link | |
| to={`/marketplace/listing/${name}`} | |
| className="text-link hover:text-primary-light transition-colors font-medium" | |
| > | |
| {formatForMaxCharCount(decodedName, 30)} | |
| </Link> | |
| <Link | |
| to={`/marketplace/names/${name}`} | |
| className="text-link hover:text-primary-light transition-colors font-medium" | |
| > | |
| {formatForMaxCharCount(decodedName, 30)} | |
| </Link> |
🤖 Prompt for AI Agents
In src/components/data-display/tables/MarketplaceListingsTable.tsx around lines
156 to 161, the link path uses `/marketplace/listing/${name}` which is
inconsistent with `/marketplace/names/${name}` used elsewhere; confirm the
correct route (likely `/marketplace/names/${name}`) and update this Link to use
the same path, and then run/adjust any affected tests or usages to ensure
routing remains consistent.
| value?: Date; | ||
| onChange: (date: Date | undefined) => void; | ||
| placeholder?: string; | ||
| minDate?: Date; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove unused minDate prop or implement its logic.
The minDate prop is defined in the interface but never used in the component logic. This is misleading to consumers of the component who might expect it to constrain date selection.
Apply this diff to remove the unused prop:
interface DateTimePickerProps {
value?: Date;
onChange: (date: Date | undefined) => void;
placeholder?: string;
- minDate?: Date;
className?: string;
}Alternatively, if minDate is intended to be used, implement it in the isDateDisabled function at Line 66.
📝 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.
| minDate?: Date; | |
| interface DateTimePickerProps { | |
| value?: Date; | |
| onChange: (date: Date | undefined) => void; | |
| placeholder?: string; | |
| className?: string; | |
| } |
🤖 Prompt for AI Agents
In src/components/inputs/DateTimePicker/DateTimePicker.tsx around line 10 (prop
definition) and line 66 (isDateDisabled), the minDate prop is declared but
unused; either remove it from the component props and related types/usages, or
implement it in isDateDisabled. To implement: update isDateDisabled to
early-return true when a candidate date is before the provided minDate (handle
undefined minDate), comparing dates normalized to the same time basis (e.g.,
strip time or use getTime()) so selection is correctly constrained; to remove:
delete minDate from the props interface and any stories/tests/usages that pass
it. Ensure TypeScript types and exported prop docs are updated accordingly.
| id={ | ||
| result.antTransferResult?.id | ||
| ? new ArweaveTransactionID(result.antTransferResult?.id) | ||
| : '0xanErrorOccured' | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Error state will throw: invalid ArweaveTransactionID.
If result.antTransferResult?.id is undefined, creating new ArweaveTransactionID('0xanErrorOccured') will throw an error because the constructor validates that the ID must be a 43-character alphanumeric string (per ArweaveTransactionID.ts lines 6-12). This could crash the success panel.
- id={
- result.antTransferResult?.id
- ? new ArweaveTransactionID(result.antTransferResult?.id)
- : '0xanErrorOccured'
- }
+ id={
+ result.antTransferResult?.id
+ ? new ArweaveTransactionID(result.antTransferResult.id)
+ : undefined
+ }Then handle the undefined case in the ArweaveID component or conditionally render this section only when result.antTransferResult?.id exists.
📝 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.
| id={ | |
| result.antTransferResult?.id | |
| ? new ArweaveTransactionID(result.antTransferResult?.id) | |
| : '0xanErrorOccured' | |
| } | |
| id={ | |
| result.antTransferResult?.id | |
| ? new ArweaveTransactionID(result.antTransferResult.id) | |
| : undefined | |
| } |
🤖 Prompt for AI Agents
In src/components/modals/ListNameForSaleModal/ListNameForSaleModal.tsx around
lines 160-164, the code constructs new ArweaveTransactionID('0xanErrorOccured')
when result.antTransferResult?.id is missing, which triggers the
ArweaveTransactionID constructor validation and will throw; change the logic to
only instantiate ArweaveTransactionID when result.antTransferResult?.id is a
valid non-empty string (e.g., conditionally render this component/section only
if result.antTransferResult?.id exists) or modify the prop passed to the
ArweaveID component to be undefined/null when id is absent and let ArweaveID
handle the empty state gracefully (do not pass an invalid placeholder string).
| const marketPriceDisplay = domainPrices?.buy | ||
| ? `Market Value: ${formatARIOWithCommas(Math.ceil(domainPrices.buy / 1000000))} ${arioTicker}` | ||
| : ''; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Inconsistent mARIO to ARIO conversion.
Lines 51-53 correctly use new mARIOToken(domainPrices.buy).toARIO().valueOf(), but lines 103, 113, and 117 use manual division by 1000000. Use the SDK's conversion method consistently to avoid errors if the decimal precision changes.
- const marketPriceDisplay = domainPrices?.buy
- ? `Market Value: ${formatARIOWithCommas(Math.ceil(domainPrices.buy / 1000000))} ${arioTicker}`
- : '';
+ const marketValueARIO = domainPrices?.buy
+ ? Math.ceil(new mARIOToken(domainPrices.buy).toARIO().valueOf())
+ : 0;
+ const marketPriceDisplay = marketValueARIO > 0
+ ? `Market Value: ${formatARIOWithCommas(marketValueARIO)} ${arioTicker}`
+ : '';Then use marketValueARIO in the JSX instead of recalculating.
Also applies to: 113-117
🤖 Prompt for AI Agents
In src/components/modals/ListNameForSaleModal/panels/FixedPricePanel.tsx around
lines 102-117, the code manually divides domainPrices.buy by 1,000,000 to
convert mARIO->ARIO; replace those manual divisions by using the SDK conversion
used earlier (new mARIOToken(domainPrices.buy).toARIO().valueOf()), assign the
result to a marketValueARIO variable (e.g., const marketValueARIO =
domainPrices?.buy ? new mARIOToken(domainPrices.buy).toARIO().valueOf() : 0) and
then use marketValueARIO when building the market price display and in the JSX
instead of recalculating with /1000000; ensure proper null/undefined handling to
match existing behavior.
| {PRICE_BUTTON_VALUES.map((value, index) => ( | ||
| <button | ||
| key={index} | ||
| className={`rounded p-2.5 transition-colors ${ | ||
| buttonSelected === index | ||
| ? 'bg-white text-black' | ||
| : 'bg-dark-grey text-white hover:bg-gray-600' | ||
| }`} | ||
| onClick={() => { | ||
| setCustomValue(value.toString()); | ||
| setListingPrice(value); | ||
| setButtonSelected(index); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bug: button selection comparison uses index instead of value.
buttonSelected === index compares the selected value with the loop index, but setButtonSelected(index) stores the index. However, lines 64-67 try to find a matching button by value: PRICE_BUTTON_VALUES.find((value) => value === listingPrice). This creates an inconsistency where returning from the confirm screen sets buttonSelected to the value, but the rendering compares against the index.
Either consistently use values:
{PRICE_BUTTON_VALUES.map((value, index) => (
<button
key={index}
className={`rounded p-2.5 transition-colors ${
- buttonSelected === index
+ buttonSelected === value
? 'bg-white text-black'
: 'bg-dark-grey text-white hover:bg-gray-600'
}`}
onClick={() => {
setCustomValue(value.toString());
setListingPrice(value);
- setButtonSelected(index);
+ setButtonSelected(value);
setCustomValueError('');
}}📝 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.
| {PRICE_BUTTON_VALUES.map((value, index) => ( | |
| <button | |
| key={index} | |
| className={`rounded p-2.5 transition-colors ${ | |
| buttonSelected === index | |
| ? 'bg-white text-black' | |
| : 'bg-dark-grey text-white hover:bg-gray-600' | |
| }`} | |
| onClick={() => { | |
| setCustomValue(value.toString()); | |
| setListingPrice(value); | |
| setButtonSelected(index); | |
| {PRICE_BUTTON_VALUES.map((value, index) => ( | |
| <button | |
| key={index} | |
| className={`rounded p-2.5 transition-colors ${ | |
| buttonSelected === value | |
| ? 'bg-white text-black' | |
| : 'bg-dark-grey text-white hover:bg-gray-600' | |
| }`} | |
| onClick={() => { | |
| setCustomValue(value.toString()); | |
| setListingPrice(value); | |
| setButtonSelected(value); |
🤖 Prompt for AI Agents
In src/components/modals/ListNameForSaleModal/panels/FixedPricePanel.tsx around
lines 133 to 144, the rendered button selection compares buttonSelected ===
index but elsewhere buttonSelected is being set from a value (listingPrice) when
returning from the confirm screen, causing mismatch; make the stored and
compared identifier consistent — either store and compare the actual price value
or consistently store and compare the index. Fix by choosing one approach and
updating all usages: if you prefer values, call setButtonSelected(value) in the
onClick and change the render comparison to buttonSelected === value (and ensure
initial state and any find/lookup use the value); if you prefer indices, ensure
when restoring from listingPrice you compute the matching index (e.g., findIndex
on PRICE_BUTTON_VALUES) and call setButtonSelected(index) so rendering remains
index-based.
| const writeMarketplaceContract = new ArNSMarketplaceWrite({ | ||
| process: new AOProcess({ | ||
| processId: marketplaceProcessId, | ||
| ao: aoClient, | ||
| }), | ||
| signer: createAoSigner(wallet.contractSigner), | ||
| ario: arioContract as AoARIOWrite, | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Find GlobalState file and search for arioContract
find . -name "GlobalState.tsx" -o -name "GlobalState.ts" 2>/dev/null | head -5Repository: ar-io/arns-react
Length of output: 96
🏁 Script executed:
#!/bin/bash
# Read GlobalState.tsx to see arioContract initialization
cat -n ./src/state/contexts/GlobalState.tsx | head -100Repository: ar-io/arns-react
Length of output: 4118
🏁 Script executed:
#!/bin/bash
# Continue reading GlobalState.tsx to find arioContract initialization
cat -n ./src/state/contexts/GlobalState.tsx | sed -n '100,250p'Repository: ar-io/arns-react
Length of output: 4916
🏁 Script executed:
#!/bin/bash
# Find the GlobalReducer to see how arioContract state changes
find . -name "GlobalReducer*" -o -name "*Reducer.ts*" | grep -i globalRepository: ar-io/arns-react
Length of output: 97
🏁 Script executed:
#!/bin/bash
# Read the GlobalReducer to see how arioContract is updated
cat -n ./src/state/reducers/GlobalReducer.tsRepository: ar-io/arns-react
Length of output: 4629
🏁 Script executed:
#!/bin/bash
# Read the WithdrawPanel to see the exact context around the cast
cat -n ./src/components/modals/ManageMarketplaceARIOModal/panels/WithdrawPanel.tsx | sed -n '130,150p'Repository: ar-io/arns-react
Length of output: 880
Remove unsafe cast or add runtime type check for arioContract.
The GlobalState types arioContract as AoARIORead | AoARIOWrite, and it is initialized with ARIO.init() without a signer, creating a read-only instance. The cast arioContract as AoARIOWrite at line 142 assumes the contract is always writeable without verification. Since arioContract can remain read-only or be updated to either read or write capability via the reducer, this cast is unsafe and will cause runtime errors if ArNSMarketplaceWrite attempts signing operations with a read-only contract instance.
| Sentry.browserTracingIntegration(), | ||
| Sentry.reactRouterV6BrowserTracingIntegration({ | ||
| useEffect, | ||
| useLocation, | ||
| useNavigationType, | ||
| createRoutesFromChildren, | ||
| matchRoutes, | ||
| }), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove the redundant browserTracingIntegration() integration.
For React Router v6, use only reactRouterV6BrowserTracingIntegration(). The Sentry documentation states to "update your Sentry.browserTracingIntegration to Sentry.reactRouterV6BrowserTracingIntegration", indicating a replacement. Using both integrations together will create duplicate tracing spans and unnecessary overhead. Remove Sentry.browserTracingIntegration() from line 24.
🤖 Prompt for AI Agents
In src/utils/sentry.ts around lines 24 to 31, the
Sentry.browserTracingIntegration() call is redundant when using
Sentry.reactRouterV6BrowserTracingIntegration; remove the
Sentry.browserTracingIntegration() entry from the integrations list so only
Sentry.reactRouterV6BrowserTracingIntegration({...}) remains, ensuring you leave
the reactRouterV6 integration and its passed hooks intact and update any imports
if necessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/components/pages/Settings/NetworkSettings.tsx (1)
492-492: Add validation guard to SU Address onPressEnter handler.The SU Address
onPressEnterhandler lacks validation, which is inconsistent with the pattern established for other inputs in this file. Transaction ID inputs (AR.IO Contract at lines 330-337 and ANT Registry at lines 369-376) validate withisArweaveTransactionIDbefore dispatching changes.Apply this diff to add validation:
- onPressEnter={(value) => updateAoNetwork({ SCHEDULER: value })} + onPressEnter={(value) => { + if (isArweaveTransactionID(value)) { + updateAoNetwork({ SCHEDULER: value }); + } + }}
🧹 Nitpick comments (3)
package.json (2)
61-61:permaweb-deployshould be a devDependency.This is a deployment CLI tool, not a runtime dependency. Moving it to
devDependenciesreduces the production bundle footprint."dependencies": { ... - "permaweb-deploy": "^2.5.1", ... }, "devDependencies": { ... + "permaweb-deploy": "^2.5.1", ... }
52-53: Redundant date libraries:date-fnsanddayjs.Both
date-fnsanddayjsare date manipulation libraries. Consider standardizing on one to reduce bundle size, unless there's a specific reason for both (e.g., existing codebase uses one, new features use another for a specific capability).src/services/wallets/EthWalletConnector.ts (1)
79-81: Remove @ts-ignore and document the type compatibility workaround or correct the signature mismatch.The aoSigner function has a
@ts-ignoredirective and defaults empty data to a space character, both suggesting a known incompatibility with theAoSignerinterface from@ar.io/sdk. This approach masks the type mismatch rather than addressing it.Consider one of:
- If data can legitimately be absent: Update the type signature to explicitly mark
dataas optional and document when this occurs and why defaulting to a space is acceptable for signing operations.- If data should always be present: Remove the default guard and fix the underlying type compatibility issue by ensuring the implementation matches the interface contract.
The current pattern with
@ts-ignoreand silent defaulting makes the code difficult to maintain and could hide integration issues with the SDK in the future.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
package.json(3 hunks)src/components/pages/Settings/NetworkSettings.tsx(7 hunks)src/services/wallets/EthWalletConnector.ts(1 hunks)src/services/wallets/WanderWalletConnector.ts(3 hunks)src/state/contexts/GlobalState.tsx(8 hunks)
✅ Files skipped from review due to trivial changes (1)
- src/services/wallets/WanderWalletConnector.ts
🧰 Additional context used
🧠 Learnings (6)
📓 Common learnings
Learnt from: dtfiedler
Repo: ar-io/arns-react PR: 873
File: src/components/modals/ant-management/UpgradeDomainsModal/UpgradeDomainsModal.tsx:0-0
Timestamp: 2025-09-10T16:35:55.727Z
Learning: In arns-react, when calling buildDomainInfoQuery (src/hooks/useDomainInfo.tsx), you must pass arioProcessId along with arioContract, domain, aoNetwork, hyperbeamUrl, and antRegistryProcessId; omitting arioProcessId can lead to “No ANT id or record found” and break upgrades.
Learnt from: dtfiedler
Repo: ar-io/arns-react PR: 873
File: src/components/modals/ant-management/UpgradeDomainsModal/UpgradeDomainsModal.tsx:0-0
Timestamp: 2025-09-10T16:35:55.727Z
Learning: In arns-react, when calling buildDomainInfoQuery (src/hooks/useDomainInfo.tsx), you must pass arioProcessId along with arioContract, domain, aoNetwork, hyperbeamUrl, and antRegistryProcessId; omitting arioProcessId can lead to "No ANT id or record found" and break upgrades.
Learnt from: vilenarios
Repo: ar-io/arns-react PR: 899
File: src/services/turbo/BaseTokenPurchaseService.ts:0-0
Timestamp: 2025-12-02T17:38:52.889Z
Learning: In src/services/turbo/BaseTokenPurchaseService.ts, the function executeBaseTokenPurchase intentionally only handles Base token top-up to Turbo credits, not the actual ArNS purchase. The ArNS purchase is handled separately by dispatchArIOInteraction in Checkout.tsx because it manages multiple complex purchase workflows (lease, permabuy, extend, increase undernames, etc.) with their own state management. This separation of concerns is by design.
📚 Learning: 2025-12-11T05:20:18.825Z
Learnt from: vilenarios
Repo: ar-io/arns-react PR: 902
File: src/components/modals/turbo/panels/crypto/CryptoConfirmation.tsx:536-540
Timestamp: 2025-12-11T05:20:18.825Z
Learning: Guideline: For tokens on Base network that use 6 decimals (e.g., base-ario ERC-20), convert amount to smallest units using a 1e6 multiplier. Use Math.round(amount * 1e6) when computing the on-chain value. Do not assume 18 decimals. Apply this conversion consistently in code paths that perform payments in CryptoConfirmation.tsx or similar components, and document that the base-ario/USDC-like tokens use 6 decimals to avoid mispricing or transfer errors.
Applied to files:
src/components/pages/Settings/NetworkSettings.tsx
📚 Learning: 2025-12-16T16:47:36.348Z
Learnt from: dtfiedler
Repo: ar-io/arns-react PR: 904
File: .github/workflows/pr-preview.yaml:52-58
Timestamp: 2025-12-16T16:47:36.348Z
Learning: In the ar-io/arns-react repository, ArNS undername cleanup for PR previews is handled by the permaweb-deploy GitHub action, not by a local script in this repository. The cleanup step should not be included in the PR preview workflow.
Applied to files:
package.json
📚 Learning: 2025-12-02T17:38:52.889Z
Learnt from: vilenarios
Repo: ar-io/arns-react PR: 899
File: src/services/turbo/BaseTokenPurchaseService.ts:0-0
Timestamp: 2025-12-02T17:38:52.889Z
Learning: In src/services/turbo/BaseTokenPurchaseService.ts, the function executeBaseTokenPurchase intentionally only handles Base token top-up to Turbo credits, not the actual ArNS purchase. The ArNS purchase is handled separately by dispatchArIOInteraction in Checkout.tsx because it manages multiple complex purchase workflows (lease, permabuy, extend, increase undernames, etc.) with their own state management. This separation of concerns is by design.
Applied to files:
src/state/contexts/GlobalState.tsx
📚 Learning: 2025-09-10T16:35:55.727Z
Learnt from: dtfiedler
Repo: ar-io/arns-react PR: 873
File: src/components/modals/ant-management/UpgradeDomainsModal/UpgradeDomainsModal.tsx:0-0
Timestamp: 2025-09-10T16:35:55.727Z
Learning: In arns-react, when calling buildDomainInfoQuery (src/hooks/useDomainInfo.tsx), you must pass arioProcessId along with arioContract, domain, aoNetwork, hyperbeamUrl, and antRegistryProcessId; omitting arioProcessId can lead to "No ANT id or record found" and break upgrades.
Applied to files:
src/state/contexts/GlobalState.tsx
📚 Learning: 2025-09-10T16:35:55.727Z
Learnt from: dtfiedler
Repo: ar-io/arns-react PR: 873
File: src/components/modals/ant-management/UpgradeDomainsModal/UpgradeDomainsModal.tsx:0-0
Timestamp: 2025-09-10T16:35:55.727Z
Learning: In arns-react, when calling buildDomainInfoQuery (src/hooks/useDomainInfo.tsx), you must pass arioProcessId along with arioContract, domain, aoNetwork, hyperbeamUrl, and antRegistryProcessId; omitting arioProcessId can lead to “No ANT id or record found” and break upgrades.
Applied to files:
src/state/contexts/GlobalState.tsx
🧬 Code graph analysis (1)
src/state/contexts/GlobalState.tsx (1)
src/utils/constants.ts (2)
MARKETPLACE_PROCESS_ID(103-105)DEFAULT_MIN_MARKETPLACE_ANT_VERSION(29-29)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: lint_and_build
🔇 Additional comments (14)
package.json (1)
23-29: Confirm intentional use of alpha dependencies in production.Using alpha versions (
@ar.io/sdk 3.23.0-alpha.1,@ardrive/turbo-sdk ^1.35.0-alpha.2) in production carries stability risks. Ensure the team is prepared to handle potential breaking changes.src/components/pages/Settings/NetworkSettings.tsx (5)
21-26: LGTM: Clean import of new validator.The addition of
isValidNetworkURLsupports the validation improvements throughout this file.
452-452: Consistent validation pattern for MU_URL.The changes mirror the CU_URL validation improvements and maintain consistency across network configuration inputs.
Also applies to: 460-464
526-526: Consistent validation for Hyperbeam URL with optional field handling.The validation changes are consistent with other network URLs. Note that Hyperbeam URL is optional (can be undefined/disabled), and the
onPressEntervalidation guard means users cannot clear the field by entering an empty string and pressing Enter. However, the Set button (lines 528-533) properly handles clearing via thetrim() || undefinedlogic, so the UX remains functional.Also applies to: 545-552
565-565: Consistent validation pattern for Turbo Payment URL.The changes align with the validation improvements for other network URLs and maintain consistency across the file.
Also applies to: 584-590
427-427: Switch toisValidNetworkURLfor network endpoint validation is sound.The implementation properly validates AO network endpoint formats (CU_URL, MU_URL) by checking for HTTPS/HTTP protocols and validating domain structure, including subdomains like
cu.ao-testnet.xyz. The addition of validation guards inonPressEnterprovides good defensive programming.src/state/contexts/GlobalState.tsx (8)
34-35: LGTM!The marketplace constants are properly imported and align with their definitions in
src/utils/constants.ts.
41-82: LGTM!The
loadSettingsFromStoragefunction is correctly extended to load marketplace settings with proper fallbacks to constants. The implementation follows the established pattern for other settings.
95-99: LGTM!Initial marketplace variables are properly initialized with the correct fallback chain from saved settings to constants.
125-142: LGTM!The
GlobalStatetype is correctly extended with marketplace fields. The union typeArNSMarketplaceRead | ArNSMarketplaceWriteappropriately mirrors the pattern used forarioContract, allowing the contract to be upgraded when a wallet connects.
144-172: LGTM!The
initialStateis correctly extended with marketplace fields and follows consistent patterns:
- Marketplace fields are properly initialized with the constants and defaults
- The network configuration changes to
aoClientandantAoClientmirror thedefaultArIOpatternmarketplaceContractis properly initialized withdefaultMarketplaceContract
8-9: Upgrade to latest stable SDK version when marketplace support is available.The code uses
@ar.io/sdkversion3.23.0-alpha.1(alpha) for marketplace types, but the latest stable version is 3.18.3. Plan to upgrade to a stable release once marketplace support is added to it, as alpha versions carry stability risks and may introduce breaking changes.
115-123: Verify marketplace contract configuration, especially MU_URL requirement.The
defaultMarketplaceContractis configured withoutMU_URL, whiledefaultArIOincludes bothCU_URLandMU_URLfrom the sameinitialAoNetwork.ARIOsource. AlthoughArNSMarketplaceReadis read-only, @ar.io/sdk documentation consistently shows both parameters in all AOProcess configuration examples. Confirm whether:
MU_URLis truly optional for read-onlyArNSMarketplaceReadcontracts, or if it should be included likedefaultArIO- Whether the marketplace process should use its own dedicated endpoint configuration instead of sharing
initialAoNetwork.ARIO.CU_URL
102-113: No issue found. The code correctly initializesdefaultArIOwith explicitCU_URL,MU_URL, andMODE: 'legacy'properties. These are the only properties required by theconnect()function for ARIO initialization, which aligns with the official ARIO SDK documentation. Other network properties likeSCHEDULERandHYPERBEAM_URLare used separately in transaction creation and ARIO initialization respectively, not in the connect() call. The pattern is consistent across allconnect()usages in the file (aoClient, antAoClient, defaultMarketplaceContract).
| "@sentry/browser": "^10.29.0", | ||
| "@sentry/react": "^7.45.0", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sentry version mismatch between @sentry/browser and @sentry/react.
@sentry/browser at ^10.29.0 and @sentry/react at ^7.45.0 are incompatible major versions. Sentry packages should use matching major versions to avoid runtime conflicts and undefined behavior.
- "@sentry/browser": "^10.29.0",
- "@sentry/react": "^7.45.0",
+ "@sentry/browser": "^7.45.0",
+ "@sentry/react": "^7.45.0",Or upgrade both to v10 if you need the latest features:
- "@sentry/browser": "^10.29.0",
- "@sentry/react": "^7.45.0",
+ "@sentry/browser": "^10.29.0",
+ "@sentry/react": "^10.29.0",📝 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.
| "@sentry/browser": "^10.29.0", | |
| "@sentry/react": "^7.45.0", | |
| "@sentry/browser": "^7.45.0", | |
| "@sentry/react": "^7.45.0", |
🤖 Prompt for AI Agents
In package.json around lines 36-37, the project lists @sentry/browser at
^10.29.0 and @sentry/react at ^7.45.0 which are mismatched major versions;
update the Sentry packages to use the same major version—either bump
@sentry/react to a v10 release matching @sentry/browser (e.g., ^10.29.0) or
downgrade @sentry/browser to the v7 series to match @sentry/react; after
changing the version strings run your package manager install (npm/yarn/pnpm)
and run the test/lint scripts to ensure nothing else breaks.
Permaweb Preview DeployedYour preview is available at: https://pr-903_arns.arweave.net
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
package.json (1)
6-20: Remove test infrastructure or document testing strategy.The complete test infrastructure has been removed: no test configuration files (jest, playwright, vitest), test files, or test dependencies remain in the repository. This PR introduces substantial marketplace functionality without any automated tests, significantly increasing regression risk.
Either:
- Restore the test suite with coverage for the new marketplace features, or
- Document the testing strategy and quality assurance approach used to validate this code
♻️ Duplicate comments (1)
package.json (1)
36-37: Sentry version mismatch remains unresolved.This issue was previously flagged:
@sentry/browser(^10.29.0) and@sentry/react(^7.45.0) use incompatible major versions. Please align them to the same major version to prevent runtime conflicts.
🧹 Nitpick comments (2)
src/components/data-display/tables/MarketplaceListingsTable.tsx (2)
83-95: Consider server-side pagination for larger datasets.The hard-coded
limit: 100may be insufficient as the marketplace grows. While the API supports pagination (hasMore,nextCursor), the component doesn't expose pagination controls to users or implement fetching beyond the first page.Consider either:
- Increasing the limit significantly (e.g., 1000) if the total listing count is expected to remain modest, or
- Implementing proper pagination controls that allow users to navigate through all listings, fetching additional pages as needed.
410-434: Unconventional but functional loading state management.The component passes
isLoading={false}toTableViewwhile managing loading state throughnoDataFoundTextand an empty data array. This works correctly but diverges from the typical pattern whereTableView'sisLoadingprop would control the loading indicator.If you prefer conventional table loading patterns, consider:
Alternative approach using TableView's isLoading prop
<TableView columns={columns as ColumnDef<MarketplaceListing>[]} - data={loadingTableData ? [] : filteredData} - isLoading={false} + data={filteredData} + isLoading={loadingTableData} defaultSortingState={{ id: 'listedAt', desc: true }} noDataFoundText={ - loadingTableData ? ( - <div className="flex flex-col items-center justify-center p-8 text-center"> - <ARIOLoadingSpinner /> - </div> - ) : ( <div className="flex flex-col items-center justify-center p-8 text-center"> <div className="text-grey mb-4"> No marketplace listings found </div> <button onClick={() => refetch()} className="flex items-center gap-2 px-4 py-2 border-white text-white rounded transition-colors" > <RefreshIcon className="w-4 h-4 fill-white" /> Retry </button> </div> - ) } />However, your current implementation is perfectly functional and may provide better UX if TableView's default loading indicator doesn't fit your design requirements.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
yarn.lockis excluded by!**/yarn.lock,!**/*.lock
📒 Files selected for processing (5)
package.json(3 hunks)src/components/data-display/tables/MarketplaceListingsTable.tsx(1 hunks)src/components/data-display/tables/TableView.tsx(1 hunks)src/components/indicators/ARIOLoadingSpinner.tsx(1 hunks)src/components/pages/Marketplace/Marketplace.tsx(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- src/components/pages/Marketplace/Marketplace.tsx
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: vilenarios
Repo: ar-io/arns-react PR: 899
File: src/services/turbo/BaseTokenPurchaseService.ts:0-0
Timestamp: 2025-12-02T17:38:52.889Z
Learning: In src/services/turbo/BaseTokenPurchaseService.ts, the function executeBaseTokenPurchase intentionally only handles Base token top-up to Turbo credits, not the actual ArNS purchase. The ArNS purchase is handled separately by dispatchArIOInteraction in Checkout.tsx because it manages multiple complex purchase workflows (lease, permabuy, extend, increase undernames, etc.) with their own state management. This separation of concerns is by design.
📚 Learning: 2025-12-11T05:20:18.825Z
Learnt from: vilenarios
Repo: ar-io/arns-react PR: 902
File: src/components/modals/turbo/panels/crypto/CryptoConfirmation.tsx:536-540
Timestamp: 2025-12-11T05:20:18.825Z
Learning: Guideline: For tokens on Base network that use 6 decimals (e.g., base-ario ERC-20), convert amount to smallest units using a 1e6 multiplier. Use Math.round(amount * 1e6) when computing the on-chain value. Do not assume 18 decimals. Apply this conversion consistently in code paths that perform payments in CryptoConfirmation.tsx or similar components, and document that the base-ario/USDC-like tokens use 6 decimals to avoid mispricing or transfer errors.
Applied to files:
src/components/indicators/ARIOLoadingSpinner.tsxsrc/components/data-display/tables/TableView.tsxsrc/components/data-display/tables/MarketplaceListingsTable.tsx
📚 Learning: 2025-08-08T16:53:05.995Z
Learnt from: dtfiedler
Repo: ar-io/arns-react PR: 841
File: src/components/data-display/tables/UndernamesTable.tsx:59-59
Timestamp: 2025-08-08T16:53:05.995Z
Learning: In src/components/data-display/tables/UndernamesTable.tsx, the isLoading prop should be optional and default to false via destructuring.
Applied to files:
src/components/data-display/tables/TableView.tsx
🧬 Code graph analysis (1)
src/components/data-display/tables/MarketplaceListingsTable.tsx (9)
src/hooks/useArIOPrice.ts (1)
useArIoPrice(9-32)src/hooks/useArNSRecords.tsx (1)
useArNSRecords(5-40)src/hooks/useMarketplaceOrders.ts (1)
useMarketplaceOrders(72-84)src/components/data-display/index.tsx (1)
Tooltip(5-5)src/utils/searchUtils/searchUtils.ts (2)
decodeDomainToASCII(48-52)lowerCaseDomain(132-134)src/utils/common/common.tsx (3)
formatForMaxCharCount(134-148)formatARIOWithCommas(215-219)formatVerboseDate(21-36)src/services/arweave/ArweaveTransactionID.ts (1)
ArweaveTransactionID(7-31)src/types.ts (1)
AoAddress(562-562)src/components/icons/index.ts (1)
RefreshIcon(127-127)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: lint_and_build
🔇 Additional comments (4)
package.json (1)
23-23: Use stable SDK versions instead of pre-release builds.@ar.io/sdk has a stable version 3.18.3 available, and @ardrive/turbo-sdk has a stable version 1.30.0 available. The current alpha versions in package.json introduce unnecessary risk. Migrate to the latest stable releases unless the alpha features are specifically required for marketplace functionality.
src/components/data-display/tables/TableView.tsx (1)
220-220: LGTM - UI styling improvement.The change from
border-btoborder-twith addedroundedclass improves the visual presentation of the no-data indicator, aligning it with the table structure above.src/components/indicators/ARIOLoadingSpinner.tsx (1)
1-17: LGTM - Clean and simple loading indicator.The component is well-structured with sensible defaults and straightforward Lottie integration. The parameterized
sizeprop and optionalclassNameprovide good flexibility for different use cases across the marketplace UI.src/components/data-display/tables/MarketplaceListingsTable.tsx (1)
1-440: Comprehensive marketplace table implementation with good UX.The component demonstrates solid engineering with:
- Proper data fetching and transformation using multiple hooks
- Resolved past issues (null name filtering, consistent URL routing)
- Comprehensive column definitions with appropriate sorting and formatting
- Good error handling and user feedback mechanisms
- Effective use of React patterns (memoization, effects, etc.)
The pagination limitation and loading state pattern have been noted separately for consideration.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 6
♻️ Duplicate comments (3)
package.json (1)
36-37: Sentry version mismatch between@sentry/browserand@sentry/react.This issue was previously flagged:
@sentry/browserat^10.29.0and@sentry/reactat^7.45.0are incompatible major versions. Please align both packages to the same major version to avoid runtime conflicts.src/components/modals/ListNameForSaleModal/ListNameForSaleModal.tsx (1)
364-395: Infinite loop risk if balance check succeeds but loop continues.The while loop on line 367 checks
newARIOBalance < feeDetails.listingFee, but inside the loop,newARIOBalanceis only updated within the try block. If an exception occurs repeatedly without updatingnewARIOBalance, the loop will continue untilmaxTriesis exhausted, which is expected. However, if the balance check succeeds (line 380-382 doesn't throw), the loop should break but currently continues.Also, the loop will exit on success when the while condition becomes false, but the success path on lines 380-385 incorrectly throws when
newARIOBalance <= feeDetails.listingFee.This is related to the logic error above. The combined fix should ensure the loop exits correctly on success.
src/components/modals/ContinueWorkflowModal/ContinueWorkflowModal.tsx (1)
94-118: Inconsistent price unit handling in orderConfig.At line 96,
priceis assigned asNumber(orderParams.price)without division, but at lines 108-112,startingPriceandreservePricedivide by1000000to convert from mARIO. This inconsistency could lead to displaying incorrect values.🔎 Proposed fix
const listingType = orderParams.orderType || 'fixed'; - const price = orderParams.price ? Number(orderParams.price) : 0; + const price = orderParams.price ? Number(orderParams.price) / 1000000 : 0; const expirationDate = orderParams.expirationTimeAlternatively, use
mARIOTokenfrom the SDK for consistent conversion:const price = orderParams.price ? new mARIOToken(Number(orderParams.price)).toARIO().valueOf() : 0;
🧹 Nitpick comments (8)
package.json (1)
61-61: Movepermaweb-deploytodevDependencies.
permaweb-deployis a deployment CLI tool used only in build/deploy scripts. It doesn't need to be bundled with production dependencies."dependencies": { ... - "permaweb-deploy": "^2.5.1", ... }, "devDependencies": { + "permaweb-deploy": "^2.5.1", ... }src/components/modals/ListNameForSaleModal/ListNameForSaleModal.tsx (2)
107-109: Consider a longer default expiration period.The default expiration of 1 hour from now is quite short. Users may not complete the listing in time, or potential buyers may have limited time to discover and purchase. Consider a default of 24 hours or longer.
const [expirationDate, setExpirationDate] = useState<Date | undefined>( - new Date(Date.now() + 60 * 60 * 1000), // Default to 1 hour from now (current day) + new Date(Date.now() + 24 * 60 * 60 * 1000), // Default to 24 hours from now );
489-503: Query reset predicates may match unintended queries.The string-based query key matching with
startsWith('marketplace')andincludes(domainName)could inadvertently reset unrelated queries. Consider using more specific query keys or exact matching.🔎 Suggested improvement
queryClient.resetQueries({ predicate: (query) => query.queryKey.some( (key: unknown) => - typeof key === 'string' && key.startsWith('marketplace'), + key === 'marketplace-orders' || + key === 'marketplace-intents' || + key === 'marketplace-user-assets', ), });src/components/data-display/VerticalTimelineStepper.tsx (1)
15-39: Consider caching the entries array to avoid repeated computation.
Object.entries(steps)is called twice per render (once in the map and once for length check). Caching it improves efficiency.🔎 Suggested improvement
export default function VerticalTimelineStepper({ steps, }: { steps: Record< string, { title: ReactNode; description: ReactNode; icon: ReactNode; } >; }) { + const stepEntries = Object.entries(steps); return ( <ol className="relative gap-2"> - {Object.entries(steps).map(([, { title, description, icon }], i) => ( + {stepEntries.map(([key, { title, description, icon }], i) => ( - <div key={i}> + <div key={key}> <li className="ms-7"> <span className="absolute flex items-center justify-center w-8 h-8 rounded-full -start-4 ring-1 ring-dark-grey"> {icon} </span> <div className="flex flex-col max-h-8 overflow-visible"> {' '} <h3 className="text-md leading-tight">{title}</h3> <p className="text-xs text-grey">{description}</p> </div> </li> <div className={ - i === Object.entries(steps).length - 1 + i === stepEntries.length - 1 ? '' : 'border-l border-dark-grey h-8' } - key={`${i}-divider`} /> </div> ))} </ol> ); }src/components/modals/ContinueWorkflowModal/ContinueWorkflowModal.tsx (1)
188-191: Avoidanytype casts for better type safety.Using
as anyon line 188 andanyforresulton line 191 loses TypeScript's type checking benefits. Consider using proper types or creating a union type if the exact type is complex.- ario: arioContract as any, // Cast to avoid type issues + ario: arioContract as AoARIOWrite,For line 191, consider typing
resultbased on the possible return types from the switch cases.src/components/data-display/tables/DomainsTable.tsx (3)
57-65: Consolidate duplicate lucide-react imports.The imports from
lucide-reactare split across two import statements (lines 57 and 58-65). Consolidate them for cleaner code.🔎 Suggested fix
-import { Activity, StoreIcon } from 'lucide-react'; -import { - AlertTriangle, - CircleCheck, - Copy, - DollarSign, - ExternalLink, - Star, -} from 'lucide-react'; +import { + Activity, + AlertTriangle, + CircleCheck, + Copy, + DollarSign, + ExternalLink, + Star, + StoreIcon, +} from 'lucide-react';
288-371: Consider extracting modal state management to reduce duplication.The
RoleDisplaycomponent andInterruptedWorkflowActionboth manageshowContinueWorkflowModalstate with similar patterns. Consider creating a custom hook or using a render prop pattern to reduce duplication.This is a minor improvement for maintainability when the pattern is used more broadly.
869-938: IIFE pattern works but could be cleaner.The immediately-invoked function expression (IIFE) pattern on lines 869-938 works for the conditional rendering logic, but extracting this to a named helper component (similar to
MarketplaceActionIcon) would improve readability and testability.This is optional since the current implementation is functional and the component is already handling multiple concerns.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
yarn.lockis excluded by!**/yarn.lock,!**/*.lock
📒 Files selected for processing (8)
package.jsonsrc/components/data-display/VerticalTimelineStepper.tsxsrc/components/data-display/tables/DomainsTable.tsxsrc/components/modals/ContinueWorkflowModal/ContinueWorkflowModal.tsxsrc/components/modals/ListNameForSaleModal/ListNameForSaleModal.tsxsrc/components/modals/ListNameForSaleModal/panels/ConfirmListingPanel.tsxsrc/components/modals/ListNameForSaleModal/panels/ProcessTransactionPanel.tsxsrc/components/pages/Manage/Manage.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
- src/components/modals/ListNameForSaleModal/panels/ConfirmListingPanel.tsx
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2025-12-02T17:38:52.889Z
Learnt from: vilenarios
Repo: ar-io/arns-react PR: 899
File: src/services/turbo/BaseTokenPurchaseService.ts:0-0
Timestamp: 2025-12-02T17:38:52.889Z
Learning: In src/services/turbo/BaseTokenPurchaseService.ts, the function executeBaseTokenPurchase intentionally only handles Base token top-up to Turbo credits, not the actual ArNS purchase. The ArNS purchase is handled separately by dispatchArIOInteraction in Checkout.tsx because it manages multiple complex purchase workflows (lease, permabuy, extend, increase undernames, etc.) with their own state management. This separation of concerns is by design.
Applied to files:
src/components/pages/Manage/Manage.tsxsrc/components/modals/ContinueWorkflowModal/ContinueWorkflowModal.tsx
📚 Learning: 2025-09-10T16:35:55.727Z
Learnt from: dtfiedler
Repo: ar-io/arns-react PR: 873
File: src/components/modals/ant-management/UpgradeDomainsModal/UpgradeDomainsModal.tsx:0-0
Timestamp: 2025-09-10T16:35:55.727Z
Learning: In arns-react, when calling buildDomainInfoQuery (src/hooks/useDomainInfo.tsx), you must pass arioProcessId along with arioContract, domain, aoNetwork, hyperbeamUrl, and antRegistryProcessId; omitting arioProcessId can lead to "No ANT id or record found" and break upgrades.
Applied to files:
src/components/pages/Manage/Manage.tsxsrc/components/data-display/tables/DomainsTable.tsxsrc/components/modals/ListNameForSaleModal/ListNameForSaleModal.tsx
📚 Learning: 2025-09-10T16:35:55.727Z
Learnt from: dtfiedler
Repo: ar-io/arns-react PR: 873
File: src/components/modals/ant-management/UpgradeDomainsModal/UpgradeDomainsModal.tsx:0-0
Timestamp: 2025-09-10T16:35:55.727Z
Learning: In arns-react, when calling buildDomainInfoQuery (src/hooks/useDomainInfo.tsx), you must pass arioProcessId along with arioContract, domain, aoNetwork, hyperbeamUrl, and antRegistryProcessId; omitting arioProcessId can lead to “No ANT id or record found” and break upgrades.
Applied to files:
src/components/pages/Manage/Manage.tsxsrc/components/data-display/tables/DomainsTable.tsxsrc/components/modals/ListNameForSaleModal/ListNameForSaleModal.tsx
📚 Learning: 2025-12-11T05:20:18.825Z
Learnt from: vilenarios
Repo: ar-io/arns-react PR: 902
File: src/components/modals/turbo/panels/crypto/CryptoConfirmation.tsx:536-540
Timestamp: 2025-12-11T05:20:18.825Z
Learning: Guideline: For tokens on Base network that use 6 decimals (e.g., base-ario ERC-20), convert amount to smallest units using a 1e6 multiplier. Use Math.round(amount * 1e6) when computing the on-chain value. Do not assume 18 decimals. Apply this conversion consistently in code paths that perform payments in CryptoConfirmation.tsx or similar components, and document that the base-ario/USDC-like tokens use 6 decimals to avoid mispricing or transfer errors.
Applied to files:
src/components/pages/Manage/Manage.tsxsrc/components/data-display/VerticalTimelineStepper.tsxsrc/components/modals/ListNameForSaleModal/panels/ProcessTransactionPanel.tsxsrc/components/data-display/tables/DomainsTable.tsxsrc/components/modals/ContinueWorkflowModal/ContinueWorkflowModal.tsxsrc/components/modals/ListNameForSaleModal/ListNameForSaleModal.tsx
🧬 Code graph analysis (3)
src/components/pages/Manage/Manage.tsx (1)
src/state/contexts/GlobalState.tsx (1)
useGlobalState(178-179)
src/components/modals/ListNameForSaleModal/panels/ProcessTransactionPanel.tsx (1)
src/components/data-display/VerticalTimelineStepper.tsx (1)
VerticalTimelineStepper(3-41)
src/components/modals/ListNameForSaleModal/ListNameForSaleModal.tsx (10)
src/state/contexts/GlobalState.tsx (1)
useGlobalState(178-179)src/state/contexts/WalletState.tsx (1)
useWalletState(49-50)src/hooks/useArIOPrice.ts (1)
useArIoPrice(9-32)src/hooks/useMarketplaceInfo.ts (1)
useMarketplaceInfo(4-25)src/hooks/useMarketplaceUserAssets.ts (1)
useMarketplaceUserAssets(44-61)src/utils/network.ts (1)
queryClient(39-45)src/hooks/useANTIntent.ts (1)
useANTIntent(17-75)src/components/layout/index.ts (1)
Loader(12-12)src/utils/searchUtils/searchUtils.ts (1)
sleep(128-130)src/services/arweave/ArweaveTransactionID.ts (1)
ArweaveTransactionID(7-31)
🔇 Additional comments (12)
src/components/pages/Manage/Manage.tsx (3)
103-122: Marketplace integration in refresh flow looks good.The addition of
marketplaceProcessIdto thedispatchArNSUpdatecall is consistent with the marketplace functionality being added. The parameter follows the same pattern as other process IDs already in use.
59-60: Minor styling refactor - no functional changes.The change from styled wrapper to class-based styling simplifies the markup without affecting behavior.
21-29: Verify ifdispatchArNSUpdatehandles optional or undefinedmarketplaceProcessId.The newly added
marketplaceProcessIdis destructured from global state but its optionality in thedispatchArNSUpdatefunction signature requires confirmation. Verify the function signature to ensure it either marksmarketplaceProcessIdas optional (with?) or provides a default value, and confirm thatundefinedvalues are handled gracefully rather than causing runtime errors.src/components/modals/ListNameForSaleModal/ListNameForSaleModal.tsx (3)
1-48: LGTM on imports and type definitions.The imports are well-organized and the type definitions for
ListingTypeandPanelStatesare clear and appropriately exported for use by other components.
114-150: Fee calculation logic looks correct.The fee calculation using SDK functions
calculateListingFeeandcalculateSaleTaxwith proper mARIO/ARIO conversions is well implemented. The separation of listing fee (paid upfront) from sale tax (deducted from proceeds) is correctly handled.
179-280: Well-structured workflow step management.The
updateWorkflowStepscallback provides a clean abstraction for updating step states with appropriate icons. The switch statements are comprehensive and handle all defined states.src/components/modals/ListNameForSaleModal/panels/ProcessTransactionPanel.tsx (1)
1-70: LGTM!Clean presentational component with clear state-based rendering. The exported
ProcessTransactionPanelPropstype enables proper typing for consumers, and the conditional UI logic for complete/error states is straightforward.src/components/modals/ContinueWorkflowModal/ContinueWorkflowModal.tsx (1)
51-68: Well-structured state management from global context.Good use of destructuring to extract only the needed values from global and wallet state hooks.
src/components/data-display/tables/DomainsTable.tsx (4)
126-204: LGTM on ErrorStateTooltip component.Well-structured error handling component with clipboard copy functionality and Discord reporting. The error config object captures relevant debugging information for support.
438-452: Helper functions are well-designed.
isMarketplaceCompatibleandgetInterruptedWorkflowForDomainare clean helper functions that improve readability of the table rendering logic.
1081-1111: LGTM on modal integrations.The modal rendering for
ListNameForSaleModalandUpgradeDomainForMarketplaceModalis well-implemented with proper state management. TheonUpgradeCompletecallback pattern for chaining the upgrade-to-sale flow is a nice UX touch.
476-489: Role detection with marketplace ownership is well-implemented.The
getRoleWithMarketplacefunction correctly prioritizes marketplace ownership check before falling back to the standard ownership status logic.
| createAoSigner, | ||
| mARIOToken, | ||
| } from '@ar.io/sdk'; | ||
| import { connect } from '@permaweb/aoconnect'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unused import.
connect is imported from @permaweb/aoconnect but never used in this file.
-import { connect } from '@permaweb/aoconnect';📝 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.
| import { connect } from '@permaweb/aoconnect'; |
🤖 Prompt for AI Agents
In src/components/modals/ContinueWorkflowModal/ContinueWorkflowModal.tsx around
line 8, the import "connect" from "@permaweb/aoconnect" is unused; remove this
import to eliminate the dead code (or if it was intended to be used, wire it
into the component export or usage where appropriate). Ensure imports are
cleaned up and run the linter to confirm no remaining unused imports.
| const retries = 0; | ||
| const maxRetries = 10; | ||
| while (retries < maxRetries) { | ||
| try { | ||
| const _res = await marketplaceContract.getUserAssets({ | ||
| address: walletAddress.toString(), | ||
| arioProcessId, | ||
| }); | ||
| } catch { | ||
| console.error('Issue retrieving marketplace assets'); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Critical: Infinite loop will freeze the application.
retries is declared as const and never incremented, causing the while loop to run forever. This will freeze the UI when the PUSH_INTENT workflow is executed.
🔎 Proposed fix
- const retries = 0;
+ let retries = 0;
const maxRetries = 10;
while (retries < maxRetries) {
try {
const _res = await marketplaceContract.getUserAssets({
address: walletAddress.toString(),
arioProcessId,
});
+ // Add success condition to break out of loop
+ break;
} catch {
console.error('Issue retrieving marketplace assets');
+ retries++;
+ await new Promise(resolve => setTimeout(resolve, 2000)); // Add delay between retries
}
}📝 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.
| const retries = 0; | |
| const maxRetries = 10; | |
| while (retries < maxRetries) { | |
| try { | |
| const _res = await marketplaceContract.getUserAssets({ | |
| address: walletAddress.toString(), | |
| arioProcessId, | |
| }); | |
| } catch { | |
| console.error('Issue retrieving marketplace assets'); | |
| } | |
| } | |
| let retries = 0; | |
| const maxRetries = 10; | |
| while (retries < maxRetries) { | |
| try { | |
| const _res = await marketplaceContract.getUserAssets({ | |
| address: walletAddress.toString(), | |
| arioProcessId, | |
| }); | |
| // Add success condition to break out of loop | |
| break; | |
| } catch { | |
| console.error('Issue retrieving marketplace assets'); | |
| retries++; | |
| await new Promise(resolve => setTimeout(resolve, 2000)); // Add delay between retries | |
| } | |
| } |
🤖 Prompt for AI Agents
In src/components/modals/ContinueWorkflowModal/ContinueWorkflowModal.tsx around
lines 221 to 232, the retry loop is broken because retries is declared as const
and never incremented, causing an infinite loop; change retries to a mutable
variable (let), increment it on each iteration, exit the loop when the call
succeeds (break) or when retries >= maxRetries, and implement a small
delay/backoff between attempts (e.g., setTimeout/await sleep) and surface or
handle the final failure after max retries to avoid freezing the UI.
| <div className="flex items-center gap-3 text-sm"> | ||
| <span className="text-grey whitespace-nowrap">ANT Owner:</span> | ||
| <ArweaveID | ||
| id={new ArweaveTransactionID(domainInfo?.state?.Owner || '')} | ||
| type={ArweaveIdTypes.ADDRESS} | ||
| characterCount={8} | ||
| /> | ||
| <ArrowRight className="w-3 h-3 text-grey" /> | ||
| <span className="text-grey">Expected:</span> | ||
| <ArweaveID | ||
| id={new ArweaveTransactionID(marketplaceProcessId)} | ||
| type={ArweaveIdTypes.ADDRESS} | ||
| characterCount={8} | ||
| /> | ||
| </div> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Potential crash if domainInfo?.state?.Owner is empty string.
Creating new ArweaveTransactionID('') will throw an error since the constructor validates the ID format. The conditional domainInfo?.state?.Owner || '' will pass an empty string when Owner is undefined.
🔎 Proposed fix
+ {domainInfo?.state?.Owner && (
<ArweaveID
id={new ArweaveTransactionID(domainInfo?.state?.Owner || '')}
type={ArweaveIdTypes.ADDRESS}
characterCount={8}
/>
+ )}
+ {!domainInfo?.state?.Owner && <span className="text-grey">Unknown</span>}📝 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.
| <div className="flex items-center gap-3 text-sm"> | |
| <span className="text-grey whitespace-nowrap">ANT Owner:</span> | |
| <ArweaveID | |
| id={new ArweaveTransactionID(domainInfo?.state?.Owner || '')} | |
| type={ArweaveIdTypes.ADDRESS} | |
| characterCount={8} | |
| /> | |
| <ArrowRight className="w-3 h-3 text-grey" /> | |
| <span className="text-grey">Expected:</span> | |
| <ArweaveID | |
| id={new ArweaveTransactionID(marketplaceProcessId)} | |
| type={ArweaveIdTypes.ADDRESS} | |
| characterCount={8} | |
| /> | |
| </div> | |
| <div className="flex items-center gap-3 text-sm"> | |
| <span className="text-grey whitespace-nowrap">ANT Owner:</span> | |
| {domainInfo?.state?.Owner && ( | |
| <ArweaveID | |
| id={new ArweaveTransactionID(domainInfo?.state?.Owner || '')} | |
| type={ArweaveIdTypes.ADDRESS} | |
| characterCount={8} | |
| /> | |
| )} | |
| {!domainInfo?.state?.Owner && <span className="text-grey">Unknown</span>} | |
| <ArrowRight className="w-3 h-3 text-grey" /> | |
| <span className="text-grey">Expected:</span> | |
| <ArweaveID | |
| id={new ArweaveTransactionID(marketplaceProcessId)} | |
| type={ArweaveIdTypes.ADDRESS} | |
| characterCount={8} | |
| /> | |
| </div> |
🤖 Prompt for AI Agents
In src/components/modals/ContinueWorkflowModal/ContinueWorkflowModal.tsx around
lines 310 to 324, the code constructs new
ArweaveTransactionID(domainInfo?.state?.Owner || '') which will pass an empty
string to the constructor and can throw; change it to only instantiate
ArweaveTransactionID when Owner is a non-empty value (e.g., use a ternary or
conditional render: if domainInfo?.state?.Owner is truthy then create new
ArweaveTransactionID(domainInfo.state.Owner) and render ArweaveID, otherwise
skip rendering the ArweaveID or pass undefined/null if ArweaveID accepts that),
ensuring you do not pass an empty string into the ArweaveTransactionID
constructor.
| if ( | ||
| tries >= maxTries || | ||
| newARIOBalance <= feeDetails.listingFee | ||
| ) { | ||
| throw new Error('Failed to deposit enough ARIO'); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Logic error in deposit verification loop.
The condition newARIOBalance <= feeDetails.listingFee on line 382 will throw an error even when the balance equals the listing fee, which should be a valid state. Additionally, the error is thrown when tries >= maxTries regardless of whether the balance check passed.
🔎 Proposed fix
if (
- tries >= maxTries ||
- newARIOBalance <= feeDetails.listingFee
+ newARIOBalance < feeDetails.listingFee &&
+ tries >= maxTries
) {
throw new Error('Failed to deposit enough ARIO');
}
+ if (newARIOBalance >= feeDetails.listingFee) {
+ break; // Exit the loop on success
+ }🤖 Prompt for AI Agents
In src/components/modals/ListNameForSaleModal/ListNameForSaleModal.tsx around
lines 380 to 385, the current condition throws even when balance equals the
listing fee and also throws on max tries regardless of balance; change the logic
so the error is thrown only when both maxTries reached AND the balance is
strictly less than the required listingFee (use <, not <=), i.e. check tries >=
maxTries && newARIOBalance < feeDetails.listingFee, otherwise allow success when
balance >= listingFee or continue retrying until maxTries.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
♻️ Duplicate comments (1)
src/components/modals/ListNameForSaleModal/ListNameForSaleModal.tsx (1)
385-390: Logic error in deposit verification loop.The condition throws an error even when the balance equals the listing fee (using
<=instead of<). Additionally, the error is thrown whentries >= maxTriesregardless of whether the balance check passed. The loop never explicitly breaks on success.🔎 Proposed fix (from previous review)
if ( - tries >= maxTries || - newARIOBalance <= feeDetails.listingFee + newARIOBalance < feeDetails.listingFee && + tries >= maxTries ) { throw new Error('Failed to deposit enough ARIO'); } + if (newARIOBalance >= feeDetails.listingFee) { + break; // Exit the loop on success + }
🧹 Nitpick comments (4)
src/components/modals/ListNameForSaleModal/ListNameForSaleModal.tsx (4)
316-318: Document or make configurable the 30-day expiration limit.The 30-day maximum expiration period is hardcoded without explanation. Consider documenting why this limit exists (marketplace policy, technical constraint, etc.) or making it configurable via marketplace settings.
💡 Suggested documentation
if (expirationDate.getTime() > Date.now() + 1000 * 60 * 60 * 24 * 30) { - throw new Error('Expiration date must be within next 30 days'); + throw new Error('Expiration date must be within next 30 days'); // Marketplace policy limit }Alternatively, retrieve this from marketplace info if available:
const maxExpirationDays = marketplaceInfo?.maxListingDays ?? 30;
494-508: Consider more precise query invalidation.The current predicates reset all queries containing 'marketplace', domainName, or antId. This might invalidate more queries than necessary. Consider using specific query keys instead.
💡 Suggested improvement
// we need to reset the query states for orders and ant state. - queryClient.resetQueries({ - predicate: (query) => - query.queryKey.some( - (key: unknown) => - typeof key === 'string' && key.startsWith('marketplace'), - ), - }); - queryClient.resetQueries({ - predicate: (query) => - query.queryKey.some( - (key: unknown) => - (typeof key === 'string' && key.includes(domainName)) || - (typeof key === 'string' && key.includes(antId ?? '')), - ), - }); + // Invalidate specific marketplace queries + queryClient.invalidateQueries({ + queryKey: ['marketplace-intents', marketplaceProcessId] + }); + queryClient.invalidateQueries({ + queryKey: ['marketplace-orders'] + }); + + // Invalidate domain and ANT info + if (antId) { + queryClient.invalidateQueries({ queryKey: ['ant', antId] }); + queryClient.invalidateQueries({ queryKey: ['domain', domainName] }); + }Using
invalidateQueriesinstead ofresetQueriesis also preferred, as it refetches rather than clearing the cache.
552-552: Add optional chaining for consistency.While the code is safe due to the enclosing conditional on line 525, using optional chaining here aligns with the null-safety pattern and makes the code more robust to future refactoring.
🔎 Proposed change
<ArweaveID - id={new ArweaveTransactionID(result.antTransferResult.id)} + id={new ArweaveTransactionID(result.antTransferResult?.id ?? '')} type={ArweaveIdTypes.INTERACTION} shouldLink characterCount={8} />Note: This assumes
ArweaveTransactionIDconstructor handles empty strings gracefully. Based on the relevant code snippet, it throws an error for invalid IDs, so you may need to conditionally render theArweaveIDcomponent instead:- <span> + {result.antTransferResult?.id && ( + <span> View transfer on aolink{' '} <ArweaveID - id={new ArweaveTransactionID(result.antTransferResult.id)} + id={new ArweaveTransactionID(result.antTransferResult.id)} type={ArweaveIdTypes.INTERACTION} shouldLink characterCount={8} /> - </span> + </span> + )}
577-577: Remove redundant query invalidation.This query invalidation on line 577 is redundant with the specific query resets performed on lines 494-508 within the success path. Consider removing this or consolidating the invalidation logic.
💡 Suggested change
- queryClient.invalidateQueries({ queryKey: ['marketplace'] }); } catch (error) { setWorkflowComplete(true); setWorkflowError(true); eventEmitter.emit('error', error); } finally { setIsLoading(false); }If you need to ensure queries are invalidated even on error, move this to the
finallyblock and remove the earlier invalidations:} catch (error) { setWorkflowComplete(true); setWorkflowError(true); eventEmitter.emit('error', error); } finally { setIsLoading(false); + // Invalidate queries whether success or error + queryClient.invalidateQueries({ queryKey: ['marketplace'] }); }
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/components/modals/ListNameForSaleModal/ListNameForSaleModal.tsx
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-09-10T16:35:55.727Z
Learnt from: dtfiedler
Repo: ar-io/arns-react PR: 873
File: src/components/modals/ant-management/UpgradeDomainsModal/UpgradeDomainsModal.tsx:0-0
Timestamp: 2025-09-10T16:35:55.727Z
Learning: In arns-react, when calling buildDomainInfoQuery (src/hooks/useDomainInfo.tsx), you must pass arioProcessId along with arioContract, domain, aoNetwork, hyperbeamUrl, and antRegistryProcessId; omitting arioProcessId can lead to “No ANT id or record found” and break upgrades.
Applied to files:
src/components/modals/ListNameForSaleModal/ListNameForSaleModal.tsx
📚 Learning: 2025-09-10T16:35:55.727Z
Learnt from: dtfiedler
Repo: ar-io/arns-react PR: 873
File: src/components/modals/ant-management/UpgradeDomainsModal/UpgradeDomainsModal.tsx:0-0
Timestamp: 2025-09-10T16:35:55.727Z
Learning: In arns-react, when calling buildDomainInfoQuery (src/hooks/useDomainInfo.tsx), you must pass arioProcessId along with arioContract, domain, aoNetwork, hyperbeamUrl, and antRegistryProcessId; omitting arioProcessId can lead to "No ANT id or record found" and break upgrades.
Applied to files:
src/components/modals/ListNameForSaleModal/ListNameForSaleModal.tsx
📚 Learning: 2025-12-11T05:20:18.825Z
Learnt from: vilenarios
Repo: ar-io/arns-react PR: 902
File: src/components/modals/turbo/panels/crypto/CryptoConfirmation.tsx:536-540
Timestamp: 2025-12-11T05:20:18.825Z
Learning: Guideline: For tokens on Base network that use 6 decimals (e.g., base-ario ERC-20), convert amount to smallest units using a 1e6 multiplier. Use Math.round(amount * 1e6) when computing the on-chain value. Do not assume 18 decimals. Apply this conversion consistently in code paths that perform payments in CryptoConfirmation.tsx or similar components, and document that the base-ario/USDC-like tokens use 6 decimals to avoid mispricing or transfer errors.
Applied to files:
src/components/modals/ListNameForSaleModal/ListNameForSaleModal.tsx
🧬 Code graph analysis (1)
src/components/modals/ListNameForSaleModal/ListNameForSaleModal.tsx (6)
src/hooks/useArIOPrice.ts (1)
useArIoPrice(9-32)src/hooks/useMarketplaceInfo.ts (1)
useMarketplaceInfo(4-25)src/hooks/useMarketplaceUserAssets.ts (1)
useMarketplaceUserAssets(44-61)src/hooks/useANTIntent.ts (1)
useANTIntent(17-75)src/utils/searchUtils/searchUtils.ts (1)
sleep(128-130)src/services/arweave/ArweaveTransactionID.ts (1)
ArweaveTransactionID(7-31)
| deposit: { | ||
| title: 'Deposit Listing Fee', | ||
| description: 'Deposit the listing fee to the marketplace', | ||
| icon: <DollarSign className="w-4 h-4" />, | ||
| }, | ||
| createIntent: { | ||
| title: 'Create Intent', | ||
| description: 'Create an intent to list the name for sale', | ||
| icon: <DollarSign className="w-4 h-4" />, | ||
| }, | ||
| transferANT: { | ||
| title: 'Transfer ANT', | ||
| description: 'Transfer the ANT to the marketplace', | ||
| icon: <DollarSign className="w-4 h-4" />, | ||
| }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Icon inconsistency in default workflow steps.
All workflow steps initially use DollarSign icon, but createIntent (line 76) and transferANT (line 81) should use their respective icons (Handshake and Send) from the start. While updateWorkflowSteps corrects these icons when the workflow runs, any initial rendering before updates will display incorrect icons.
🔎 Proposed fix
createIntent: {
title: 'Create Intent',
description: 'Create an intent to list the name for sale',
- icon: <DollarSign className="w-4 h-4" />,
+ icon: <Handshake className="w-4 h-4" />,
},
transferANT: {
title: 'Transfer ANT',
description: 'Transfer the ANT to the marketplace',
- icon: <DollarSign className="w-4 h-4" />,
+ icon: <Send className="w-4 h-4" />,
},🤖 Prompt for AI Agents
In src/components/modals/ListNameForSaleModal/ListNameForSaleModal.tsx around
lines 68 to 82, the default workflow steps set all icons to DollarSign which
causes createIntent and transferANT to render incorrect icons initially; update
the createIntent icon to use the Handshake component and the transferANT icon to
use the Send component in the initial steps object, and ensure Handshake and
Send are imported at the top of the file so the correct icons display before
updateWorkflowSteps runs.
| const difference = Math.max( | ||
| 1, | ||
| Math.round(feeDetails.listingFee - existingBalance), | ||
| ).toString(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use Math.ceil instead of Math.round for deposit amount.
Math.round() can round down, potentially depositing less than required. For example, if the difference is 1.4 ARIO, Math.round(1.4) returns 1, which is insufficient. Use Math.ceil() to always round up.
🔎 Proposed fix
const difference = Math.max(
1,
- Math.round(feeDetails.listingFee - existingBalance),
+ Math.ceil(feeDetails.listingFee - existingBalance),
).toString();📝 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.
| const difference = Math.max( | |
| 1, | |
| Math.round(feeDetails.listingFee - existingBalance), | |
| ).toString(); | |
| const difference = Math.max( | |
| 1, | |
| Math.ceil(feeDetails.listingFee - existingBalance), | |
| ).toString(); |
🤖 Prompt for AI Agents
In src/components/modals/ListNameForSaleModal/ListNameForSaleModal.tsx around
lines 351 to 354, the deposit difference uses Math.round which can under-deposit
(e.g., 1.4 → 1); change the calculation to use Math.ceil so the difference is
always rounded up, and keep the Math.max(1, ...) guard and .toString()
conversion unchanged.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
🧹 Nitpick comments (5)
src/components/pages/ViewListing/ViewListing.tsx (5)
239-266: Enhance deposit confirmation polling with better reliability.The balance polling logic has several areas for improvement:
- Fixed 5-second intervals: No exponential backoff could lead to unnecessary load during temporary network issues.
- Silent error handling: Line 262 logs balance check errors but doesn't inform the user of transient issues.
- No progress feedback: Users wait up to 50 seconds without updates during the polling loop.
🔎 Proposed improvements
// Wait for deposit to be confirmed let newBalance = 0; let tries = 0; const maxTries = 10; + const baseDelay = 3000; // Start with 3s while (newBalance < orderPrice && tries < maxTries) { try { const balanceResult = await marketplaceContract.getMarketplaceBalance({ address: walletAddress.toString(), }); newBalance = new mARIOToken(Number(balanceResult?.balance ?? 0)) .toARIO() .valueOf(); if (newBalance >= orderPrice) break; + + // Update user on progress + updateWorkflowSteps({ + step: 'deposit', + status: 'processing', + description: `Confirming deposit... (${tries + 1}/${maxTries})`, + }); } catch (error) { console.error('Error checking balance:', error); } tries++; - await sleep(5000); + // Exponential backoff: 3s, 6s, 12s, capped at 15s + const delay = Math.min(baseDelay * Math.pow(2, tries - 1), 15000); + await sleep(delay); }
320-333: Query invalidation strategy may be overly broad.Lines 320-332 use
resetQuerieswith predicate functions that search forprocessIdornamein query keys. This approach:
- Could inadvertently reset unrelated queries if keys happen to contain these strings
- May not be comprehensive if some related queries use different key structures
Consider using more specific query key patterns if the query keys follow a consistent structure:
// Instead of predicate-based matching queryClient.resetQueries({ queryKey: ['domainInfo', name] }); queryClient.resetQueries({ queryKey: ['ant', domainInfo.processId] }); queryClient.resetQueries({ queryKey: ['ant-info', domainInfo.processId] }); queryClient.invalidateQueries({ queryKey: ['marketplace'] }); queryClient.invalidateQueries({ queryKey: ['marketplace-order', domainInfo.processId] }); queryClient.invalidateQueries({ queryKey: ['marketplace-user-assets', walletAddress] });This is more explicit and reduces the risk of unintended cache invalidations.
556-564: Manual address truncation instead of using ArweaveID component.Lines 556-564 manually truncate the seller address with string slicing, while other places use the
ArweaveIDcomponent. This creates inconsistency in how addresses are displayed.For consistency with the rest of the component, consider using the
ArweaveIDcomponent here as well:🔎 Proposed change
<a href={`https://aolink.ar-io.dev/#/entity/${orderData.creator}`} target="_blank" rel="noopener noreferrer" className="text-blue-400 hover:text-blue-300 transition-colors flex items-center gap-1 justify-end" > - {orderData.creator.slice(0, 8)}... - {orderData.creator.slice(-8)} + <ArweaveID + id={new ArweaveTransactionID(orderData.creator)} + type={ArweaveIdTypes.ADDRESS} + shouldLink={false} + characterCount={8} + /> <ExternalLink className="w-3 h-3" /> </a>Note: You'll need to adjust this to avoid double-linking, or remove the outer
<a>tag ifArweaveIDhandles the link.
393-401: Loading state could use Loader component.The loading state displays plain text instead of using the imported
Loadercomponent, which would provide a more polished and consistent loading experience.🔎 Proposed improvement
if (isLoading) { return ( <div className="page text-white"> <div className="flex justify-center items-center min-h-[400px]"> - <div className="text-white text-xl">Loading listing details...</div> + <div className="flex flex-col items-center gap-4"> + <Loader size={40} /> + <div className="text-white text-xl">Loading listing details...</div> + </div> </div> </div> ); }
113-121: Use consistent mARIO-to-ARIO conversion method for balance comparison.The balance check uses two different conversion APIs from different packages:
- Line 116–118:
new mARIOToken(...).toARIO().valueOf()(from @ar.io/sdk) for user balance- Line 119:
mARIOToTokenAmount(...)(from @ardrive/turbo-sdk) for order priceWhile both convert mARIO to ARIO, standardizing on a single method throughout the component would improve clarity and reduce maintenance risk. Consider using
mARIOTokenconsistently since it's already imported and used elsewhere in the codebase.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/components/pages/ViewListing/ViewListing.tsx
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-12-11T05:20:18.825Z
Learnt from: vilenarios
Repo: ar-io/arns-react PR: 902
File: src/components/modals/turbo/panels/crypto/CryptoConfirmation.tsx:536-540
Timestamp: 2025-12-11T05:20:18.825Z
Learning: Guideline: For tokens on Base network that use 6 decimals (e.g., base-ario ERC-20), convert amount to smallest units using a 1e6 multiplier. Use Math.round(amount * 1e6) when computing the on-chain value. Do not assume 18 decimals. Apply this conversion consistently in code paths that perform payments in CryptoConfirmation.tsx or similar components, and document that the base-ario/USDC-like tokens use 6 decimals to avoid mispricing or transfer errors.
Applied to files:
src/components/pages/ViewListing/ViewListing.tsx
🧬 Code graph analysis (1)
src/components/pages/ViewListing/ViewListing.tsx (15)
src/components/icons/index.ts (2)
CheckIcon(89-89)ArNSLogo(72-72)src/components/pages/index.ts (1)
ViewListing(18-18)src/state/contexts/GlobalState.tsx (1)
useGlobalState(178-179)src/state/contexts/WalletState.tsx (1)
useWalletState(49-50)src/hooks/useDomainInfo.tsx (1)
useDomainInfo(206-255)src/hooks/useMarketplaceOrder.tsx (1)
useMarketplaceOrder(44-56)src/hooks/useArIOPrice.ts (1)
useArIoPrice(9-32)src/hooks/useMarketplaceUserAssets.ts (1)
useMarketplaceUserAssets(44-61)src/utils/searchUtils/searchUtils.ts (2)
sleep(128-130)decodeDomainToASCII(48-52)src/utils/network.ts (1)
queryClient(39-45)src/services/arweave/ArweaveTransactionID.ts (1)
ArweaveTransactionID(7-31)src/components/data-display/AntLogoIcon.tsx (1)
AntLogoIcon(7-57)src/utils/common/common.tsx (1)
formatARIOWithCommas(215-219)src/components/layout/index.ts (1)
Loader(12-12)src/components/data-display/VerticalTimelineStepper.tsx (1)
VerticalTimelineStepper(3-41)
🔇 Additional comments (9)
src/components/pages/ViewListing/ViewListing.tsx (9)
1-41: LGTM!The imports are well-organized and all appear necessary for the marketplace listing functionality.
43-66: LGTM!The workflow step configuration is clear and provides good user feedback structure for the multi-step purchase process.
68-112: LGTM!The component initialization and state management are well-structured. The
isExpiredcheck correctly compares the current time against the order expiration.
123-180: LGTM!The
updateWorkflowStepsfunction provides clear visual feedback with appropriate icons and styling for each workflow state.
233-236: Deposit amount calculation looks correct.The calculation
Math.max(1, Math.ceil(orderPrice - existingBalance))appropriately:
- Ensures at least 1 ARIO is deposited (prevents zero deposits)
- Rounds up fractional amounts to cover the full order price
- Accounts for existing balance to avoid over-depositing
628-654: LGTM! Buy button state handling is comprehensive.The buy button correctly handles all states:
- Disabled when expired or already processing
- Shows appropriate icons and text for each state
- Uses proper styling for visual feedback
656-698: LGTM! Workflow UI provides excellent user feedback.The processing workflow UI effectively:
- Shows real-time progress through the VerticalTimelineStepper
- Provides clear messaging for success and failure states
- Offers appropriate actions (retry on error, view name on success)
- Warns users not to close the page during processing
239-244: Add timeout handling for the purchase transaction.The
buyFixedPriceANTcall at line 300 lacks any timeout mechanism. If the transaction hangs indefinitely, users remain stuck in the processing state with no recourse except refreshing the page.The deposit call (line 239) has implicit timeout protection via its polling loop (10 retries × 5-second intervals), but the purchase call has none.
Wrap the purchase transaction with explicit timeout handling:
async function withTimeout<T>( promise: Promise<T>, timeoutMs: number, errorMessage: string ): Promise<T> { const timeout = new Promise<never>((_, reject) => setTimeout(() => reject(new Error(errorMessage)), timeoutMs) ); return Promise.race([promise, timeout]); } // Usage at line 300: const result = await withTimeout( marketplaceContract.buyFixedPriceANT({ antId: domainInfo.processId, }), 60000, // 60 second timeout 'Purchase transaction timed out' );⛔ Skipped due to learnings
Learnt from: vilenarios Repo: ar-io/arns-react PR: 902 File: src/components/modals/turbo/panels/crypto/CryptoConfirmation.tsx:536-540 Timestamp: 2025-12-11T05:20:30.374Z Learning: The Base ARIO (base-ario) ERC-20 token contract on Base network uses 6 decimals, not 18 decimals. When converting base-ario amounts to smallest units for payment transactions, use 1e6 multiplier (Math.round(amount * 1e6)), consistent with USDC handling.
427-428: Decimal precision for ARIO conversions is accurate.Mainnet ARIO has a denomination of 6, and each ARIO is divisible into 1,000,000 µARIO, confirming 6 decimal precision. The code correctly uses
mARIOToTokenAmountfrom turbo-sdk to convert from mARIO (10^-6 ARIO) to ARIO, andformatARIOWithCommasappropriately displays the result with up to 3 decimal places. The base-ario ERC-20 token also uses 6 decimals, consistent with mainnet ARIO. No precision issues detected.
| ao: aoClient, | ||
| }), | ||
| signer: createAoSigner(wallet.contractSigner), | ||
| ario: arioContract as AoARIOWrite, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Type assertion bypasses type safety.
The type assertion arioContract as AoARIOWrite on Line 215 could hide potential type mismatches. If arioContract doesn't fully satisfy the AoARIOWrite interface, this could lead to runtime errors.
🔎 Verify type compatibility or add validation
Option 1: Add runtime validation:
+ if (!arioContract || typeof arioContract.depositArIO !== 'function') {
+ throw new Error('Invalid ARIO contract instance');
+ }
const marketplaceContract = new ArNSMarketplaceWrite({
process: new AOProcess({
processId: marketplaceProcessId,
ao: aoClient,
}),
signer: createAoSigner(wallet.contractSigner),
ario: arioContract as AoARIOWrite,
});Option 2: Verify that arioContract from global state is properly typed as AoARIOWrite throughout the application to eliminate the need for this assertion.
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In src/components/pages/ViewListing/ViewListing.tsx around line 215, the inline
type assertion "arioContract as AoARIOWrite" bypasses compile-time safety;
instead ensure arioContract is correctly typed at its source (e.g., update the
global state/type definitions or the selector that provides arioContract to
return AoARIOWrite) or add a small runtime type guard/validation that checks
required methods/properties before using it and only then narrow the type;
remove the unsafe "as" cast and either rely on the corrected source typing or
perform the guard and narrow the type with a safe conditional.
| id={new ArweaveTransactionID(orderData.creator)} | ||
| type={ArweaveIdTypes.ADDRESS} | ||
| shouldLink={true} | ||
| characterCount={12} | ||
| /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ArweaveTransactionID constructor can throw on invalid input.
The ArweaveTransactionID constructor throws an error if the provided ID doesn't match the expected 43-character format. If orderData.creator, domainInfo.processId, or orderData.id contain invalid values, this will cause unhandled exceptions in the render phase, crashing the component.
Add validation before constructing ArweaveTransactionID instances:
🔎 Proposed defensive rendering
{orderData?.creator && (
<div className="flex items-center justify-between gap-2 text-grey text-sm">
<span>Owner:</span>
<div className="flex items-end justify-end">
+ {(() => {
+ try {
+ return (
<ArweaveID
id={new ArweaveTransactionID(orderData.creator)}
type={ArweaveIdTypes.ADDRESS}
shouldLink={true}
characterCount={12}
/>
+ );
+ } catch {
+ return <span className="text-white font-mono">{orderData.creator}</span>;
+ }
+ })()}
</div>
</div>
)}Apply similar try-catch wrappers for Lines 505-509 (ANT Process) and Lines 617-621 (Order ID).
Alternatively, create a safe wrapper component:
function SafeArweaveID({ id, ...props }: { id: string } & Omit<ComponentProps<typeof ArweaveID>, 'id'>) {
try {
return <ArweaveID id={new ArweaveTransactionID(id)} {...props} />;
} catch {
return <span className="text-white font-mono">{id}</span>;
}
}Also applies to: 505-509, 617-621
🤖 Prompt for AI Agents
In src/components/pages/ViewListing/ViewListing.tsx around lines 483-487,
constructing new ArweaveTransactionID(orderData.creator) can throw for invalid
IDs; wrap that construction with a validation or try/catch and fall back to
rendering the raw string (or a safe placeholder) so render errors are not
thrown; apply the same defensive pattern to the other instances mentioned (lines
505-509 for domainInfo.processId and 617-621 for orderData.id), or replace
usages with a small SafeArweaveID wrapper component that catches construction
errors and returns a safe fallback.
| <LeaseDurationFromEndTimestamp | ||
| endTimestamp={ | ||
| domainInfo?.arnsRecord?.type === 'lease' | ||
| ? (domainInfo.arnsRecord as any)?.endTimestamp |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Type assertion bypasses type safety for lease record.
Line 519 uses (domainInfo.arnsRecord as any)?.endTimestamp to access the endTimestamp property. This bypasses TypeScript's type checking and could lead to runtime errors if the structure changes.
🔎 Use proper type guard instead
<span className="text-white">
<LeaseDurationFromEndTimestamp
endTimestamp={
- domainInfo?.arnsRecord?.type === 'lease'
- ? (domainInfo.arnsRecord as any)?.endTimestamp
- : undefined // Permabuy domains or no record - let component show "Permanent"
+ domainInfo?.arnsRecord?.type === 'lease' &&
+ 'endTimestamp' in domainInfo.arnsRecord
+ ? domainInfo.arnsRecord.endTimestamp
+ : undefined
}
/>
</span>Or better, update the type definition to properly discriminate the union type based on the type field.
📝 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.
| ? (domainInfo.arnsRecord as any)?.endTimestamp | |
| <span className="text-white"> | |
| <LeaseDurationFromEndTimestamp | |
| endTimestamp={ | |
| domainInfo?.arnsRecord?.type === 'lease' && | |
| 'endTimestamp' in domainInfo.arnsRecord | |
| ? domainInfo.arnsRecord.endTimestamp | |
| : undefined | |
| } | |
| /> | |
| </span> |
🤖 Prompt for AI Agents
In src/components/pages/ViewListing/ViewListing.tsx around line 519, the code
uses `(domainInfo.arnsRecord as any)?.endTimestamp`, which bypasses TypeScript
safety; replace this with a proper type-narrowing approach: add a type guard
(e.g., function isLeaseRecord(r): r is LeaseRecord { return r?.type === 'lease'
&& 'endTimestamp' in r }) or refine the domainInfo/arnsRecord union type so you
can safely access endTimestamp only when arnsRecord is the lease variant, then
use that guard before reading endTimestamp and handle the non-lease case
explicitly (fallback/undefined) instead of casting to any.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Nitpick comments (1)
src/components/pages/ViewListing/ViewListing.tsx (1)
320-333: Prefer explicit query key patterns over string matching.The query invalidation logic uses string matching on query keys, which is fragile. If query key structures change (e.g., from strings to structured objects), this will silently fail.
🔎 Proposed refactor using explicit query keys
Consider defining query key factories in your hooks and using them consistently:
// In your hooks file or a shared queryKeys file export const queryKeys = { domain: (name: string) => ['domain', name] as const, marketplaceOrder: (antId: string) => ['marketplace', 'order', antId] as const, marketplaceUserAssets: () => ['marketplace', 'user-assets'] as const, };Then update invalidation:
- queryClient.resetQueries({ - predicate: (query) => - query.queryKey.some( - (key: unknown) => - typeof key === 'string' && key.includes(domainInfo.processId), - ), - }); - queryClient.resetQueries({ - predicate: (query) => - query.queryKey.some( - (key: unknown) => typeof key === 'string' && key.includes(name), - ), - }); - queryClient.invalidateQueries({ queryKey: ['marketplace'] }); + // Invalidate domain-specific queries + queryClient.invalidateQueries({ + queryKey: queryKeys.domain(name) + }); + // Invalidate order queries + queryClient.invalidateQueries({ + queryKey: queryKeys.marketplaceOrder(domainInfo.processId) + }); + // Invalidate user assets + queryClient.invalidateQueries({ + queryKey: queryKeys.marketplaceUserAssets() + }); + // Invalidate all marketplace listings + queryClient.invalidateQueries({ + queryKey: ['marketplace'] + });This approach is type-safe, explicit, and won't break silently if query key structures evolve.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/components/pages/ViewListing/ViewListing.tsx
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2025-12-02T17:38:52.889Z
Learnt from: vilenarios
Repo: ar-io/arns-react PR: 899
File: src/services/turbo/BaseTokenPurchaseService.ts:0-0
Timestamp: 2025-12-02T17:38:52.889Z
Learning: In src/services/turbo/BaseTokenPurchaseService.ts, the function executeBaseTokenPurchase intentionally only handles Base token top-up to Turbo credits, not the actual ArNS purchase. The ArNS purchase is handled separately by dispatchArIOInteraction in Checkout.tsx because it manages multiple complex purchase workflows (lease, permabuy, extend, increase undernames, etc.) with their own state management. This separation of concerns is by design.
Applied to files:
src/components/pages/ViewListing/ViewListing.tsx
📚 Learning: 2025-09-10T16:35:55.727Z
Learnt from: dtfiedler
Repo: ar-io/arns-react PR: 873
File: src/components/modals/ant-management/UpgradeDomainsModal/UpgradeDomainsModal.tsx:0-0
Timestamp: 2025-09-10T16:35:55.727Z
Learning: In arns-react, when calling buildDomainInfoQuery (src/hooks/useDomainInfo.tsx), you must pass arioProcessId along with arioContract, domain, aoNetwork, hyperbeamUrl, and antRegistryProcessId; omitting arioProcessId can lead to “No ANT id or record found” and break upgrades.
Applied to files:
src/components/pages/ViewListing/ViewListing.tsx
📚 Learning: 2025-09-10T16:35:55.727Z
Learnt from: dtfiedler
Repo: ar-io/arns-react PR: 873
File: src/components/modals/ant-management/UpgradeDomainsModal/UpgradeDomainsModal.tsx:0-0
Timestamp: 2025-09-10T16:35:55.727Z
Learning: In arns-react, when calling buildDomainInfoQuery (src/hooks/useDomainInfo.tsx), you must pass arioProcessId along with arioContract, domain, aoNetwork, hyperbeamUrl, and antRegistryProcessId; omitting arioProcessId can lead to "No ANT id or record found" and break upgrades.
Applied to files:
src/components/pages/ViewListing/ViewListing.tsx
📚 Learning: 2025-12-11T05:20:18.825Z
Learnt from: vilenarios
Repo: ar-io/arns-react PR: 902
File: src/components/modals/turbo/panels/crypto/CryptoConfirmation.tsx:536-540
Timestamp: 2025-12-11T05:20:18.825Z
Learning: Guideline: For tokens on Base network that use 6 decimals (e.g., base-ario ERC-20), convert amount to smallest units using a 1e6 multiplier. Use Math.round(amount * 1e6) when computing the on-chain value. Do not assume 18 decimals. Apply this conversion consistently in code paths that perform payments in CryptoConfirmation.tsx or similar components, and document that the base-ario/USDC-like tokens use 6 decimals to avoid mispricing or transfer errors.
Applied to files:
src/components/pages/ViewListing/ViewListing.tsx
🔇 Additional comments (1)
src/components/pages/ViewListing/ViewListing.tsx (1)
81-87: No issue. TheuseDomainInfohook's public signature only acceptsdomainandantId; callers cannot supplyarioProcessId. The call at line 85 with{ domain: name }is correct as implemented.
| // Wait for deposit to be confirmed | ||
| let newBalance = 0; | ||
| let tries = 0; | ||
| const maxTries = 10; | ||
| while (newBalance < orderPrice && tries < maxTries) { | ||
| try { | ||
| const balanceResult = | ||
| await marketplaceContract.getMarketplaceBalance({ | ||
| address: walletAddress.toString(), | ||
| }); | ||
| newBalance = new mARIOToken(Number(balanceResult?.balance ?? 0)) | ||
| .toARIO() | ||
| .valueOf(); | ||
|
|
||
| if (newBalance >= orderPrice) break; | ||
| } catch (error) { | ||
| console.error('Error checking balance:', error); | ||
| } | ||
| tries++; | ||
| await sleep(5000); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add cleanup for balance polling to prevent memory leaks.
The balance polling loop doesn't check if the component is still mounted. If the user navigates away during the purchase flow, the loop continues executing and may attempt state updates on an unmounted component.
🔎 Recommended fix using AbortController pattern
async function handleBuy() {
+ const abortController = new AbortController();
setIsBuying(true);
setShowProcessing(true);
setWorkflowComplete(false);
setWorkflowError(false);
setWorkflowSteps(defaultBuyWorkflowSteps);
try {
// ... existing validation ...
if (!hasSufficientBalance) {
updateWorkflowSteps({ step: 'deposit', status: 'processing' });
// ... deposit logic ...
// Wait for deposit to be confirmed
let newBalance = 0;
let tries = 0;
const maxTries = 10;
- while (newBalance < orderPrice && tries < maxTries) {
+ while (newBalance < orderPrice && tries < maxTries && !abortController.signal.aborted) {
try {
const balanceResult =
await marketplaceContract.getMarketplaceBalance({
address: walletAddress.toString(),
});
newBalance = new mARIOToken(Number(balanceResult?.balance ?? 0))
.toARIO()
.valueOf();
if (newBalance >= orderPrice) break;
} catch (error) {
console.error('Error checking balance:', error);
}
tries++;
await sleep(5000);
}
+ if (abortController.signal.aborted) {
+ throw new Error('Purchase cancelled');
+ }
+
if (newBalance < orderPrice) {
throw new Error('Failed to deposit enough ARIO');
}
// ... rest of logic ...
}
} catch (error) {
console.error(error);
setWorkflowComplete(true);
setWorkflowError(true);
eventEmitter.emit('error', error);
} finally {
setIsBuying(false);
}
}Then add cleanup in a useEffect:
useEffect(() => {
const controller = new AbortController();
return () => controller.abort();
}, []);Or consider moving the entire purchase flow to a custom hook that properly manages lifecycle.
📝 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.
| // Wait for deposit to be confirmed | |
| let newBalance = 0; | |
| let tries = 0; | |
| const maxTries = 10; | |
| while (newBalance < orderPrice && tries < maxTries) { | |
| try { | |
| const balanceResult = | |
| await marketplaceContract.getMarketplaceBalance({ | |
| address: walletAddress.toString(), | |
| }); | |
| newBalance = new mARIOToken(Number(balanceResult?.balance ?? 0)) | |
| .toARIO() | |
| .valueOf(); | |
| if (newBalance >= orderPrice) break; | |
| } catch (error) { | |
| console.error('Error checking balance:', error); | |
| } | |
| tries++; | |
| await sleep(5000); | |
| } | |
| const abortControllerRef = useRef<AbortController | null>(null); | |
| async function handleBuy() { | |
| abortControllerRef.current = new AbortController(); | |
| setIsBuying(true); | |
| setShowProcessing(true); | |
| setWorkflowComplete(false); | |
| setWorkflowError(false); | |
| setWorkflowSteps(defaultBuyWorkflowSteps); | |
| try { | |
| // ... existing validation ... | |
| if (!hasSufficientBalance) { | |
| updateWorkflowSteps({ step: 'deposit', status: 'processing' }); | |
| // ... deposit logic ... | |
| // Wait for deposit to be confirmed | |
| let newBalance = 0; | |
| let tries = 0; | |
| const maxTries = 10; | |
| while (newBalance < orderPrice && tries < maxTries && !abortControllerRef.current?.signal.aborted) { | |
| try { | |
| const balanceResult = | |
| await marketplaceContract.getMarketplaceBalance({ | |
| address: walletAddress.toString(), | |
| }); | |
| newBalance = new mARIOToken(Number(balanceResult?.balance ?? 0)) | |
| .toARIO() | |
| .valueOf(); | |
| if (newBalance >= orderPrice) break; | |
| } catch (error) { | |
| console.error('Error checking balance:', error); | |
| } | |
| tries++; | |
| await sleep(5000); | |
| } | |
| if (abortControllerRef.current?.signal.aborted) { | |
| throw new Error('Purchase cancelled'); | |
| } | |
| if (newBalance < orderPrice) { | |
| throw new Error('Failed to deposit enough ARIO'); | |
| } | |
| // ... rest of logic ... | |
| } | |
| } catch (error) { | |
| console.error(error); | |
| setWorkflowComplete(true); | |
| setWorkflowError(true); | |
| eventEmitter.emit('error', error); | |
| } finally { | |
| setIsBuying(false); | |
| } | |
| } | |
| useEffect(() => { | |
| return () => { | |
| if (abortControllerRef.current) { | |
| abortControllerRef.current.abort(); | |
| } | |
| }; | |
| }, []); |
🤖 Prompt for AI Agents
In src/components/pages/ViewListing/ViewListing.tsx around lines 246 to 266, the
balance-polling loop can continue running after the component unmounts causing
memory leaks and possible state updates on an unmounted component; fix by wiring
an AbortController (or a mounted flag) into the loop: create the controller in a
surrounding useEffect and return a cleanup that calls controller.abort(), pass
controller.signal or check controller.signal.aborted (or the mounted flag)
inside the while loop and break/return when aborted, and guard any state updates
so they only run when not aborted/unmounted; alternatively move the purchase
flow into a custom hook that accepts and respects an AbortSignal and performs
the same cleanup.
| eventEmitter.emit('success', { | ||
| name: 'Buy ANT', | ||
| message: ( | ||
| <span> | ||
| Successfully bought {name}! | ||
| <br /> | ||
| <br /> | ||
| <span>View transaction on aolink </span> | ||
| <ArweaveID | ||
| id={new ArweaveTransactionID(result.id)} | ||
| type={ArweaveIdTypes.INTERACTION} | ||
| characterCount={8} | ||
| shouldLink={true} | ||
| /> | ||
| </span> | ||
| ), | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add defensive error handling for ArweaveTransactionID construction.
Line 344 constructs new ArweaveTransactionID(result.id) within the success event message without validation. If result.id is invalid or malformed, this will throw an error and prevent the success message from being displayed, degrading the user experience.
🔎 Proposed defensive rendering
eventEmitter.emit('success', {
name: 'Buy ANT',
message: (
<span>
Successfully bought {name}!
<br />
<br />
- <span>View transaction on aolink </span>
- <ArweaveID
- id={new ArweaveTransactionID(result.id)}
- type={ArweaveIdTypes.INTERACTION}
- characterCount={8}
- shouldLink={true}
- />
+ {result?.id ? (
+ <>
+ <span>View transaction on aolink </span>
+ {(() => {
+ try {
+ return (
+ <ArweaveID
+ id={new ArweaveTransactionID(result.id)}
+ type={ArweaveIdTypes.INTERACTION}
+ characterCount={8}
+ shouldLink={true}
+ />
+ );
+ } catch (error) {
+ console.warn('Invalid transaction ID:', result.id);
+ return <span className="font-mono">{result.id}</span>;
+ }
+ })()}
+ </>
+ ) : null}
</span>
),
});Alternatively, validate result.id format before constructing the ArweaveTransactionID, or add a safe wrapper utility function.
📝 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.
| eventEmitter.emit('success', { | |
| name: 'Buy ANT', | |
| message: ( | |
| <span> | |
| Successfully bought {name}! | |
| <br /> | |
| <br /> | |
| <span>View transaction on aolink </span> | |
| <ArweaveID | |
| id={new ArweaveTransactionID(result.id)} | |
| type={ArweaveIdTypes.INTERACTION} | |
| characterCount={8} | |
| shouldLink={true} | |
| /> | |
| </span> | |
| ), | |
| }); | |
| eventEmitter.emit('success', { | |
| name: 'Buy ANT', | |
| message: ( | |
| <span> | |
| Successfully bought {name}! | |
| <br /> | |
| <br /> | |
| {result?.id ? ( | |
| <> | |
| <span>View transaction on aolink </span> | |
| {(() => { | |
| try { | |
| return ( | |
| <ArweaveID | |
| id={new ArweaveTransactionID(result.id)} | |
| type={ArweaveIdTypes.INTERACTION} | |
| characterCount={8} | |
| shouldLink={true} | |
| /> | |
| ); | |
| } catch (error) { | |
| console.warn('Invalid transaction ID:', result.id); | |
| return <span className="font-mono">{result.id}</span>; | |
| } | |
| })()} | |
| </> | |
| ) : null} | |
| </span> | |
| ), | |
| }); |
🤖 Prompt for AI Agents
In src/components/pages/ViewListing/ViewListing.tsx around lines 335 to 351,
constructing new ArweaveTransactionID(result.id) directly inside the success
message can throw if result.id is missing or malformed; wrap the construction in
a small validation/try-catch (or use a safe helper) that verifies result.id is a
valid transaction id before creating ArweaveTransactionID, and if validation
fails fallback to rendering a plain transaction id/link or omit the ArweaveID
component while logging the error for telemetry so the success message still
displays reliably.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
♻️ Duplicate comments (2)
package.json (2)
26-26: Alpha version still in use (duplicate concern).This was previously flagged: using
^1.35.0-alpha.2introduces instability risk. Consider switching to the latest stable release or pinning the exact alpha version if you require newer features.
36-37: Sentry version mismatch still present (duplicate concern).This was previously flagged:
@sentry/browserat^10.29.0and@sentry/reactat^7.45.0are incompatible major versions. Update both packages to use the same major version to avoid runtime conflicts.
🧹 Nitpick comments (1)
package.json (1)
60-60: Consider using caret range for consistency.
lucide-reactis pinned to an exact version0.556.0without a caret, which is inconsistent with other dependencies. Consider using^0.556.0to allow patch and minor updates, unless the exact pinning is intentional to prevent breaking changes.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
yarn.lockis excluded by!**/yarn.lock,!**/*.lock
📒 Files selected for processing (1)
package.json
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: lint_and_build
🔇 Additional comments (1)
package.json (1)
23-23: Verify the necessity of using an alpha version.
@ar.io/sdkis set to^3.23.0-alpha.3, which introduces instability risk. The latest stable release is3.19.0; alpha releases can have breaking changes, and the caret operator allows auto-upgrading to newer alpha versions. Ensure this alpha version is required for specific features; otherwise, use the stable release or pin the exact alpha version.
| "@dha-team/arbundles": "^1.0.4", | ||
| "@permaweb/ao-scheduler-utils": "^0.0.29", | ||
| "@permaweb/aoconnect": "0.0.68", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🌐 Web query:
@permaweb/ao-scheduler-utils npm package stability production ready
💡 Result:
Short answer: Not production-ready. Use for experimentation only.
Why:
- Package is pre-1.0 (v0.0.27) and published under the permaweb/ao monorepo which is described as an actively changing testnet-phase project (BSL/experimental). [1][2].
- npm shows modest usage (≈1.7k weekly downloads) and recent releases but no 1.x stable marker or clear OSS license on the package page. [1][3].
- The GitHub monorepo indicates the stack is still under active development and “no guarantees or core support” for self-hosting; project guidance describes it as testnet/experimental. [2].
Recommendation:
- OK to adopt for development, prototypes, or internal tooling that can tolerate breaking changes.
- Avoid for high‑risk production systems unless you vendor/pin a specific commit, add tests, and are prepared to maintain/fix issues yourself.
- Before deploying, review the repo’s issues/PRs, test coverage, recent commits, and confirm the exact license for your use case. [2][4].
Sources:
[1] npm package page for @permaweb/ao-scheduler-utils.
[2] permaweb/ao GitHub repository (monorepo README & notes about testnet/dev).
[3] Libraries.io / Socket.dev package metadata and security/maintenance summaries.
🌐 Web query:
@permaweb/aoconnect npm package version history stability
💡 Result:
Summary — @permaweb/aoconnect stability and version history
- Package: @permaweb/aoconnect (successor to deprecated @permaweb/ao-connect). [1][2]
- Release activity: frequent small releases in the 0.0.x line; recent publishes within days/weeks (npm shows recent publish and latest metadata). [1][3]
- Latest published version (reported by Snyk/npm during checks): 0.0.92 (Snyk) / npm listing shows 0.0.90 with a recent publish stamp — confirm exact latest with npm before install. [3][1]
- Popularity/maintenance signals: modest weekly downloads (thousands) and an active repo under permaweb org; package is maintained and not deprecated. [1][2]
- Migration note: older package @permaweb/ao-connect was deprecated in favor of @permaweb/aoconnect. [4]
If you want I can:
- fetch the exact latest version and full changelog now from the npm registry and the GitHub releases (recommended to confirm).
Consider the experimental status of these dependencies and evaluate whether outdated pinned versions are appropriate.
Both @permaweb/ao-scheduler-utils (^0.0.29) and @permaweb/aoconnect (0.0.68) are part of the permaweb/ao testnet-phase project and explicitly designated as experimental, not production-ready. The pinned version of @permaweb/aoconnect appears outdated (0.0.68 vs. available 0.0.90+); evaluate whether updating to the latest 0.0.x release is necessary for security or bug fixes, and confirm the team understands the stability risks of adopting experimental dependencies.
🤖 Prompt for AI Agents
In package.json around lines 27-29, the review flags that
@permaweb/ao-scheduler-utils (^0.0.29) and @permaweb/aoconnect (0.0.68) are
experimental and the aoconnect pin appears outdated; inspect the packages'
changelogs/release notes for security/bug fixes since 0.0.68, run the project's
test suite and smoke tests against the latest 0.0.x release (or the most recent
safe patch, e.g., bump aoconnect to ^0.0.90+ if fixes are relevant), decide
whether to relax or tighten the version spec (use caret for receiving patches or
pin if stability is required), update package.json accordingly, regenerate the
lockfile, run full CI, and add a brief comment in the repo or PR noting the
experimental risk so the team acknowledges the stability trade-offs.
| "lottie-react": "^2.4.0", | ||
| "lucide-react": "^0.438.0", | ||
| "lucide-react": "0.556.0", | ||
| "permaweb-deploy": "^2.5.1", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Move deployment tool to devDependencies.
permaweb-deploy is a deployment tool used only in build scripts (lines 8-9), not in runtime code. It should be in devDependencies rather than dependencies to avoid unnecessarily increasing the production bundle size and installation footprint.
🔎 Proposed fix
Move permaweb-deploy from dependencies to devDependencies:
"lucide-react": "0.556.0",
- "permaweb-deploy": "^2.5.1",
"plimit-lit": "^3.0.1",And add it to devDependencies section (around line 93):
"http-server": "^14.1.1",
"husky": "^9.1.7",
+ "permaweb-deploy": "^2.5.1",
"lint-staged": "^13.1.0",📝 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.
| "permaweb-deploy": "^2.5.1", | |
| // Dependencies section (with permaweb-deploy removed): | |
| "lucide-react": "0.556.0", | |
| "plimit-lit": "^3.0.1", | |
| // DevDependencies section (with permaweb-deploy added): | |
| "http-server": "^14.1.1", | |
| "husky": "^9.1.7", | |
| "permaweb-deploy": "^2.5.1", | |
| "lint-staged": "^13.1.0", |
🤖 Prompt for AI Agents
In package.json around line 61, permaweb-deploy is listed under "dependencies"
but is only used by build/deploy scripts; remove the "permaweb-deploy": "^2.5.1"
entry from "dependencies" and add the same version string under
"devDependencies" (around line ~93) so it’s installed only for development/build
environments; after editing, run your package manager (npm/yarn/pnpm) to update
node_modules and the lockfile so CI and installs reflect the change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/components/data-display/tables/DomainsTable.tsx (1)
519-525: Missing dependencies inuseEffectmay cause stale table data.The effect uses
walletAddress(lines 463, 472, 487),latestAntVersion(line 473), andmarketplaceProcessId(line 480), but these are not included in the dependency array. This can cause:
- Table not updating when user connects/disconnects wallet
- Stale role calculations when marketplace process ID changes
- Incorrect compatibility status with stale ANT version
🔎 Proposed fix
}, [ domainData, loading, loadingArnsState, primaryNameData, dispatchArNSState, + walletAddress, + latestAntVersion, + marketplaceProcessId, ]);
🧹 Nitpick comments (1)
src/components/data-display/tables/DomainsTable.tsx (1)
289-372: Consider extracting duplicated modal logic.The
ContinueWorkflowModalrendering pattern is duplicated across multiple conditional branches (lines 321-329, 357-365). This could be extracted into a shared pattern.🔎 Suggested refactor
function RoleDisplay({ role, domainName, processId, }: { role: string; domainName: string; processId: string; }) { const { hasIntent, intent } = useANTIntent(processId); const [showContinueWorkflowModal, setShowContinueWorkflowModal] = useState(false); + const renderModal = () => + showContinueWorkflowModal && intent && ( + <ContinueWorkflowModal + show={showContinueWorkflowModal} + onClose={() => setShowContinueWorkflowModal(false)} + domainName={domainName} + antId={processId} + intentId={intent.intentId} + /> + ); + if (role === 'marketplace') { if (hasIntent && intent) { return ( <> <div className="flex items-center gap-2"> <button onClick={() => setShowContinueWorkflowModal(true)} className="relative flex items-center gap-2 hover:opacity-80 transition-opacity" > {/* ... */} </button> </div> - {showContinueWorkflowModal && ( - <ContinueWorkflowModal - show={showContinueWorkflowModal} - onClose={() => setShowContinueWorkflowModal(false)} - domainName={domainName} - antId={processId} - intentId={intent.intentId} - /> - )} + {renderModal()} </> ); } // ... apply similarly to other branches }
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
yarn.lockis excluded by!**/yarn.lock,!**/*.lock
📒 Files selected for processing (4)
package.jsonsrc/components/data-display/tables/DomainsTable.tsxsrc/components/pages/Register/Checkout.tsxsrc/utils/constants.ts
🚧 Files skipped from review as they are similar to previous changes (3)
- package.json
- src/components/pages/Register/Checkout.tsx
- src/utils/constants.ts
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-09-10T16:35:55.727Z
Learnt from: dtfiedler
Repo: ar-io/arns-react PR: 873
File: src/components/modals/ant-management/UpgradeDomainsModal/UpgradeDomainsModal.tsx:0-0
Timestamp: 2025-09-10T16:35:55.727Z
Learning: In arns-react, when calling buildDomainInfoQuery (src/hooks/useDomainInfo.tsx), you must pass arioProcessId along with arioContract, domain, aoNetwork, hyperbeamUrl, and antRegistryProcessId; omitting arioProcessId can lead to "No ANT id or record found" and break upgrades.
Applied to files:
src/components/data-display/tables/DomainsTable.tsx
📚 Learning: 2025-09-10T16:35:55.727Z
Learnt from: dtfiedler
Repo: ar-io/arns-react PR: 873
File: src/components/modals/ant-management/UpgradeDomainsModal/UpgradeDomainsModal.tsx:0-0
Timestamp: 2025-09-10T16:35:55.727Z
Learning: In arns-react, when calling buildDomainInfoQuery (src/hooks/useDomainInfo.tsx), you must pass arioProcessId along with arioContract, domain, aoNetwork, hyperbeamUrl, and antRegistryProcessId; omitting arioProcessId can lead to “No ANT id or record found” and break upgrades.
Applied to files:
src/components/data-display/tables/DomainsTable.tsx
📚 Learning: 2025-12-11T05:20:18.825Z
Learnt from: vilenarios
Repo: ar-io/arns-react PR: 902
File: src/components/modals/turbo/panels/crypto/CryptoConfirmation.tsx:536-540
Timestamp: 2025-12-11T05:20:18.825Z
Learning: Guideline: For tokens on Base network that use 6 decimals (e.g., base-ario ERC-20), convert amount to smallest units using a 1e6 multiplier. Use Math.round(amount * 1e6) when computing the on-chain value. Do not assume 18 decimals. Apply this conversion consistently in code paths that perform payments in CryptoConfirmation.tsx or similar components, and document that the base-ario/USDC-like tokens use 6 decimals to avoid mispricing or transfer errors.
Applied to files:
src/components/data-display/tables/DomainsTable.tsx
🧬 Code graph analysis (1)
src/components/data-display/tables/DomainsTable.tsx (6)
src/utils/constants.ts (1)
ARIO_DISCORD_LINK(251-251)src/hooks/useANTIntent.ts (1)
useANTIntent(17-75)src/hooks/useMarketplaceOrder.tsx (1)
useMarketplaceOrder(44-56)src/hooks/useInterruptedWorkflows.ts (2)
InterruptedWorkflow(6-10)useInterruptedWorkflows(15-67)src/utils/common/common.tsx (1)
formatForMaxCharCount(134-148)src/components/modals/index.ts (2)
ListNameForSaleModal(15-15)UpgradeDomainForMarketplaceModal(16-16)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: deploy-preview
- GitHub Check: lint_and_build
🔇 Additional comments (6)
src/components/data-display/tables/DomainsTable.tsx (6)
7-66: LGTM!The new imports are well-organized and support the marketplace integration. The separation between component imports, hooks, and icons is clear.
127-205: LGTM!Error state tooltip is well-implemented with proper event handling, clipboard API error handling, and secure external link opening.
406-453: LGTM!The marketplace-related state management and helper functions follow existing patterns and are cleanly implemented.
607-632: LGTM!The enhanced name column rendering with tooltip and external link is well-structured.
870-939: LGTM!The action column logic correctly handles the different marketplace states:
- Marketplace-owned domains without interrupted workflows → marketplace icon
- Domains with interrupted workflows → continue workflow action
- Other domains → list for sale/upgrade flow
The conditional flow is clear and the upgrade-to-marketplace path correctly chains to the listing modal.
1082-1113: LGTM!The modal integrations correctly handle the upgrade-to-marketplace flow, chaining the sale modal after successful upgrade.
| // Helper component to determine the correct icon for marketplace-owned domains | ||
| function MarketplaceActionIcon({ | ||
| domainName, | ||
| processId, | ||
| }: { domainName: string; processId: string }) { | ||
| const { hasIntent } = useANTIntent(processId); | ||
|
|
||
| const { data: order, error: orderError } = useMarketplaceOrder({ | ||
| antId: processId, | ||
| }); | ||
|
|
||
| // If there's an intent but no order (or order fetch failed), show Activity icon | ||
| const hasOrder = order && !orderError; | ||
|
|
||
| // Error state: no intent and no order (marketplace owns ANT but nothing exists) | ||
| if (!hasIntent && !hasOrder) { | ||
| return <ErrorStateTooltip domainName={domainName} antId={processId} />; | ||
| } | ||
|
|
||
| if (hasIntent && !hasOrder) { | ||
| return ( | ||
| <Tooltip | ||
| message="Pending marketplace activity" | ||
| icon={ | ||
| <div className="flex items-center justify-center w-[18px] h-[18px] text-orange-400 hover:text-orange-300 transition-colors"> | ||
| <Activity className="w-[18px] h-[18px]" /> | ||
| </div> | ||
| } | ||
| /> | ||
| ); | ||
| } | ||
|
|
||
| // Default to marketplace store icon | ||
| return ( | ||
| <Tooltip | ||
| message="View in Marketplace" | ||
| icon={ | ||
| <Link | ||
| to={`/marketplace/names/${domainName}`} | ||
| className="flex items-center justify-center w-[18px] h-[18px] text-blue-400 hover:text-blue-300 transition-colors" | ||
| > | ||
| <StoreIcon className="w-[18px] h-[18px]" /> | ||
| </Link> | ||
| } | ||
| /> | ||
| ); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider loading states before showing error tooltip.
When useANTIntent or useMarketplaceOrder are still loading, their data will be undefined/false, which could cause the error state (line 222) to show prematurely before the queries complete.
🔎 Proposed fix
function MarketplaceActionIcon({
domainName,
processId,
}: { domainName: string; processId: string }) {
- const { hasIntent } = useANTIntent(processId);
+ const { hasIntent, isLoading: intentLoading } = useANTIntent(processId);
- const { data: order, error: orderError } = useMarketplaceOrder({
+ const { data: order, error: orderError, isLoading: orderLoading } = useMarketplaceOrder({
antId: processId,
});
+ // Don't show error state while still loading
+ if (intentLoading || orderLoading) {
+ return null;
+ }
+
// If there's an intent but no order (or order fetch failed), show Activity icon
const hasOrder = order && !orderError;
Summary by CodeRabbit
✏️ Tip: You can customize this high-level summary in your review settings.