-
Notifications
You must be signed in to change notification settings - Fork 1
feat: add infastructure for long running tools and implement limit orders #168
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
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Caution Review failedThe pull request is closed. 📝 WalkthroughWalkthroughAdds end-to-end CoW Protocol limit-order support: server tools (create/get/cancel), client UIs and multi-step execution hooks (create/cancel), activity types/normalization, activity-row refactor into per-type components, step utilities and EIP‑712 signing helpers, and analytics tracking. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant ChatUI
participant Wallet
participant Server
participant CoW
Note over User,CoW: Create Limit Order
User->>ChatUI: trigger createLimitOrder(input)
ChatUI->>Server: executeCreateLimitOrder(input)
Server-->>ChatUI: CreateLimitOrderOutput (orderParams, signingData, approvalTx?)
ChatUI->>Wallet: switch network (if needed)
Wallet-->>ChatUI: network switched
alt approval required
ChatUI->>Wallet: send approval tx
Wallet-->>ChatUI: approval tx hash
end
ChatUI->>Wallet: sign EIP‑712 order
Wallet-->>ChatUI: signature
ChatUI->>Server: submitSignedOrder(signature)
Server->>CoW: POST signed order
CoW-->>Server: orderId
Server-->>ChatUI: orderId/trackingUrl
ChatUI->>User: show complete / tracking link
sequenceDiagram
participant User
participant ChatUI
participant Wallet
participant Server
participant CoW
Note over User,CoW: Cancel Limit Order
User->>ChatUI: trigger cancelLimitOrder(orderId)
ChatUI->>Server: executeCancelLimitOrder(orderId)
Server-->>ChatUI: CancellationSigningData
ChatUI->>Wallet: switch network (if needed)
Wallet-->>ChatUI: network switched
ChatUI->>Wallet: sign cancellation (EIP‑712)
Wallet-->>ChatUI: signature
ChatUI->>Server: submit cancellation(signature)
Server->>CoW: DELETE order with signature
CoW-->>Server: confirmation
Server-->>ChatUI: cancellation confirmed
ChatUI->>User: show cancelled status
sequenceDiagram
participant User
participant ChatUI
participant Server
participant CoW
Note over User,CoW: Get Limit Orders
User->>ChatUI: request list (params)
ChatUI->>Server: executeGetLimitOrders(params)
Server->>CoW: fetch orders per chain
CoW-->>Server: orders
Server->>Server: enrich & format orders
Server-->>ChatUI: GetLimitOrdersOutput (order list)
ChatUI->>User: render list with badges & tracking links
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60–75 minutes
Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro Disabled knowledge base sources:
📒 Files selected for processing (34)
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 |
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: 13
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/agentic-chat/src/hooks/useToolExecutionEffect.ts (1)
33-54: Don’t silently swallow tool failures—persist an error/terminal state (or rethrow selectively).Catching + fire-and-forget avoids unhandled rejections, but it also means callers/UI can’t react; you may end up with initialized runtime state and no indication the tool failed. Consider setting an error/status in runtime state (keyed by
toolCallId) inside the catch, and includetoolCallIdin the log.const executeWrapper = async () => { try { await executeRef.current(data, setStateRef.current) } catch (error) { - console.error('[useToolExecutionEffect] Tool execution failed:', error) + console.error('[useToolExecutionEffect] Tool execution failed:', { toolCallId, error }) + setStateRef.current((draft: any) => { + draft.status = 'error' + draft.error = String(error) + }) } }
🧹 Nitpick comments (10)
apps/agentic-server/src/lib/cow/index.ts (2)
53-64: Unreachable error due to fallback logic.Line 54 falls back to
COW_SETTLEMENT_CONTRACT[1]when the chainId isn't found, soverifyingContractwill always be defined. The error on line 56 is unreachable dead code. This also silently uses mainnet's contract for unsupported chains instead of failing early.Consider removing the fallback to properly reject unsupported chains:
function getEIP712Domain(chainId: number): CowEIP712Domain { - const verifyingContract = COW_SETTLEMENT_CONTRACT[chainId] ?? COW_SETTLEMENT_CONTRACT[1] - if (!verifyingContract) { - throw new Error(`No settlement contract for chain ${chainId}`) - } + const verifyingContract = COW_SETTLEMENT_CONTRACT[chainId] + if (!verifyingContract) { + throw new Error(`No settlement contract for chain ${chainId}`) + } return {
147-153: Consider adding request timeouts for external API calls.All
fetchcalls to the CoW API lack timeouts. If the CoW API is slow or unresponsive, these requests could hang indefinitely. Consider usingAbortControllerwith a reasonable timeout (e.g., 30 seconds) for better resilience.const controller = new AbortController() const timeoutId = setTimeout(() => controller.abort(), 30000) try { const response = await fetch(url, { signal: controller.signal, // ... other options }) } finally { clearTimeout(timeoutId) }Also applies to: 176-180, 196-200, 244-253
apps/agentic-chat/src/components/tools/GetLimitOrdersUI.tsx (1)
23-29: Make link click handler element-typed + more robust status check.
ConsiderMouseEvent<HTMLAnchorElement>forhandleLinkClick, andObject.prototype.hasOwnProperty.call(STATUS_CONFIG, status)to avoid prototype edge-cases.Also applies to: 97-105
apps/agentic-server/src/index.ts (1)
91-110: Consolidate the 3 re-export blocks into one for maintainability.
No functional issue—just reduces duplication and future drift when renaming/moving exports.apps/agentic-chat/src/components/tools/LimitOrderUI.tsx (1)
29-48: Emoji in UI strings may not match existing tone/i18n strategy.
If the rest of the app avoids emoji (or requires i18n), consider replacing with standard copy.apps/agentic-chat/src/components/Portfolio/ActivityRow.tsx (1)
22-31: Type link click handler to anchor + consider optionaltrackingUrl.
handleLinkClickcan beMouseEvent<HTMLAnchorElement>. Also, ifLimitOrderActivityDetails.trackingUrlcan ever be missing, guard the Track Order row (or make the type required).Also applies to: 114-147
apps/agentic-server/src/tools/limitOrder/createLimitOrder.ts (1)
54-68: Schema constraints should be enforced at validation time.Update
expirationHourstoz.number().min(1).max(8760).default(168)to validate bounds directly. ForsellAmountandlimitPrice, add regex validation to ensure numeric strings, e.g.,z.string().regex(/^\d+(\.\d+)?$/).describe(...)to reject non-numeric input at schema level rather than relying on runtime handling.apps/agentic-server/src/tools/limitOrder/cancelLimitOrder.ts (1)
34-48: Consider logging failed detection attempts for debugging.The empty catch block silently swallows errors, making it harder to diagnose issues when order detection fails across all chains.
for (const chainId of chainsToTry) { try { const order = await getCowOrder(orderId, chainId) if (order) return chainId } catch { - // Order not found on this chain, try next + // Order not found on this chain, try next + console.debug(`[cancelLimitOrder] Order ${orderId} not found on chain ${chainId}`) } }apps/agentic-chat/src/hooks/useLimitOrderExecution.tsx (2)
102-132: Clarify intent:currentStepis always set toCOMPLETE.The function unconditionally sets
currentStep = LimitOrderStep.COMPLETEregardless of the persisted phases. This appears intentional (hydration only occurs for terminal states), but adding a brief comment would improve maintainability.function persistedStateToLimitOrderState(persisted: PersistedToolState): LimitOrderState { const completedSteps = new Set<LimitOrderStep>() - const currentStep = LimitOrderStep.COMPLETE + // Persisted states are only saved for completed/errored flows, so we default to COMPLETE + const currentStep = LimitOrderStep.COMPLETE
166-216: Response parsing could be more robust.The manual quote stripping
orderId.replace(/"/g, '')is fragile. If the CoW API returns a proper JSON string, consider usingJSON.parse()for safer parsing.- const orderId = await response.text() - return orderId.replace(/"/g, '') + const responseText = await response.text() + // CoW API returns the order ID as a JSON-encoded string (e.g., "0x123...") + try { + return JSON.parse(responseText) + } catch { + // Fallback if response is not JSON-encoded + return responseText.replace(/"/g, '') + }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (17)
apps/agentic-chat/src/components/Portfolio/ActivityRow.tsx(6 hunks)apps/agentic-chat/src/components/toolUIRegistry.tsx(2 hunks)apps/agentic-chat/src/components/tools/GetLimitOrdersUI.tsx(1 hunks)apps/agentic-chat/src/components/tools/LimitOrderUI.tsx(1 hunks)apps/agentic-chat/src/hooks/useLimitOrderExecution.tsx(1 hunks)apps/agentic-chat/src/hooks/useToolExecutionEffect.ts(1 hunks)apps/agentic-chat/src/lib/activityNormalizer.ts(3 hunks)apps/agentic-chat/src/stores/chatStore.ts(2 hunks)apps/agentic-chat/src/types/activity.ts(2 hunks)apps/agentic-server/src/index.ts(1 hunks)apps/agentic-server/src/lib/cow/index.ts(1 hunks)apps/agentic-server/src/lib/cow/types.ts(1 hunks)apps/agentic-server/src/routes/chat.ts(4 hunks)apps/agentic-server/src/tools/limitOrder/cancelLimitOrder.ts(1 hunks)apps/agentic-server/src/tools/limitOrder/createLimitOrder.ts(1 hunks)apps/agentic-server/src/tools/limitOrder/getLimitOrders.ts(1 hunks)apps/agentic-server/src/tools/limitOrder/index.ts(1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-09-18T20:08:31.777Z
Learnt from: kaladinlight
Repo: shapeshift/agentic-chat PR: 73
File: apps/agentic-chat/src/components/assistant-ui/GetPortalsAssets.tsx:30-33
Timestamp: 2025-09-18T20:08:31.777Z
Learning: In assistant UI components using makeAssistantToolUI, the toolName should match the exact key name as defined in the agent's tools configuration object, not the underlying tool's id. For example, if a tool is referenced as 'getPortalsAssetsTool' in the agent's tools object, the UI component should use toolName: 'getPortalsAssetsTool'.
Applied to files:
apps/agentic-chat/src/components/toolUIRegistry.tsx
📚 Learning: 2025-09-22T20:32:10.539Z
Learnt from: kaladinlight
Repo: shapeshift/agentic-chat PR: 76
File: apps/agentic-server/src/mastra/tools/asset/coingecko/constants.ts:1-10
Timestamp: 2025-09-22T20:32:10.539Z
Learning: When cross-app dependencies are identified (like server code importing from chat packages), the user prefers to extract shared constants into dedicated packages in the packages/ directory following the existing pattern of shapeshiftoss/types and shapeshiftoss/utils.
Applied to files:
apps/agentic-chat/src/stores/chatStore.ts
🧬 Code graph analysis (4)
apps/agentic-chat/src/components/toolUIRegistry.tsx (2)
apps/agentic-chat/src/components/tools/LimitOrderUI.tsx (1)
LimitOrderUI(15-187)apps/agentic-chat/src/components/tools/GetLimitOrdersUI.tsx (1)
GetLimitOrdersUI(111-179)
apps/agentic-chat/src/lib/activityNormalizer.ts (3)
apps/agentic-chat/src/stores/chatStore.ts (1)
PersistedToolState(21-29)apps/agentic-chat/src/types/activity.ts (2)
ActivityItem(70-70)LimitOrderActivityDetails(39-52)apps/agentic-server/src/tools/limitOrder/createLimitOrder.ts (1)
CreateLimitOrderOutput(90-106)
apps/agentic-server/src/routes/chat.ts (5)
apps/agentic-server/src/index.ts (3)
createLimitOrderTool(92-92)getLimitOrdersTool(99-99)cancelLimitOrderTool(105-105)apps/agentic-server/src/tools/limitOrder/createLimitOrder.ts (1)
createLimitOrderTool(272-294)apps/agentic-server/src/tools/limitOrder/index.ts (3)
createLimitOrderTool(2-2)getLimitOrdersTool(11-11)cancelLimitOrderTool(19-19)apps/agentic-server/src/tools/limitOrder/getLimitOrders.ts (1)
getLimitOrdersTool(155-176)apps/agentic-server/src/tools/limitOrder/cancelLimitOrder.ts (1)
cancelLimitOrderTool(111-130)
apps/agentic-server/src/lib/cow/index.ts (1)
apps/agentic-server/src/lib/cow/types.ts (10)
CowEIP712Types(44-46)CowEIP712Domain(37-42)CreateCowOrderParams(55-64)CreateCowOrderResult(66-72)isCowSupportedChain(96-98)CowOrderQuote(24-35)CowOrderSigningData(48-53)getCowExplorerUrl(100-106)getCowApiUrl(108-121)CowOrder(7-22)
⏰ 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: main
🔇 Additional comments (21)
apps/agentic-chat/src/hooks/useToolExecutionEffect.ts (1)
38-53: Verify this remains single-execution under React 18/19 StrictMode and store timing.The “run once” gating depends on
hasRuntimeState(toolCallId)becoming true before any second effect pass; ifinitializeRuntimeStateisn’t synchronous/atomic, you can double-execute. Worth confirming with a StrictMode dev run and/or making initialization+check atomic in the store.apps/agentic-server/src/lib/cow/index.ts (3)
70-114: LGTM!The function correctly prepares EIP-712 signing data for CoW limit orders. The placeholder orderId is appropriately noted in the comment, and the actual ID will be assigned after submission.
214-231: LGTM!The cancellation signing data preparation correctly follows the CoW Protocol's EIP-712 format for order cancellations.
236-259: LGTM!The cancellation submission correctly uses the DELETE method with the signature payload as expected by the CoW API.
apps/agentic-chat/src/stores/chatStore.ts (1)
21-29: LGTM!The
PersistedToolStateinterface is correctly extended to support limit orders by adding'limit_order'to thetoolTypeunion andCreateLimitOrderOutputto thetoolOutputunion. This follows the existing pattern for swap, send, and network_switch tools.apps/agentic-chat/src/types/activity.ts (2)
39-52: LGTM!The
LimitOrderActivityDetailsinterface is well-structured with all necessary fields for displaying limit order information. IncludingtrackingUrldirectly in the details is convenient for UI rendering.
64-68: LGTM!Using
Omit<BaseActivityItem, 'txHash'>is the correct approach since CoW limit orders are off-chain and don't have a transaction hash until execution. TheorderIdfield appropriately replacestxHashfor order identification.apps/agentic-chat/src/lib/activityNormalizer.ts (1)
118-119: No action needed.LimitOrderSummary.sellAssetincludes bothchainIdandnetworkfields as string properties (defined at createLimitOrder.ts lines 76-77), so accessing them at lines 118-119 is safe and will not returnundefined.apps/agentic-server/src/tools/limitOrder/index.ts (1)
1-24: Clean barrel export.
Nice consolidation point for the limit-order tool surface.apps/agentic-chat/src/components/Portfolio/ActivityRow.tsx (1)
149-175: Good: suppress TX link for limit orders and add dedicated details.
This avoids misleading users (limit orders are off-chain until executed) while still providing an explorer tracking link.apps/agentic-server/src/tools/limitOrder/createLimitOrder.ts (1)
184-201: Remove Sepolia from consideration or add explicit chain validation earlier in the flow.The WETH_ADDRESSES mapping currently covers only mainnet chains (Ethereum, Gnosis, Arbitrum). If a Sepolia request (chainId 11155111) reaches this code, it will fall back to the native token address, which CoW Protocol will reject. Since CoW Protocol does not officially support Sepolia testnet, either ensure the function rejects unsupported chains before reaching this point, or remove any Sepolia-related functionality from the supported chain list if it's being exposed elsewhere.
apps/agentic-server/src/routes/chat.ts (3)
28-28: LGTM!The import follows the existing pattern and is correctly placed with other tool imports.
164-187: LGTM!The three limit order tools are correctly wired with the same pattern as other wallet-context-dependent tools (e.g.,
initiateSwapTool,sendTool). Each tool logs arguments and passeswalletContextto the underlying execute function.
304-310: LGTM!The system prompt documentation for limit orders is clear and consistent with the existing format. It correctly documents supported networks, use cases, and behavior.
apps/agentic-server/src/tools/limitOrder/cancelLimitOrder.ts (3)
9-15: LGTM!Schema definition is well-structured with clear descriptions. The optional
networkparameter with auto-detection fallback provides good UX.
50-108: LGTM!The execution flow is well-structured with proper validation:
- Chain ID resolution with fallback detection
- Order ownership verification
- Status checks preventing cancellation of fulfilled/cancelled/expired orders
- Proper EIP-712 signing data preparation
The error messages are user-friendly and actionable.
111-129: LGTM!The tool description follows the established pattern with UI card guidance and concise default responses.
apps/agentic-chat/src/hooks/useLimitOrderExecution.tsx (4)
22-53: LGTM!The step enum and state interface are well-designed. The
getStepStatushelper correctly handles all state transitions including failure states.
218-245: LGTM!The hydration logic correctly resets on toolCallId change and properly handles the case where runtime state already exists.
263-396: Well-structured multi-step execution flow.The execution logic properly handles:
- Wallet validation and address verification
- Network switching with primary wallet handling
- Conditional approval flow with confirmation
- EIP-712 signing
- Order submission with proper state persistence at each step
- Comprehensive error handling with toast notifications
The use of
current(draft)from Immer on line 385 correctly captures the state snapshot for persistence after an error.
405-422: LGTM!The return value properly exposes step statuses, error state, and tracking information. The
trackingUrlfallback fromstate.orderIdtoorderData?.trackingUrlhandles both successful submissions and pre-submission states.
940daa0 to
dfd2b10
Compare
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
♻️ Duplicate comments (9)
apps/agentic-chat/src/components/tools/LimitOrderUI.tsx (1)
50-57:completedCountcalculation inconsistent with rendered step statuses.When
needsApprovalis false, the UI renders approval steps asStepStatus.SKIPPED(lines 148, 152), butcompletedCountuses the rawapprovalStep.statusandapprovalConfirmStep.status. This causes progress bar mismatch.Apply this diff to compute effective statuses consistently:
- const completedCount = [ - prepareStep.status, - networkStep.status, - approvalStep.status, - approvalConfirmStep.status, - signStep.status, - submitStep.status, - ].filter(s => s === StepStatus.COMPLETE || s === StepStatus.SKIPPED).length + const effectiveApprovalStatus = needsApproval ? approvalStep.status : StepStatus.SKIPPED + const effectiveApprovalConfirmStatus = needsApproval ? approvalConfirmStep.status : StepStatus.SKIPPED + const completedCount = [ + prepareStep.status, + networkStep.status, + effectiveApprovalStatus, + effectiveApprovalConfirmStatus, + signStep.status, + submitStep.status, + ].filter(s => s === StepStatus.COMPLETE || s === StepStatus.SKIPPED).lengthapps/agentic-server/src/lib/cow/types.ts (1)
74-95: Incomplete sepolia support across mappings.
COW_SUPPORTED_CHAINSincludes sepolia (11155111), butNETWORK_TO_CHAIN_IDandCHAIN_ID_TO_NETWORKomit it. This creates inconsistency whereisCowSupportedChain(11155111)returns true, butCHAIN_ID_TO_NETWORK[11155111]returnsundefined.Either fully support sepolia by adding it to all mappings, or remove it from
COW_SUPPORTED_CHAINS:export const NETWORK_TO_CHAIN_ID: Record<string, number> = { ethereum: 1, gnosis: 100, arbitrum: 42161, + sepolia: 11155111, } export const CHAIN_ID_TO_NETWORK: Record<number, string> = { + 11155111: 'sepolia', }apps/agentic-server/src/tools/limitOrder/createLimitOrder.ts (3)
108-120: Critical:parseFloatcauses precision loss in limit order price calculations.Using
parseFloatfor financial math will silently misprice orders due to floating-point representation errors. UseBigNumberend-to-end as it's already imported.function calculateBuyAmount(buyAsset: Asset, sellAmount: string, limitPrice: string): string { - const sellAmountNum = parseFloat(sellAmount) - const limitPriceNum = parseFloat(limitPrice) - - if (isNaN(sellAmountNum) || isNaN(limitPriceNum)) { - throw new Error('Invalid sellAmount or limitPrice') - } - - const buyAmountNum = sellAmountNum * limitPriceNum - return toBaseUnit(buyAmountNum.toString(), buyAsset.precision) + const bnSell = new BigNumber(sellAmount) + const bnPrice = new BigNumber(limitPrice) + if (!bnSell.isFinite() || !bnPrice.isFinite() || bnSell.lte(0) || bnPrice.lte(0)) { + throw new Error('Invalid sellAmount or limitPrice') + } + const buyAmountHuman = bnSell.times(bnPrice) + return toBaseUnit(buyAmountHuman.toFixed(buyAsset.precision), buyAsset.precision) }
135-136: Remove sensitive input logging.
console.log('[createLimitOrder]:', JSON.stringify(input, null, 2))can leak user trading intent and amounts. Use a structured logger with redaction or remove entirely.- console.log('[createLimitOrder]:', JSON.stringify(input, null, 2))
268-268:trackingUrlwith placeholder 'pending' leads to invalid page.The URL
https://explorer.cow.fi/orders/pendingdoesn't exist. Either maketrackingUrloptional and set it after order submission, or omit it until a real order ID is available.- trackingUrl: getCowExplorerUrl('pending'), + trackingUrl: '', // Will be set after order submission with real order IDOr make the field optional in
CreateLimitOrderOutput:- trackingUrl: string + trackingUrl?: stringapps/agentic-server/src/tools/limitOrder/getLimitOrders.ts (2)
74-79:presignaturePendingmapped toopenloses distinct order state.Per CoW Protocol semantics,
presignaturePending(awaiting on-chain pre-signature, not tradable) differs fromopen(active in orderbook). Collapsing them prevents users from distinguishing orders awaiting signature from actively tradable orders.Either preserve the distinct status or document why simplification is intentional:
function formatOrderStatus(status: CowOrderStatus): CowOrderStatus { - if (status === 'presignaturePending') return 'open' + // Keep presignaturePending distinct - UI can handle both states return status }And update the schema to include
presignaturePendingif preserving:status: z - .enum(['open', 'filled', 'cancelled', 'expired', 'all']) + .enum(['open', 'presignaturePending', 'filled', 'cancelled', 'expired', 'all'])
93-93: Remove verbose input logging.
console.log('[getLimitOrders]:', JSON.stringify(input, null, 2))is noisy and may leak user intent. Use a structured logger with appropriate log levels or remove.- console.log('[getLimitOrders]:', JSON.stringify(input, null, 2))apps/agentic-chat/src/lib/activityNormalizer.ts (1)
92-122: Use network-aware tracking URL from output.Line 110 hardcodes
https://explorer.cow.fi/orders/${orderId}, which always produces the mainnet format. ThegetCowExplorerUrlutility in the server (as noted in past reviews) handles network-specific URLs likeexplorer.cow.fi/gnosis/orders/for Gnosis chain.Apply this diff to use the network-aware URL:
- trackingUrl: `https://explorer.cow.fi/orders/${orderId}`, + trackingUrl: output.trackingUrl,The
output.trackingUrlfromCreateLimitOrderOutputshould already contain the correct network-specific URL from the server.apps/agentic-chat/src/hooks/useLimitOrderExecution.tsx (1)
315-337: Handle missingpublicClientexplicitly before marking approval confirmation complete.
This can markAPPROVAL_CONFIRMATIONas complete without actually confirming on-chain.
🧹 Nitpick comments (10)
apps/agentic-chat/src/components/tools/LimitOrderUI.tsx (1)
69-92: Minor: Inner component functions recreate on each render.
SwapPairandBuyAmountare defined inside the component body, causing new function instances on every render. For this UI, the impact is negligible, but if performance becomes a concern, consider memoizing or extracting them.apps/agentic-server/src/tools/limitOrder/createLimitOrder.ts (1)
192-200: Native token fallback may return original address for unmapped chains.If
WETH_ADDRESSES[evmChainId]is undefined (e.g., for a new chain), the fallback|| sellTokenAddressreturns the native token address, which CoW Protocol likely doesn't accept. This is partially guarded byisCowSupportedChain, but adding explicit validation would be safer.const sellToken = sellTokenAddress.toLowerCase() === NATIVE_TOKEN_ADDRESS.toLowerCase() - ? WETH_ADDRESSES[evmChainId] || sellTokenAddress + ? WETH_ADDRESSES[evmChainId] ?? (() => { throw new Error(`No WETH mapping for chain ${evmChainId}`) })() : sellTokenAddress const buyToken = buyTokenAddress.toLowerCase() === NATIVE_TOKEN_ADDRESS.toLowerCase() - ? WETH_ADDRESSES[evmChainId] || buyTokenAddress + ? WETH_ADDRESSES[evmChainId] ?? (() => { throw new Error(`No WETH mapping for chain ${evmChainId}`) })() : buyTokenAddressapps/agentic-server/src/tools/limitOrder/getLimitOrders.ts (2)
113-173: Sequential chain fetching increases latency.The
for...ofloop queries chains sequentially. For users with wallets on multiple chains, this creates cumulative latency. Consider parallelizing withPromise.allSettled:- for (const chainId of chainsToQuery) { - const chainIdString = `eip155:${chainId}` - let userAddress: string - try { - userAddress = getAddressForChain(walletContext, chainIdString) - } catch { - continue - } - - try { - const orders = await getCowOrders(userAddress, chainId, { limit: 50 }) - // ... process orders - } catch (error) { - console.warn(`Failed to fetch orders from chain ${chainId}:`, error) - } - } + const fetchPromises = chainsToQuery.map(async (chainId) => { + const chainIdString = `eip155:${chainId}` + let userAddress: string + try { + userAddress = getAddressForChain(walletContext, chainIdString) + } catch { + return [] // No wallet for this chain + } + + try { + const orders = await getCowOrders(userAddress, chainId, { limit: 50 }) + // ... process and return orders + } catch (error) { + console.warn(`Failed to fetch orders from chain ${chainId}:`, error) + return [] + } + }) + + const results = await Promise.allSettled(fetchPromises) + for (const result of results) { + if (result.status === 'fulfilled') { + allOrders.push(...result.value) + } + }
44-44: Magic number for default decimals.Consider extracting
DEFAULT_DECIMALS = 18to a shared constants file or adding a comment explaining why 18 is the fallback (standard ERC-20 decimals).apps/agentic-server/src/tools/limitOrder/cancelLimitOrder.ts (1)
34-48: Consider explaining chain priority order.The chain order
[1, 42161, 100](Ethereum, Arbitrum, Gnosis) is stated as "most likely chains first", but this assumption may change over time or differ based on usage patterns.Consider adding a brief comment explaining the priority rationale or making it configurable:
- // Try each chain to find the order - const chainsToTry = [1, 42161, 100] // Most likely chains first + // Try chains in order of typical usage volume: Ethereum > Arbitrum > Gnosis + const chainsToTry = [1, 42161, 100]apps/agentic-chat/src/components/Portfolio/ActivityRow/index.tsx (2)
20-24: Add a defensive fallback for unknown activity types (runtime safety).
Even ifActivityItemis a union, activity data can still be malformed at runtime;ACTIVITY_ICONS[activity.type]would becomeundefinedand crash render.const ACTIVITY_ICONS = { swap: ArrowRightLeft, send: Send, limit_order: Timer, } @@ export function ActivityRow({ activity }: ActivityRowProps) { - const Icon = ACTIVITY_ICONS[activity.type] + const Icon = ACTIVITY_ICONS[activity.type] ?? TimerAlso applies to: 71-78
30-39: MakeformatActivityTitle()exhaustiveness explicit.
Right now there’s nodefault, so a futureActivityItemextension can silently returnundefinedat runtime.function formatActivityTitle(activity: ActivityItem): string { switch (activity.type) { case 'swap': return `Swapped ${formatCryptoAmount(activity.details.sellAsset.amount, { symbol: activity.details.sellAsset.symbol })} to ${formatCryptoAmount(activity.details.buyAsset.amount, { symbol: activity.details.buyAsset.symbol })}` case 'send': return `Sent ${formatCryptoAmount(activity.details.asset.amount, { symbol: activity.details.asset.symbol })}` case 'limit_order': return `Limit order: ${formatCryptoAmount(activity.details.sellAsset.amount, { symbol: activity.details.sellAsset.symbol })} → ${formatCryptoAmount(activity.details.buyAsset.estimatedAmount, { symbol: activity.details.buyAsset.symbol })}` } + // If `ActivityItem` is widened without updating this switch, fail loudly. + return 'Activity' }apps/agentic-chat/src/hooks/useCancelLimitOrderExecution.tsx (2)
140-168: Add timeout/abort + surface structured API errors for cancellation submission.
Right nowfetch()can hang indefinitely, andresponse.text()may not be helpful/consistent error parsing.At minimum, wrap with
AbortController+ a reasonable timeout, and include HTTP status in the thrown error.
141-146: Avoid duplicating CoW chain → base URL mapping across hooks.
This mapping is repeated inuseLimitOrderExecution.tsx; centralize it (e.g.,apps/agentic-chat/src/lib/cow.ts) to prevent drift.Also applies to: 147-150
apps/agentic-chat/src/hooks/useLimitOrderExecution.tsx (1)
166-216: Harden order submission: timeout + response parsing.
response.text()and stripping quotes is brittle; preferawait response.json()(or at least handle a plain string vs JSON string explicitly), and add an abort timeout to avoid hanging tool executions.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (24)
apps/agentic-chat/src/components/Portfolio/ActivityList.tsx(2 hunks)apps/agentic-chat/src/components/Portfolio/ActivityRow.tsx(0 hunks)apps/agentic-chat/src/components/Portfolio/ActivityRow/components/LimitOrderDetails.tsx(1 hunks)apps/agentic-chat/src/components/Portfolio/ActivityRow/components/SendDetails.tsx(1 hunks)apps/agentic-chat/src/components/Portfolio/ActivityRow/components/SwapDetails.tsx(1 hunks)apps/agentic-chat/src/components/Portfolio/ActivityRow/index.tsx(1 hunks)apps/agentic-chat/src/components/toolUIRegistry.tsx(2 hunks)apps/agentic-chat/src/components/tools/CancelLimitOrderUI.tsx(1 hunks)apps/agentic-chat/src/components/tools/GetLimitOrdersUI.tsx(1 hunks)apps/agentic-chat/src/components/tools/LimitOrderUI.tsx(1 hunks)apps/agentic-chat/src/hooks/useCancelLimitOrderExecution.tsx(1 hunks)apps/agentic-chat/src/hooks/useLimitOrderExecution.tsx(1 hunks)apps/agentic-chat/src/hooks/useToolExecutionEffect.ts(1 hunks)apps/agentic-chat/src/lib/activityNormalizer.ts(3 hunks)apps/agentic-chat/src/stores/chatStore.ts(2 hunks)apps/agentic-chat/src/types/activity.ts(2 hunks)apps/agentic-server/src/index.ts(1 hunks)apps/agentic-server/src/lib/cow/index.ts(1 hunks)apps/agentic-server/src/lib/cow/types.ts(1 hunks)apps/agentic-server/src/routes/chat.ts(4 hunks)apps/agentic-server/src/tools/limitOrder/cancelLimitOrder.ts(1 hunks)apps/agentic-server/src/tools/limitOrder/createLimitOrder.ts(1 hunks)apps/agentic-server/src/tools/limitOrder/getLimitOrders.ts(1 hunks)apps/agentic-server/src/tools/limitOrder/index.ts(1 hunks)
💤 Files with no reviewable changes (1)
- apps/agentic-chat/src/components/Portfolio/ActivityRow.tsx
🚧 Files skipped from review as they are similar to previous changes (6)
- apps/agentic-chat/src/components/toolUIRegistry.tsx
- apps/agentic-server/src/routes/chat.ts
- apps/agentic-chat/src/components/tools/GetLimitOrdersUI.tsx
- apps/agentic-server/src/lib/cow/index.ts
- apps/agentic-server/src/index.ts
- apps/agentic-server/src/tools/limitOrder/index.ts
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-22T20:32:10.539Z
Learnt from: kaladinlight
Repo: shapeshift/agentic-chat PR: 76
File: apps/agentic-server/src/mastra/tools/asset/coingecko/constants.ts:1-10
Timestamp: 2025-09-22T20:32:10.539Z
Learning: When cross-app dependencies are identified (like server code importing from chat packages), the user prefers to extract shared constants into dedicated packages in the packages/ directory following the existing pattern of shapeshiftoss/types and shapeshiftoss/utils.
Applied to files:
apps/agentic-chat/src/stores/chatStore.ts
🧬 Code graph analysis (9)
apps/agentic-chat/src/components/Portfolio/ActivityRow/components/SendDetails.tsx (5)
apps/agentic-chat/src/types/activity.ts (1)
SendActivityDetails(28-37)apps/agentic-chat/src/components/ui/ToolCard.tsx (1)
ToolCard(71-78)apps/agentic-chat/src/components/ui/Amount.tsx (1)
Amount(88-92)apps/agentic-chat/src/lib/utils.ts (1)
truncateAddress(9-12)apps/agentic-chat/src/components/Portfolio/ActivityRow.tsx (1)
SendDetails(85-99)
apps/agentic-chat/src/stores/chatStore.ts (2)
apps/agentic-server/src/tools/limitOrder/createLimitOrder.ts (1)
CreateLimitOrderOutput(90-106)apps/agentic-server/src/tools/limitOrder/cancelLimitOrder.ts (1)
CancelLimitOrderOutput(26-32)
apps/agentic-chat/src/components/Portfolio/ActivityRow/components/SwapDetails.tsx (6)
apps/agentic-chat/src/types/activity.ts (1)
SwapActivityDetails(9-26)apps/agentic-chat/src/lib/explorers.ts (1)
getExplorerUrl(1-15)apps/agentic-chat/src/components/ui/ToolCard.tsx (1)
ToolCard(71-78)apps/agentic-chat/src/components/ui/Amount.tsx (1)
Amount(88-92)apps/agentic-chat/src/lib/utils.ts (1)
truncateAddress(9-12)apps/agentic-chat/src/components/Portfolio/ActivityRow.tsx (1)
SwapDetails(30-83)
apps/agentic-server/src/tools/limitOrder/getLimitOrders.ts (4)
apps/agentic-server/src/lib/cow/types.ts (5)
CowOrderStatus(5-5)CowOrder(7-22)NETWORK_TO_CHAIN_ID(83-87)COW_SUPPORTED_CHAINS(75-80)getCowExplorerUrl(100-102)packages/types/src/network.ts (1)
Network(44-44)apps/agentic-server/src/utils/walletContextSimple.ts (2)
WalletContext(4-6)getAddressForChain(13-26)packages/utils/src/index.ts (1)
fromBaseUnit(38-41)
apps/agentic-chat/src/components/Portfolio/ActivityRow/components/LimitOrderDetails.tsx (3)
apps/agentic-chat/src/types/activity.ts (1)
LimitOrderActivityDetails(39-52)apps/agentic-chat/src/components/ui/ToolCard.tsx (1)
ToolCard(71-78)apps/agentic-chat/src/components/ui/Amount.tsx (1)
Amount(88-92)
apps/agentic-chat/src/hooks/useCancelLimitOrderExecution.tsx (5)
apps/agentic-server/src/tools/limitOrder/cancelLimitOrder.ts (1)
CancelLimitOrderOutput(26-32)apps/agentic-chat/src/stores/chatStore.ts (2)
PersistedToolState(22-30)useChatStore(75-194)apps/agentic-chat/src/hooks/useWalletConnection.ts (1)
useWalletConnection(23-48)apps/agentic-chat/src/providers/ChatProvider.tsx (1)
useChatContext(33-39)apps/agentic-chat/src/hooks/useToolExecutionEffect.ts (1)
useToolExecutionEffect(10-60)
apps/agentic-chat/src/lib/activityNormalizer.ts (5)
apps/agentic-chat/src/stores/chatStore.ts (1)
PersistedToolState(22-30)apps/agentic-chat/src/types/activity.ts (2)
ActivityItem(70-70)LimitOrderActivityDetails(39-52)apps/agentic-server/src/index.ts (1)
CreateLimitOrderOutput(95-95)apps/agentic-server/src/tools/limitOrder/createLimitOrder.ts (1)
CreateLimitOrderOutput(90-106)apps/agentic-server/src/tools/limitOrder/index.ts (1)
CreateLimitOrderOutput(6-6)
apps/agentic-chat/src/components/Portfolio/ActivityRow/index.tsx (8)
apps/agentic-chat/src/types/activity.ts (1)
ActivityItem(70-70)apps/agentic-chat/src/lib/explorers.ts (1)
getExplorerUrl(1-15)apps/agentic-chat/src/components/ui/ToolCard.tsx (1)
ToolCard(71-78)apps/agentic-chat/src/lib/utils.ts (1)
truncateAddress(9-12)apps/agentic-chat/src/components/Portfolio/ActivityRow/components/SwapDetails.tsx (1)
SwapDetails(18-71)apps/agentic-chat/src/components/Portfolio/ActivityRow/components/SendDetails.tsx (1)
SendDetails(10-24)apps/agentic-chat/src/components/Portfolio/ActivityRow/components/LimitOrderDetails.tsx (1)
LimitOrderDetails(15-48)apps/agentic-chat/src/components/ui/DrawerListItem.tsx (1)
DrawerListItem(13-41)
apps/agentic-chat/src/components/tools/LimitOrderUI.tsx (6)
apps/agentic-chat/src/components/tools/toolUIHelpers.tsx (1)
ToolUIComponentProps(6-8)apps/agentic-server/src/tools/limitOrder/createLimitOrder.ts (1)
CreateLimitOrderOutput(90-106)apps/agentic-server/src/tools/limitOrder/index.ts (1)
CreateLimitOrderOutput(6-6)apps/agentic-chat/src/hooks/useLimitOrderExecution.tsx (1)
useLimitOrderExecution(218-423)apps/agentic-chat/src/components/ui/TxStepCard.tsx (1)
TxStepCard(149-162)apps/agentic-chat/src/components/ui/TruncateText.tsx (1)
TruncateText(9-26)
⏰ 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: main
🔇 Additional comments (13)
apps/agentic-chat/src/hooks/useToolExecutionEffect.ts (1)
44-52: LGTM! Error handling improves resilience.The try/catch wrapper prevents unhandled promise rejections from crashing the execution flow, and
void executeWrapper()correctly signals fire-and-forget intent.One consideration: errors are only logged to console and not exposed to the UI state. If downstream components need to display tool execution failures, consider updating the state with error information within the catch block.
apps/agentic-chat/src/components/tools/CancelLimitOrderUI.tsx (1)
1-125: LGTM! Well-structured cancellation UI component.The component properly handles:
- Historical skip scenarios
- Step validation before rendering
- Error state propagation to footer
- External link security attributes (
rel="noopener noreferrer")Minor UX note: Using
XCirclewith green color for successful cancellation (line 67) might be slightly confusing since X typically indicates failure. Consider usingCheckCirclefor the completed state while keepingXCirclefor the in-progress state.apps/agentic-chat/src/components/tools/LimitOrderUI.tsx (1)
165-175: LGTM! External link security.Proper use of
target="_blank"withrel="noopener noreferrer"prevents reverse tabnabbing.apps/agentic-server/src/tools/limitOrder/cancelLimitOrder.ts (2)
9-32: LGTM! Well-structured schema and type definitions.The Zod schema provides clear descriptions for the AI agent, and the TypeScript interfaces properly model the EIP-712 cancellation data structure.
50-109: LGTM! Solid security and validation logic.The function properly:
- Validates ownership by comparing wallet addresses
- Prevents cancellation of already filled/cancelled/expired orders
- Provides clear error messages for each failure case
apps/agentic-chat/src/stores/chatStore.ts (1)
2-8: LGTM! Consistent extension of store types.The additions follow the established pattern for tool state management, adding limit order types alongside existing swap/send types.
Also applies to: 24-24, 29-29
apps/agentic-chat/src/components/Portfolio/ActivityList.tsx (1)
15-15: LGTM! Clean integration of limit order activities.The changes properly extend activity filtering to include limit orders and add sensible descending timestamp sorting for a better user experience.
Also applies to: 19-19, 26-26
apps/agentic-chat/src/components/Portfolio/ActivityRow/components/LimitOrderDetails.tsx (1)
1-48: LGTM! Well-structured detail component.The component cleanly renders all limit order fields with appropriate formatting. The click handler on line 11-13 correctly prevents event propagation for the tracking link.
apps/agentic-chat/src/components/Portfolio/ActivityRow/components/SendDetails.tsx (1)
1-24: LGTM! Clean component extraction.This is a straightforward refactor that extracts send details rendering into a dedicated component, improving code organization and maintainability.
apps/agentic-chat/src/components/Portfolio/ActivityRow/components/SwapDetails.tsx (1)
1-71: LGTM! Clean component extraction.This refactor successfully extracts swap details rendering into a dedicated component. The conditional approval TX section and link handling are implemented correctly.
apps/agentic-chat/src/types/activity.ts (1)
39-70: LGTM! Well-designed activity type extensions.The
LimitOrderActivityItemcorrectly omitstxHashfrom the base type since limit orders useorderIdinstead. This design appropriately reflects the different lifecycle of limit orders vs immediate transactions.apps/agentic-chat/src/lib/activityNormalizer.ts (1)
1-23: LGTM! Clean dispatch routing.The normalization dispatcher correctly routes limit orders to the new handler, following the established pattern for swap and send activities.
apps/agentic-chat/src/components/Portfolio/ActivityRow/index.tsx (1)
41-67: Details rendering looks clean and consistent.
Stopping propagation on links is the right move to avoid toggling the drawer, and per-type detail components keep this maintainable.
dfd2b10 to
7536eb0
Compare
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
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/agentic-server/src/routes/chat.ts (1)
272-311: Fix tool naming mismatch in system prompt for limit order tools.The system prompt (lines 305, 308-309) instructs the model to use
createLimitOrder,getLimitOrders, andcancelLimitOrder, but the registered tool keys inbuildTools()(lines 164-186) arecreateLimitOrderTool,getLimitOrdersTool, andcancelLimitOrderTool. The model will fail to invoke these tools because the names in the prompt don't match the registered keys. Update lines 305, 308-309 to use the suffixed names:createLimitOrderTool,getLimitOrdersTool,cancelLimitOrderTool.
♻️ Duplicate comments (12)
apps/agentic-chat/src/components/tools/GetLimitOrdersUI.tsx (1)
174-187: Verify thatsellAmountandbuyAmountfrom server are human-readable values.The
Amount.Cryptocomponent will display these values directly. If the server returns base-unit amounts (wei, etc.), users will see misleading large numbers. This concern was raised in a previous review.#!/bin/bash # Check getLimitOrders output structure to verify amount format ast-grep --pattern 'interface OrderInfo { $$$ }' # Also check the getLimitOrders implementation rg -n "sellAmount|buyAmount" --type ts -A2 -B2 apps/agentic-server/src/tools/limitOrder/getLimitOrders.tsapps/agentic-server/src/tools/limitOrder/createLimitOrder.ts (3)
108-120: Precision loss fromparseFloatfor financial calculations.This issue was flagged in a previous review. The function uses
parseFloatwhich loses precision for large numbers or decimals with many significant digits, potentially mispricing orders.
135-136: Remove or redact sensitive input logging.This issue was flagged in a previous review. Logging the full input can leak user intent and amounts.
273-273:trackingUrlwith placeholder 'pending' may confuse users.This issue was flagged in a previous review. Users clicking this link would land on a non-existent order page.
apps/agentic-chat/src/lib/activityNormalizer.ts (1)
110-110: Hardcoded mainnet URL ignores network-specific explorer paths.This issue was flagged in a previous review. The hardcoded URL
https://explorer.cow.fi/orders/${orderId}produces the mainnet format regardless of chain. CoW explorer uses different paths for Gnosis (/gnosis/orders/) and Arbitrum (/arbitrum/orders/).Consider using the output's trackingUrl if available, or constructing the URL based on chainId:
- trackingUrl: `https://explorer.cow.fi/orders/${orderId}`, + trackingUrl: output.trackingUrl ?? `https://explorer.cow.fi/orders/${orderId}`,apps/agentic-chat/src/components/tools/LimitOrderUI.tsx (1)
50-58: FixcompletedCountto match the rendered “effective” step statuses (approval skipped).
Right now progress can disagree with the UI whenneedsApprovalis false.- const completedCount = [ - prepareStep.status, - networkStep.status, - approvalStep.status, - approvalConfirmStep.status, - signStep.status, - submitStep.status, - ].filter(s => s === StepStatus.COMPLETE || s === StepStatus.SKIPPED).length + const effectiveApprovalStatus = needsApproval ? approvalStep.status : StepStatus.SKIPPED + const effectiveApprovalConfirmStatus = needsApproval ? approvalConfirmStep.status : StepStatus.SKIPPED + const completedCount = [ + prepareStep.status, + networkStep.status, + effectiveApprovalStatus, + effectiveApprovalConfirmStatus, + signStep.status, + submitStep.status, + ].filter(s => s === StepStatus.COMPLETE || s === StepStatus.SKIPPED).lengthAlso applies to: 141-163
apps/agentic-server/src/tools/limitOrder/getLimitOrders.ts (3)
113-173: Parallelize per-chain fetch (independent calls) and keep partial failures.
Today each chain is fetched sequentially; worst-case latency scales with #chains.- for (const chainId of chainsToQuery) { + await Promise.allSettled( + chainsToQuery.map(async chainId => { // Get user address for this chain const chainIdString = `eip155:${chainId}` let userAddress: string try { userAddress = getAddressForChain(walletContext, chainIdString) } catch { // User doesn't have wallet connected for this chain, skip - continue + return } try { const orders = await getCowOrders(userAddress, chainId, { limit: 50 }) @@ - } catch (error) { + } catch (error) { // ... - } - } + } + }) + )
11-21: PreservepresignaturePending(don’t collapse it toopen) and consider aligningfilledvsfulfilledvocabulary.
formatOrderStatus()currently mapspresignaturePending→open, losing an important CoW order state; and the input schema usesfilledwhile the underlying type isfulfilled.export const getLimitOrdersSchema = z.object({ status: z - .enum(['open', 'filled', 'cancelled', 'expired', 'all']) + .enum(['open', 'filled', 'cancelled', 'expired', 'presignaturePending', 'all']) .optional() .default('all') .describe('Filter orders by status. Default is "all".'), @@ function mapStatusFilter(status: string): CowOrderStatus | null { switch (status) { @@ case 'expired': return 'expired' + case 'presignaturePending': + return 'presignaturePending' default: return null } } function formatOrderStatus(status: CowOrderStatus): CowOrderStatus { - // Map CoW status to our simplified status if (status === 'fulfilled') return 'fulfilled' - if (status === 'presignaturePending') return 'open' return status }Also applies to: 59-72, 74-79
89-93: Remove/redactconsole.log/console.warn(PII + user intent) and use the server logger.export async function executeGetLimitOrders( input: GetLimitOrdersInput, walletContext?: WalletContext ): Promise<GetLimitOrdersOutput> { - console.log('[getLimitOrders]:', JSON.stringify(input, null, 2)) + // TODO: use structured logger + redact (avoid logging wallet addresses / user intent) @@ - } catch (error) { - console.warn(`Failed to fetch orders from chain ${chainId}:`, error) + } catch (error) { + // TODO: logger.warn({ chainId, err: safeError(error) }, 'Failed to fetch orders from chain') // Continue with other chains } }Also applies to: 169-172
apps/agentic-chat/src/hooks/useCancelLimitOrderExecution.tsx (1)
41-79: Persist + rehydratecurrentStepandfailedStep(post-refresh step UI is currently wrong).function cancelOrderStateToPersistedState( @@ return { @@ meta: { ...(orderOutput?.orderId && { orderId: orderOutput.orderId }), ...(state.error && { error: state.error }), + ...(state.failedStep !== undefined && { failedStep: state.failedStep }), + ...(state.currentStep !== undefined && { currentStep: state.currentStep }), ...(networkName && { networkName }), }, @@ } function persistedStateToCancelOrderState(persisted: PersistedToolState): CancelOrderState { const completedSteps = new Set<CancelOrderStep>() - const currentStep = CancelOrderStep.COMPLETE + const currentStep = (persisted.meta.currentStep as CancelOrderStep | undefined) ?? CancelOrderStep.COMPLETE @@ return { currentStep, completedSteps, error: persisted.meta.error as string | undefined, + failedStep: persisted.meta.failedStep as CancelOrderStep | undefined, } }Also applies to: 81-103
apps/agentic-chat/src/hooks/useLimitOrderExecution.tsx (2)
49-95: Persist + rehydratecurrentStepandfailedStep(same step-status fidelity bug as before).return { @@ meta: { ...(state.orderId && { orderId: state.orderId }), ...(state.approvalTxHash && { approvalTxHash: state.approvalTxHash }), ...(state.error && { error: state.error }), + ...(state.failedStep !== undefined && { failedStep: state.failedStep }), + ...(state.currentStep !== undefined && { currentStep: state.currentStep }), ...(networkName && { networkName }), }, @@ function persistedStateToLimitOrderState(persisted: PersistedToolState): LimitOrderState { const completedSteps = new Set<LimitOrderStep>() - const currentStep = LimitOrderStep.COMPLETE + const currentStep = (persisted.meta.currentStep as LimitOrderStep | undefined) ?? LimitOrderStep.COMPLETE @@ return { currentStep, completedSteps, orderId: persisted.meta.orderId as string | undefined, approvalTxHash: persisted.meta.approvalTxHash as string | undefined, error: persisted.meta.error as string | undefined, + failedStep: persisted.meta.failedStep as LimitOrderStep | undefined, } }Also applies to: 96-126
282-298: Handle missingpublicClientexplicitly (don’t silently skip receipt confirmation).const publicClient = getPublicClient(wagmiConfig, { chainId: orderParams.chainId, }) - if (publicClient) { - await publicClient.waitForTransactionReceipt({ - hash: approvalTxHash as `0x${string}`, - confirmations: 1, - }) - } + if (!publicClient) { + throw new Error(`No public client for chain ${orderParams.chainId}; cannot confirm approval transaction`) + } + await publicClient.waitForTransactionReceipt({ + hash: approvalTxHash as `0x${string}`, + confirmations: 1, + })
🧹 Nitpick comments (7)
apps/agentic-chat/src/lib/cow-config.ts (1)
2-6: Consider tightening types to prevent accidental unsupported chain calls.
If the supported set is intentionally small, you can make it harder to callgetCowApiUrl()with arbitrary numbers by using anas constmap + derived union type (and/or anisSupportedCowChainId()type guard).Also applies to: 8-14
apps/agentic-chat/src/components/tools/GetLimitOrdersUI.tsx (1)
102-107:filledPercentdisplay logic may be misleading for fulfilled orders.For fulfilled orders (
status === 'fulfilled'), showing "X% filled" when it's 100% filled is redundant. Consider showing this only for partially filled orders or changing the label.- {isFilled && filledPercent > 0 && ( + {isFilled && filledPercent > 0 && filledPercent < 100 && ( <> <span>•</span> - <span>{filledPercent}% filled</span> + <span>{filledPercent}% partially filled</span> </> )}apps/agentic-chat/src/components/Portfolio/ActivityRow/index.tsx (2)
20-24: Harden icon mapping against futureActivityItem['type']additions (avoidIconbeingundefined).
Today it’s safe via the union, but this will become a runtime crash if a new type ships before this file is updated.-const ACTIVITY_ICONS = { +const ACTIVITY_ICONS = { swap: ArrowRightLeft, send: Send, limit_order: Timer, -} +} satisfies Record<ActivityItem['type'], React.ComponentType<{ className?: string }>>Also applies to: 67-74
26-35: MakeformatActivityTitle()exhaustiveness explicit (prevents accidentalundefinedtitles).
Right now it relies on the union being perfectly in sync. An explicit guard makes failures loud during dev.function formatActivityTitle(activity: ActivityItem): string { switch (activity.type) { case 'swap': return `Swapped ${formatCryptoAmount(activity.details.sellAsset.amount, { symbol: activity.details.sellAsset.symbol })} to ${formatCryptoAmount(activity.details.buyAsset.amount, { symbol: activity.details.buyAsset.symbol })}` case 'send': return `Sent ${formatCryptoAmount(activity.details.asset.amount, { symbol: activity.details.asset.symbol })}` case 'limit_order': return `Limit order: ${formatCryptoAmount(activity.details.sellAsset.amount, { symbol: activity.details.sellAsset.symbol })} → ${formatCryptoAmount(activity.details.buyAsset.estimatedAmount, { symbol: activity.details.buyAsset.symbol })}` } + // If `ActivityItem` expands, fail loudly instead of rendering an empty title. + // eslint-disable-next-line @typescript-eslint/restrict-template-expressions + throw new Error(`Unhandled activity type: ${(activity as any).type}`) }apps/agentic-chat/src/components/tools/CancelLimitOrderUI.tsx (2)
50-54: Ensureerroris always a string before interpolating (avoidCancellation failed: [object Object]).
If the hook ever returns a non-string, this UI will degrade.
-->- if (error) return { type: 'error' as const, text: `Cancellation failed: ${error}` } + if (error) return { type: 'error' as const, text: `Cancellation failed: ${String(error)}` }
66-69: Optional: use a success icon when complete (XCircle-in-green reads odd).
ConsiderCheckCirclefor complete and keepXCirclefor error/failed states.apps/agentic-server/src/routes/chat.ts (1)
164-187: Optional: reduce repetition with awrapWalletToolWithLogging()helper.
The three limit-order tool blocks are mechanically identical to other walletContext wrappers here.+function wrapWalletToolWithLogging<T extends { description: string; inputSchema: unknown }>( + name: string, + tool: T & { execute: (args: any, walletContext: WalletContext) => unknown }, + walletContext: WalletContext +) { + return { + description: tool.description, + inputSchema: tool.inputSchema, + execute: async (args: Parameters<typeof tool.execute>[0]) => { + console.log(`[Tool] ${name}:`, JSON.stringify(args, null, 2)) + return tool.execute(args, walletContext) + }, + } +}(Then use it for
createLimitOrderTool,getLimitOrdersTool,cancelLimitOrderTool.)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (27)
apps/agentic-chat/src/components/Portfolio/ActivityList.tsx(2 hunks)apps/agentic-chat/src/components/Portfolio/ActivityRow.tsx(0 hunks)apps/agentic-chat/src/components/Portfolio/ActivityRow/components/LimitOrderDetails.tsx(1 hunks)apps/agentic-chat/src/components/Portfolio/ActivityRow/components/SendDetails.tsx(1 hunks)apps/agentic-chat/src/components/Portfolio/ActivityRow/components/SwapDetails.tsx(1 hunks)apps/agentic-chat/src/components/Portfolio/ActivityRow/index.tsx(1 hunks)apps/agentic-chat/src/components/toolUIRegistry.tsx(2 hunks)apps/agentic-chat/src/components/tools/CancelLimitOrderUI.tsx(1 hunks)apps/agentic-chat/src/components/tools/GetLimitOrdersUI.tsx(1 hunks)apps/agentic-chat/src/components/tools/LimitOrderUI.tsx(1 hunks)apps/agentic-chat/src/hooks/useCancelLimitOrderExecution.tsx(1 hunks)apps/agentic-chat/src/hooks/useLimitOrderExecution.tsx(1 hunks)apps/agentic-chat/src/hooks/useToolExecutionEffect.ts(1 hunks)apps/agentic-chat/src/lib/activityNormalizer.ts(3 hunks)apps/agentic-chat/src/lib/cow-config.ts(1 hunks)apps/agentic-chat/src/lib/eventHandlers.ts(1 hunks)apps/agentic-chat/src/lib/stepUtils.ts(1 hunks)apps/agentic-chat/src/stores/chatStore.ts(2 hunks)apps/agentic-chat/src/types/activity.ts(2 hunks)apps/agentic-server/src/index.ts(1 hunks)apps/agentic-server/src/lib/cow/index.ts(1 hunks)apps/agentic-server/src/lib/cow/types.ts(1 hunks)apps/agentic-server/src/routes/chat.ts(4 hunks)apps/agentic-server/src/tools/limitOrder/cancelLimitOrder.ts(1 hunks)apps/agentic-server/src/tools/limitOrder/createLimitOrder.ts(1 hunks)apps/agentic-server/src/tools/limitOrder/getLimitOrders.ts(1 hunks)apps/agentic-server/src/tools/limitOrder/index.ts(1 hunks)
💤 Files with no reviewable changes (1)
- apps/agentic-chat/src/components/Portfolio/ActivityRow.tsx
🚧 Files skipped from review as they are similar to previous changes (11)
- apps/agentic-chat/src/hooks/useToolExecutionEffect.ts
- apps/agentic-chat/src/stores/chatStore.ts
- apps/agentic-chat/src/components/toolUIRegistry.tsx
- apps/agentic-server/src/index.ts
- apps/agentic-chat/src/types/activity.ts
- apps/agentic-chat/src/components/Portfolio/ActivityList.tsx
- apps/agentic-server/src/lib/cow/types.ts
- apps/agentic-chat/src/components/Portfolio/ActivityRow/components/SwapDetails.tsx
- apps/agentic-chat/src/components/Portfolio/ActivityRow/components/SendDetails.tsx
- apps/agentic-chat/src/components/Portfolio/ActivityRow/components/LimitOrderDetails.tsx
- apps/agentic-server/src/lib/cow/index.ts
🧰 Additional context used
🧬 Code graph analysis (11)
apps/agentic-chat/src/lib/cow-config.ts (1)
apps/agentic-server/src/lib/cow/types.ts (1)
getCowApiUrl(104-117)
apps/agentic-chat/src/lib/activityNormalizer.ts (5)
apps/agentic-chat/src/stores/chatStore.ts (1)
PersistedToolState(22-30)apps/agentic-chat/src/types/activity.ts (2)
ActivityItem(70-70)LimitOrderActivityDetails(39-52)apps/agentic-server/src/index.ts (1)
CreateLimitOrderOutput(95-95)apps/agentic-server/src/tools/limitOrder/createLimitOrder.ts (1)
CreateLimitOrderOutput(90-106)apps/agentic-server/src/tools/limitOrder/index.ts (1)
CreateLimitOrderOutput(6-6)
apps/agentic-chat/src/components/tools/GetLimitOrdersUI.tsx (8)
apps/agentic-chat/src/lib/utils.ts (1)
cn(5-7)apps/agentic-chat/src/components/ui/Amount.tsx (1)
Amount(88-92)apps/agentic-chat/src/lib/eventHandlers.ts (1)
stopPropagationHandler(4-6)apps/agentic-chat/src/components/tools/toolUIHelpers.tsx (2)
ToolUIComponentProps(6-8)useToolStateRender(10-31)apps/agentic-server/src/index.ts (1)
GetLimitOrdersOutput(102-102)apps/agentic-server/src/tools/limitOrder/getLimitOrders.ts (1)
GetLimitOrdersOutput(54-57)apps/agentic-server/src/tools/limitOrder/index.ts (1)
GetLimitOrdersOutput(15-15)apps/agentic-chat/src/components/ui/ToolCard.tsx (1)
ToolCard(71-78)
apps/agentic-server/src/tools/limitOrder/createLimitOrder.ts (9)
apps/agentic-server/src/lib/schemas/swapSchemas.ts (2)
TransactionData(75-75)assetInputSchema(15-21)packages/caip/src/assetId.ts (1)
fromAssetId(87-106)apps/agentic-server/src/utils/transactionHelpers.ts (1)
createTransaction(17-33)apps/agentic-server/src/tools/limitOrder/index.ts (1)
createLimitOrderTool(2-2)apps/agentic-server/src/lib/cow/types.ts (3)
CowOrderSigningData(48-53)isCowSupportedChain(96-98)getCowExplorerUrl(100-102)apps/agentic-server/src/utils/walletContextSimple.ts (2)
WalletContext(4-6)getAddressForChain(13-26)apps/agentic-server/src/utils/assetHelpers.ts (1)
resolveAsset(8-25)apps/agentic-server/src/lib/cow/index.ts (2)
COW_VAULT_RELAYER_ADDRESS(30-30)prepareCowLimitOrder(70-114)apps/agentic-server/src/utils/getAllowance.ts (1)
getAllowance(24-57)
apps/agentic-chat/src/components/Portfolio/ActivityRow/index.tsx (9)
apps/agentic-chat/src/types/activity.ts (1)
ActivityItem(70-70)apps/agentic-chat/src/lib/explorers.ts (1)
getExplorerUrl(1-15)apps/agentic-chat/src/components/ui/ToolCard.tsx (1)
ToolCard(71-78)apps/agentic-chat/src/lib/eventHandlers.ts (1)
stopPropagationHandler(4-6)apps/agentic-chat/src/lib/utils.ts (1)
truncateAddress(9-12)apps/agentic-chat/src/components/Portfolio/ActivityRow/components/SwapDetails.tsx (1)
SwapDetails(13-66)apps/agentic-chat/src/components/Portfolio/ActivityRow/components/SendDetails.tsx (1)
SendDetails(10-24)apps/agentic-chat/src/components/Portfolio/ActivityRow/components/LimitOrderDetails.tsx (1)
LimitOrderDetails(10-43)apps/agentic-chat/src/components/ui/DrawerListItem.tsx (1)
DrawerListItem(13-41)
apps/agentic-chat/src/lib/stepUtils.ts (1)
apps/agentic-chat/src/hooks/useWalletConnection.ts (1)
useWalletConnection(23-48)
apps/agentic-server/src/routes/chat.ts (5)
apps/agentic-server/src/index.ts (3)
createLimitOrderTool(92-92)getLimitOrdersTool(99-99)cancelLimitOrderTool(105-105)apps/agentic-server/src/tools/limitOrder/createLimitOrder.ts (1)
createLimitOrderTool(277-299)apps/agentic-server/src/tools/limitOrder/index.ts (3)
createLimitOrderTool(2-2)getLimitOrdersTool(11-11)cancelLimitOrderTool(19-19)apps/agentic-server/src/tools/limitOrder/getLimitOrders.ts (1)
getLimitOrdersTool(184-205)apps/agentic-server/src/tools/limitOrder/cancelLimitOrder.ts (1)
cancelLimitOrderTool(111-130)
apps/agentic-server/src/tools/limitOrder/getLimitOrders.ts (4)
apps/agentic-server/src/lib/cow/types.ts (6)
CowOrderStatus(5-5)CHAIN_ID_TO_NETWORK(90-94)CowOrder(7-22)NETWORK_TO_CHAIN_ID(83-87)COW_SUPPORTED_CHAINS(75-80)getCowExplorerUrl(100-102)apps/agentic-server/src/utils/walletContextSimple.ts (2)
WalletContext(4-6)getAddressForChain(13-26)apps/agentic-server/src/lib/cow/index.ts (1)
getCowOrders(168-188)packages/utils/src/index.ts (1)
fromBaseUnit(38-41)
apps/agentic-chat/src/components/tools/LimitOrderUI.tsx (9)
apps/agentic-chat/src/components/tools/toolUIHelpers.tsx (1)
ToolUIComponentProps(6-8)apps/agentic-server/src/index.ts (1)
CreateLimitOrderOutput(95-95)apps/agentic-server/src/tools/limitOrder/createLimitOrder.ts (1)
CreateLimitOrderOutput(90-106)apps/agentic-server/src/tools/limitOrder/index.ts (1)
CreateLimitOrderOutput(6-6)apps/agentic-chat/src/hooks/useLimitOrderExecution.tsx (1)
useLimitOrderExecution(185-390)apps/agentic-chat/src/components/ui/TxStepCard.tsx (1)
TxStepCard(149-162)apps/agentic-chat/src/components/ui/Skeleton.tsx (1)
Skeleton(7-7)apps/agentic-chat/src/components/ui/Amount.tsx (1)
Amount(88-92)apps/agentic-chat/src/components/ui/TruncateText.tsx (1)
TruncateText(9-26)
apps/agentic-server/src/tools/limitOrder/cancelLimitOrder.ts (3)
apps/agentic-server/src/lib/cow/types.ts (4)
CowEIP712Domain(37-42)NETWORK_TO_CHAIN_ID(83-87)CHAIN_ID_TO_NETWORK(90-94)getCowExplorerUrl(100-102)apps/agentic-server/src/lib/cow/index.ts (2)
getCowOrder(193-208)prepareCowOrderCancellation(214-231)apps/agentic-server/src/utils/walletContextSimple.ts (2)
WalletContext(4-6)getAddressForChain(13-26)
apps/agentic-chat/src/hooks/useCancelLimitOrderExecution.tsx (6)
apps/agentic-server/src/tools/limitOrder/cancelLimitOrder.ts (1)
CancelLimitOrderOutput(26-32)apps/agentic-chat/src/stores/chatStore.ts (1)
PersistedToolState(22-30)apps/agentic-chat/src/lib/cow-config.ts (1)
getCowApiUrl(8-14)apps/agentic-chat/src/hooks/useWalletConnection.ts (1)
useWalletConnection(23-48)apps/agentic-chat/src/providers/ChatProvider.tsx (1)
useChatContext(33-39)apps/agentic-chat/src/lib/stepUtils.ts (1)
signTypedDataWithWallet(32-48)
⏰ 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: main
🔇 Additional comments (20)
apps/agentic-chat/src/lib/stepUtils.ts (1)
32-48: LGTM!The type assertions are well-documented, and the approach pragmatically handles the type mismatch between CoW Protocol's API format and viem's expectations.
apps/agentic-server/src/tools/limitOrder/createLimitOrder.ts (3)
27-52: LGTM!The approval transaction builder correctly handles the ERC20 approve encoding and chain validation.
54-68: LGTM!The schema has sensible defaults (168 hours = 7 days) and clear descriptions for the AI to understand limit price semantics.
277-299: LGTM!The tool definition has clear documentation including the important note about using the maths tool to calculate limitPrice from total amounts.
apps/agentic-chat/src/lib/eventHandlers.ts (1)
1-6: LGTM!Simple, focused utility that prevents click propagation. The React
MouseEventtyping is correct.apps/agentic-chat/src/lib/activityNormalizer.ts (3)
1-8: LGTM!The imports are correctly updated to include the new limit order types.
18-19: LGTM!The switch case correctly routes limit orders to the new normalization function.
92-122: LGTM overall structure.The normalization function follows the established pattern for swap and send activities. The null checks and data mapping are consistent with other normalizers.
apps/agentic-chat/src/components/tools/GetLimitOrdersUI.tsx (4)
15-27: LGTM!The status configuration covers all CoW Protocol order states with appropriate icons and colors. The type guard
isValidOrderStatusprovides safe runtime validation.
29-39: LGTM!Clean component with fallback to 'open' status config if lookup fails.
67-76: Good use of BigNumber for rate calculation.The rate computation correctly uses BigNumber to avoid floating-point precision issues, and handles division by zero gracefully.
126-155: LGTM!The component properly handles loading, error, and empty states using the shared
useToolStateRenderhook. The empty state UI is clean and informative.apps/agentic-chat/src/components/tools/CancelLimitOrderUI.tsx (1)
13-25: Good step orchestration + historical-skipped handling.
The early-return paths make this resilient to missing persisted state and partial hydration.apps/agentic-server/src/tools/limitOrder/index.ts (1)
1-24: Clean, discoverable public surface for limit-order tooling.
Nice consolidation for downstream imports.apps/agentic-server/src/routes/chat.ts (1)
28-29: Wallet-context passthrough for limit-order tools looks consistent with the other wallet tools.
Good: context is passed server-side (vs relying on model-provided addresses).Also applies to: 164-187
apps/agentic-chat/src/components/Portfolio/ActivityRow/index.tsx (1)
26-35: The code is correct as-is.activity.timestampis properly typed asnumber(line 3 oftypes/activity.ts), which is an accepted parameter type forformatDistanceToNow. No parsing is required.apps/agentic-server/src/tools/limitOrder/cancelLimitOrder.ts (1)
83-93: Cancellability + signing payload assembly looks correct.
Status gating is clear andprepareCowOrderCancellation()is wired through withprimaryType.Also applies to: 94-109
apps/agentic-chat/src/hooks/useCancelLimitOrderExecution.tsx (2)
118-137: Confirm CoW cancellation endpoint contract (DELETE with JSON body +signingScheme).
If CoW changes/varies by chain, this will fail at runtime.
195-225: Step transitions + error capture look solid.
failedStep = currentSteppaired with toast + persistence is a good UX pattern (once hydration is fixed).Also applies to: 237-253
apps/agentic-chat/src/hooks/useLimitOrderExecution.tsx (1)
142-183: CoW order submission payload: verify required fields + response parsing.
await response.text()+ stripping quotes works, but considerJSON.parseif/when CoW returns JSON consistently.
7536eb0 to
9a40ec3
Compare
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 (19)
apps/agentic-chat/src/lib/stepUtils.ts (1)
14-20: Step status logic incorrectly reports completed current step as IN_PROGRESS.When
currentStep === stepand the step exists incompletedSteps, Line 17 returnsIN_PROGRESSbefore Line 18 can check completion status. Move thecompletedSteps.has(step)check before thecurrentStep === stepcheck.Apply this diff:
export function getStepStatus<TStep extends number>(step: TStep, state: StepState<TStep>): StepStatus { if (state.failedStep === step) return StepStatus.FAILED + if (state.completedSteps.has(step)) return StepStatus.COMPLETE if (state.currentStep < step) return StepStatus.NOT_STARTED if (state.currentStep === step && !state.error) return StepStatus.IN_PROGRESS - if (state.completedSteps.has(step)) return StepStatus.COMPLETE return StepStatus.SKIPPED }apps/agentic-chat/src/lib/cow-config.ts (1)
2-14: Client/server CoW API URL logic is inconsistent.The client throws on unsupported chains and omits Sepolia (chain ID
11155111), but the server (atapps/agentic-server/src/lib/cow/types.tslines 103-116) includes Sepolia and defaults unknown chains to mainnet. This causes:
- Client-side limit orders fail hard on Sepolia or future chains
- Different error behavior between client and server
Align with server behavior:
export const COW_API_URLS: Record<number, string> = { + 11155111: 'https://api.cow.fi/sepolia', } export function getCowApiUrl(chainId: number): string { - const apiUrl = COW_API_URLS[chainId] - if (!apiUrl) { - throw new Error(`Unsupported chain for CoW Protocol: ${chainId}`) - } - return apiUrl + return COW_API_URLS[chainId] ?? COW_API_URLS[1] }apps/agentic-chat/src/components/tools/LimitOrderUI.tsx (1)
50-58:completedCountdisagrees with rendered step statuses when approval is skipped.Lines 50-57 compute
completedCountfrom raw step statuses, but Lines 148-157 render approval steps with conditional statuses (needsApproval ? status : StepStatus.SKIPPED). This causes the progress indicator to show incorrect completion counts when approval is not needed.Compute
completedCountusing effective statuses:+ const effectiveApprovalStatus = needsApproval ? approvalStep.status : StepStatus.SKIPPED + const effectiveApprovalConfirmStatus = needsApproval ? approvalConfirmStep.status : StepStatus.SKIPPED const completedCount = [ prepareStep.status, networkStep.status, - approvalStep.status, - approvalConfirmStep.status, + effectiveApprovalStatus, + effectiveApprovalConfirmStatus, signStep.status, submitStep.status, ].filter(s => s === StepStatus.COMPLETE || s === StepStatus.SKIPPED).lengthapps/agentic-server/src/tools/limitOrder/getLimitOrders.ts (3)
124-185: Consider parallel per-chain fetch withPromise.allSettled.This issue was previously flagged and remains unaddressed. Sequential chain calls increase worst-case latency.
Parallelize:
- const allOrders: OrderInfo[] = [] - for (const chainId of chainsToQuery) { - // ... fetch and process - } + const chainResults = await Promise.allSettled( + chainsToQuery.map(async chainId => { + const chainIdString = `eip155:${chainId}` + let userAddress: string + try { + userAddress = getAddressForChain(walletContext, chainIdString) + } catch { + return [] + } + try { + const orders = await getCowOrders(userAddress, chainId, { limit: 50 }) + // ... process and return OrderInfo[] + } catch (error) { + console.warn(`Failed to fetch orders from chain ${chainId}:`, error) + return [] + } + }) + ) + const allOrders = chainResults.flatMap(r => r.status === 'fulfilled' ? r.value : [])
82-87: Remove thepresignaturePending→openmapping; preserve distinct order states.This issue was previously flagged and remains unaddressed. CoW Protocol distinguishes between
presignaturePending(awaiting on-chain pre-signature) andopen(active in orderbook). Collapsing them loses important state information.Either remove the mapping or document why it's intentional:
function formatOrderStatus(status: CowOrderStatus): CowOrderStatus { - // Map CoW status to our simplified status - if (status === 'fulfilled') return 'fulfilled' - if (status === 'presignaturePending') return 'open' - return status + // Return status as-is; UI and filters should handle all CoW statuses + return status }Then add
'presignaturePending'to any filter schemas that currently exclude it.
101-101: Remove raw input logging.This issue was previously flagged and remains unaddressed. Logging full input can leak user intent/filters.
- console.log('[getLimitOrders]:', JSON.stringify(input, null, 2)) + // Use structured logger with redaction if neededapps/agentic-server/src/tools/limitOrder/createLimitOrder.ts (5)
108-120: Blocker:parseFloatloses precision for limit order price math.This issue was previously flagged and remains unaddressed. Float arithmetic will silently misprice orders, especially for tokens with high precision or small fractional prices.
Apply this diff to use
BigNumberthroughout:function calculateBuyAmount(buyAsset: Asset, sellAmount: string, limitPrice: string): string { - // limitPrice is buyAsset per sellAsset - // buyAmount = sellAmount * limitPrice - const sellAmountNum = parseFloat(sellAmount) - const limitPriceNum = parseFloat(limitPrice) - - if (isNaN(sellAmountNum) || isNaN(limitPriceNum)) { - throw new Error('Invalid sellAmount or limitPrice') - } - - const buyAmountNum = sellAmountNum * limitPriceNum - return toBaseUnit(buyAmountNum.toString(), buyAsset.precision) + const bnSell = new BigNumber(sellAmount) + const bnPrice = new BigNumber(limitPrice) + if (!bnSell.isFinite() || !bnPrice.isFinite() || bnSell.lte(0) || bnPrice.lte(0)) { + throw new Error('Invalid sellAmount or limitPrice') + } + const buyAmountHuman = bnSell.times(bnPrice) + return toBaseUnit(buyAmountHuman.toFixed(buyAsset.precision), buyAsset.precision) }
122-129:parseIntmay returnNaNwithout validation.This issue was previously flagged and remains unaddressed. If
parts[1]contains invalid characters,NaNpropagates silently.Apply this diff:
function getEvmChainId(chainId: string): number { const parts = chainId.split(':') if (parts.length !== 2 || !parts[1]) { throw new Error(`Invalid chainId format: ${chainId}`) } - return parseInt(parts[1], 10) + const numericId = parseInt(parts[1], 10) + if (isNaN(numericId)) { + throw new Error(`Invalid numeric chainId: ${chainId}`) + } + return numericId }
135-136: Remove raw input logging.This issue was previously flagged and remains unaddressed. Logging full input can leak sensitive user data (amounts, assets, wallet intent).
Remove or replace with a structured logger that redacts sensitive fields:
- console.log('[createLimitOrder]:', JSON.stringify(input, null, 2)) + // Use structured logger with redaction if needed
237-238: Inconsistent precision:estimatedBuyAmountusesBigNumberbutcalculateBuyAmountusesparseFloat.This issue was previously flagged and remains unaddressed. The display value (line 238) and actual order amount (line 209) may differ due to float rounding in
calculateBuyAmount.Once
calculateBuyAmountis fixed to useBigNumber, this inconsistency will be resolved.
273-273: PlaceholdertrackingUrlmay confuse users.This issue was previously flagged and remains unaddressed. Returning
getCowExplorerUrl('pending')leads users to a non-existent order page.Consider making
trackingUrloptional or returning a generic explorer URL without an order ID:- trackingUrl: getCowExplorerUrl('pending'), + trackingUrl: undefined, // or 'https://explorer.cow.fi'Update the
CreateLimitOrderOutputtype to maketrackingUrloptional if you chooseundefined.apps/agentic-server/src/lib/cow/types.ts (2)
74-94: Incomplete sepolia support + unsafe mainnet fallback.This issue was previously flagged and remains unaddressed. Sepolia exists in
COW_SUPPORTED_CHAINSand has API routing, but is absent fromNETWORK_TO_CHAIN_IDandCHAIN_ID_TO_NETWORK, causing "unknown" network labels and potential misrouting.Either fully support sepolia or remove it:
export const NETWORK_TO_CHAIN_ID: Record<string, number> = { ethereum: 1, gnosis: 100, arbitrum: 42161, + sepolia: 11155111, } export const CHAIN_ID_TO_NETWORK: Record<number, string> = { + 11155111: 'sepolia', }Also update tool schemas to include
'sepolia'in network enums and remove any explicit filters excluding11155111.
104-117:getCowApiUrlsilently defaults to mainnet for unknown chains.This issue was previously flagged and remains unaddressed. Orders could be submitted to the wrong network.
Replace the default fallback with an error:
export function getCowApiUrl(chainId: number): string { switch (chainId) { case 1: return 'https://api.cow.fi/mainnet' case 100: return 'https://api.cow.fi/gnosis' case 11155111: return 'https://api.cow.fi/sepolia' case 42161: return 'https://api.cow.fi/arbitrum_one' default: - return 'https://api.cow.fi/mainnet' + throw new Error(`Unsupported chain ID for CoW Protocol: ${chainId}`) } }apps/agentic-chat/src/hooks/useLimitOrderExecution.tsx (2)
285-295: Handle missingpublicClientexplicitly.This issue was previously flagged and remains unaddressed. When
publicClientis null, the code silently skips waiting for the transaction receipt, which could cause race conditions.Consider logging a warning:
if (needsApproval && approvalTxHash) { const publicClient = getPublicClient(wagmiConfig, { chainId: orderParams.chainId, }) - if (publicClient) { - await publicClient.waitForTransactionReceipt({ - hash: approvalTxHash as `0x${string}`, - confirmations: 1, - }) + if (!publicClient) { + console.warn(`[useLimitOrderExecution] No public client for chain ${orderParams.chainId}, skipping receipt confirmation`) + } else { + await publicClient.waitForTransactionReceipt({ + hash: approvalTxHash as `0x${string}`, + confirmations: 1, + }) }
49-128: Persist/rehydrate losesfailedStepandcurrentStep, breaking step-status fidelity after refresh.This issue was previously flagged and remains unaddressed. The UI cannot reliably show which step failed after a page refresh.
Apply the same fix pattern as suggested for the cancel hook—persist and restore both fields:
function limitOrderStateToPersistedState( // ... ): PersistedToolState { // ... return { // ... meta: { ...(state.orderId && { orderId: state.orderId }), ...(state.approvalTxHash && { approvalTxHash: state.approvalTxHash }), ...(state.error && { error: state.error }), + ...(state.failedStep !== undefined && { failedStep: state.failedStep }), + ...(state.currentStep !== undefined && { currentStep: state.currentStep }), ...(networkName && { networkName }), }, // ... } } function persistedStateToLimitOrderState(persisted: PersistedToolState): LimitOrderState { const completedSteps = new Set<LimitOrderStep>() - const currentStep = LimitOrderStep.COMPLETE + const currentStep = (persisted.meta.currentStep as LimitOrderStep | undefined) ?? LimitOrderStep.COMPLETE // ... (rebuild completedSteps) return { currentStep, completedSteps, orderId: persisted.meta.orderId as string | undefined, approvalTxHash: persisted.meta.approvalTxHash as string | undefined, error: persisted.meta.error as string | undefined, + failedStep: persisted.meta.failedStep as LimitOrderStep | undefined, } }apps/agentic-server/src/tools/limitOrder/cancelLimitOrder.ts (2)
34-48: Differentiate 404 (not found) from transient errors in chain detection.This issue was previously flagged and remains unaddressed. All errors are treated as "order not on this chain," masking real API failures.
Enhance
getCowOrderto expose HTTP status and handle errors appropriately:// In getCowOrder (lib/cow/index.ts): if (!response.ok) { if (response.status === 404) { throw new NotFoundError(`Order ${orderId} not found on chain ${chainId}`) } const errorText = await response.text() throw new Error(`Failed to fetch order from CoW: ${errorText}`) } // Then in detectChainIdFromOrderId: for (const chainId of chainsToTry) { try { const order = await getCowOrder(orderId, chainId) if (order) return chainId } catch (error) { if (error instanceof NotFoundError) continue // Try next chain throw error // Propagate transient failures } }
54-54: Remove raw input logging and avoid echoingorder.ownerin error messages.This issue was previously flagged and remains unaddressed. Logging and error messages can leak sensitive data.
Apply this diff:
- console.log('[cancelLimitOrder]:', JSON.stringify(input, null, 2)) + // Use structured logger with redaction if needed const order = await getCowOrder(input.orderId, chainId) if (order.owner.toLowerCase() !== userAddress.toLowerCase()) { - throw new Error(`You do not own this order. Order owner: ${order.owner}`) + throw new Error(`You do not own this order. Please verify the order ID and connected wallet.`) }Also applies to: 79-81
apps/agentic-chat/src/hooks/useCancelLimitOrderExecution.tsx (1)
41-103: Persist/rehydrate losesfailedStepandcurrentStep, breaking step-status fidelity after refresh.This issue was previously flagged and remains unaddressed. The UI cannot reliably show which step failed after a page refresh.
Apply the same fix pattern:
function cancelOrderStateToPersistedState( // ... ): PersistedToolState { // ... return { // ... meta: { ...(orderOutput?.orderId && { orderId: orderOutput.orderId }), ...(state.error && { error: state.error }), + ...(state.failedStep !== undefined && { failedStep: state.failedStep }), + ...(state.currentStep !== undefined && { currentStep: state.currentStep }), ...(networkName && { networkName }), }, // ... } } function persistedStateToCancelOrderState(persisted: PersistedToolState): CancelOrderState { const completedSteps = new Set<CancelOrderStep>() - const currentStep = CancelOrderStep.COMPLETE + const currentStep = (persisted.meta.currentStep as CancelOrderStep | undefined) ?? CancelOrderStep.COMPLETE // ... (rebuild completedSteps) return { currentStep, completedSteps, error: persisted.meta.error as string | undefined, + failedStep: persisted.meta.failedStep as CancelOrderStep | undefined, } }apps/agentic-server/src/lib/cow/index.ts (1)
121-163:fromfield should be the order signer, not receiver.This issue was previously flagged and remains unaddressed. The
fromfield represents the order owner/signer, but usingorder.receiveris incorrect when the receiver differs from the signer.Update the function signature and usage:
export async function submitCowOrder( chainId: number, order: CowOrderQuote, signature: string, - signingScheme: 'eip712' | 'ethsign' = 'eip712' + signingScheme: 'eip712' | 'ethsign' = 'eip712', + from: string ): Promise<string> { // ... const orderPayload = { // ... - from: order.receiver, // owner of the order + from, // owner/signer of the order }Update all callers to pass the actual signer address.
🧹 Nitpick comments (3)
apps/agentic-chat/src/components/Portfolio/ActivityRow/components/SwapDetails.tsx (1)
24-28: Remove unnecessary fragment wrappers.The fragment wrappers around the
Amount.Fiatcomponents in the suffix props serve no purpose since they only contain a single child.Apply this diff to simplify the code:
suffix={ - <> - (<Amount.Fiat value={details.sellAsset.valueUSD} />) - </> + <>(<Amount.Fiat value={details.sellAsset.valueUSD} />)</> }And similarly for the buyAsset:
suffix={ - <> - (<Amount.Fiat value={details.buyAsset.valueUSD} />) - </> + <>(<Amount.Fiat value={details.buyAsset.valueUSD} />)</> }Also applies to: 38-42
apps/agentic-chat/src/components/Portfolio/ActivityRow/components/LimitOrderDetails.tsx (1)
3-3: Fix event typing forstopPropagationHandler(React event ≠ DOMMouseEvent)
onClickreceives a React synthetic mouse event; the shared helper is typed as DOMMouseEvent, which is brittle and can start failing as TS settings/types evolve.Minimal local fix:
+import type React from 'react' ... - onClick={stopPropagationHandler} + onClick={(e: React.MouseEvent<HTMLAnchorElement>) => e.stopPropagation()}Better global fix: retarget
apps/agentic-chat/src/lib/eventHandlers.tsto acceptReact.SyntheticEvent(or a small structural type withstopPropagation(): void) and keep callsites clean.Also applies to: 35-35
apps/agentic-chat/src/lib/stepUtils.ts (1)
40-45: Type assertions bypass viem's type safety.The type assertions at Lines 40-44 force compatibility between CoW Protocol's API format and viem's
signTypedDataexpectations, but they bypass compile-time type checking. If CoW's format or viem's signature changes, runtime errors could occur.Consider adding runtime validation or helper functions that safely transform CoW data to viem's expected structure, documenting the specific format differences you're bridging.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (27)
apps/agentic-chat/src/components/Portfolio/ActivityList.tsx(2 hunks)apps/agentic-chat/src/components/Portfolio/ActivityRow.tsx(0 hunks)apps/agentic-chat/src/components/Portfolio/ActivityRow/components/LimitOrderDetails.tsx(1 hunks)apps/agentic-chat/src/components/Portfolio/ActivityRow/components/SendDetails.tsx(1 hunks)apps/agentic-chat/src/components/Portfolio/ActivityRow/components/SwapDetails.tsx(1 hunks)apps/agentic-chat/src/components/Portfolio/ActivityRow/index.tsx(1 hunks)apps/agentic-chat/src/components/toolUIRegistry.tsx(2 hunks)apps/agentic-chat/src/components/tools/CancelLimitOrderUI.tsx(1 hunks)apps/agentic-chat/src/components/tools/GetLimitOrdersUI.tsx(1 hunks)apps/agentic-chat/src/components/tools/LimitOrderUI.tsx(1 hunks)apps/agentic-chat/src/hooks/useCancelLimitOrderExecution.tsx(1 hunks)apps/agentic-chat/src/hooks/useLimitOrderExecution.tsx(1 hunks)apps/agentic-chat/src/hooks/useToolExecutionEffect.ts(1 hunks)apps/agentic-chat/src/lib/activityNormalizer.ts(3 hunks)apps/agentic-chat/src/lib/cow-config.ts(1 hunks)apps/agentic-chat/src/lib/eventHandlers.ts(1 hunks)apps/agentic-chat/src/lib/stepUtils.ts(1 hunks)apps/agentic-chat/src/stores/chatStore.ts(2 hunks)apps/agentic-chat/src/types/activity.ts(2 hunks)apps/agentic-server/src/index.ts(1 hunks)apps/agentic-server/src/lib/cow/index.ts(1 hunks)apps/agentic-server/src/lib/cow/types.ts(1 hunks)apps/agentic-server/src/routes/chat.ts(4 hunks)apps/agentic-server/src/tools/limitOrder/cancelLimitOrder.ts(1 hunks)apps/agentic-server/src/tools/limitOrder/createLimitOrder.ts(1 hunks)apps/agentic-server/src/tools/limitOrder/getLimitOrders.ts(1 hunks)apps/agentic-server/src/tools/limitOrder/index.ts(1 hunks)
💤 Files with no reviewable changes (1)
- apps/agentic-chat/src/components/Portfolio/ActivityRow.tsx
🚧 Files skipped from review as they are similar to previous changes (7)
- apps/agentic-chat/src/hooks/useToolExecutionEffect.ts
- apps/agentic-chat/src/lib/eventHandlers.ts
- apps/agentic-chat/src/types/activity.ts
- apps/agentic-chat/src/components/toolUIRegistry.tsx
- apps/agentic-server/src/index.ts
- apps/agentic-chat/src/components/Portfolio/ActivityRow/index.tsx
- apps/agentic-chat/src/lib/activityNormalizer.ts
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-22T20:32:10.539Z
Learnt from: kaladinlight
Repo: shapeshift/agentic-chat PR: 76
File: apps/agentic-server/src/mastra/tools/asset/coingecko/constants.ts:1-10
Timestamp: 2025-09-22T20:32:10.539Z
Learning: When cross-app dependencies are identified (like server code importing from chat packages), the user prefers to extract shared constants into dedicated packages in the packages/ directory following the existing pattern of shapeshiftoss/types and shapeshiftoss/utils.
Applied to files:
apps/agentic-chat/src/stores/chatStore.ts
🧬 Code graph analysis (11)
apps/agentic-chat/src/components/Portfolio/ActivityRow/components/SwapDetails.tsx (7)
apps/agentic-chat/src/types/activity.ts (1)
SwapActivityDetails(9-26)apps/agentic-chat/src/lib/explorers.ts (1)
getExplorerUrl(1-15)apps/agentic-chat/src/components/ui/ToolCard.tsx (1)
ToolCard(71-78)apps/agentic-chat/src/components/ui/Amount.tsx (1)
Amount(88-92)apps/agentic-chat/src/lib/eventHandlers.ts (1)
stopPropagationHandler(4-6)apps/agentic-chat/src/lib/utils.ts (1)
truncateAddress(9-12)apps/agentic-chat/src/components/Portfolio/ActivityRow.tsx (1)
SwapDetails(30-83)
apps/agentic-chat/src/components/Portfolio/ActivityRow/components/LimitOrderDetails.tsx (4)
apps/agentic-chat/src/types/activity.ts (1)
LimitOrderActivityDetails(39-52)apps/agentic-chat/src/components/ui/ToolCard.tsx (1)
ToolCard(71-78)apps/agentic-chat/src/components/ui/Amount.tsx (1)
Amount(88-92)apps/agentic-chat/src/lib/eventHandlers.ts (1)
stopPropagationHandler(4-6)
apps/agentic-server/src/tools/limitOrder/createLimitOrder.ts (9)
apps/agentic-server/src/utils/chains/helpers.ts (1)
isEvmChain(3-6)packages/caip/src/assetId.ts (1)
fromAssetId(87-106)apps/agentic-server/src/utils/transactionHelpers.ts (1)
createTransaction(17-33)apps/agentic-server/src/lib/cow/types.ts (3)
CowOrderSigningData(48-53)isCowSupportedChain(96-98)getCowExplorerUrl(100-102)packages/utils/src/index.ts (1)
toBaseUnit(43-46)apps/agentic-server/src/utils/walletContextSimple.ts (1)
WalletContext(4-6)apps/agentic-server/src/utils/assetHelpers.ts (1)
resolveAsset(8-25)apps/agentic-server/src/lib/cow/index.ts (2)
COW_VAULT_RELAYER_ADDRESS(30-30)prepareCowLimitOrder(70-114)apps/agentic-server/src/utils/getAllowance.ts (1)
getAllowance(24-57)
apps/agentic-server/src/routes/chat.ts (5)
apps/agentic-server/src/index.ts (3)
createLimitOrderTool(92-92)getLimitOrdersTool(99-99)cancelLimitOrderTool(105-105)apps/agentic-server/src/tools/limitOrder/createLimitOrder.ts (1)
createLimitOrderTool(277-299)apps/agentic-server/src/tools/limitOrder/index.ts (3)
createLimitOrderTool(2-2)getLimitOrdersTool(11-11)cancelLimitOrderTool(19-19)apps/agentic-server/src/tools/limitOrder/getLimitOrders.ts (1)
getLimitOrdersTool(196-221)apps/agentic-server/src/tools/limitOrder/cancelLimitOrder.ts (1)
cancelLimitOrderTool(111-130)
apps/agentic-chat/src/components/tools/LimitOrderUI.tsx (7)
apps/agentic-server/src/index.ts (1)
CreateLimitOrderOutput(95-95)apps/agentic-server/src/tools/limitOrder/createLimitOrder.ts (1)
CreateLimitOrderOutput(90-106)apps/agentic-server/src/tools/limitOrder/index.ts (1)
CreateLimitOrderOutput(6-6)apps/agentic-chat/src/hooks/useLimitOrderExecution.tsx (1)
useLimitOrderExecution(187-393)apps/agentic-chat/src/components/ui/TxStepCard.tsx (1)
TxStepCard(149-162)apps/agentic-chat/src/components/ui/Amount.tsx (1)
Amount(88-92)apps/agentic-chat/src/components/ui/TruncateText.tsx (1)
TruncateText(9-26)
apps/agentic-server/src/lib/cow/types.ts (1)
apps/agentic-chat/src/lib/cow-config.ts (1)
getCowApiUrl(8-14)
apps/agentic-server/src/tools/limitOrder/getLimitOrders.ts (4)
apps/agentic-server/src/lib/cow/types.ts (5)
CowOrderStatus(5-5)CHAIN_ID_TO_NETWORK(90-94)CowOrder(7-22)COW_SUPPORTED_CHAINS(75-80)getCowExplorerUrl(100-102)apps/agentic-server/src/utils/walletContextSimple.ts (2)
WalletContext(4-6)getAddressForChain(13-26)apps/agentic-server/src/lib/cow/index.ts (1)
getCowOrders(168-188)packages/utils/src/index.ts (1)
fromBaseUnit(38-41)
apps/agentic-chat/src/lib/cow-config.ts (1)
apps/agentic-server/src/lib/cow/types.ts (1)
getCowApiUrl(104-117)
apps/agentic-server/src/tools/limitOrder/cancelLimitOrder.ts (3)
apps/agentic-server/src/lib/cow/types.ts (4)
CowEIP712Domain(37-42)NETWORK_TO_CHAIN_ID(83-87)CHAIN_ID_TO_NETWORK(90-94)getCowExplorerUrl(100-102)apps/agentic-server/src/lib/cow/index.ts (2)
getCowOrder(193-208)prepareCowOrderCancellation(214-231)apps/agentic-server/src/utils/walletContextSimple.ts (2)
WalletContext(4-6)getAddressForChain(13-26)
apps/agentic-server/src/lib/cow/index.ts (2)
apps/agentic-server/src/lib/cow/types.ts (10)
CowEIP712Types(44-46)CowEIP712Domain(37-42)CreateCowOrderParams(55-64)CreateCowOrderResult(66-72)isCowSupportedChain(96-98)CowOrderQuote(24-35)CowOrderSigningData(48-53)getCowExplorerUrl(100-102)getCowApiUrl(104-117)CowOrder(7-22)apps/agentic-chat/src/lib/cow-config.ts (1)
getCowApiUrl(8-14)
apps/agentic-chat/src/lib/stepUtils.ts (1)
apps/agentic-chat/src/hooks/useWalletConnection.ts (1)
useWalletConnection(23-48)
⏰ 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: main
🔇 Additional comments (10)
apps/agentic-chat/src/components/Portfolio/ActivityRow/components/SwapDetails.tsx (2)
1-6: LGTM!All imports are correctly structured and necessary for the component implementation.
8-11: LGTM!Type definition is clean and appropriate for the component's requirements.
apps/agentic-chat/src/components/Portfolio/ActivityRow/components/LimitOrderDetails.tsx (1)
30-38: ValidatetrackingUrlscheme to prevent unexpected navigationWhile
rel="noopener noreferrer"provides some protection, ensuretrackingUrlis validated as anhttp(s)URL before rendering to prevent navigation via unexpected schemes if the source changes in the future. This is particularly important if the value is derived from external data or user input rather than being hardcoded server-side.If
trackingUrlis guaranteed to be safe (e.g., hardcoded or validated server-side), add a comment documenting this assumption. Otherwise, implement URL validation:+const isValidHttpUrl = (url: string): boolean => { + try { + const parsed = new URL(url) + return parsed.protocol === 'https:' || parsed.protocol === 'http:' + } catch { + return false + } +} + +const safeTrackingHref = isValidHttpUrl(details.trackingUrl) ? details.trackingUrl : undefined ... - <a - href={details.trackingUrl} + <a + href={safeTrackingHref} target="_blank"apps/agentic-chat/src/components/Portfolio/ActivityList.tsx (1)
13-20: LGTM! Filter and sort logic correctly extended for limit orders.The filter now includes
'limit_order'alongside swaps and sends, and the new.sort()ensures latest activities appear first. The implementation is clean and maintains consistency with existing patterns.apps/agentic-server/src/routes/chat.ts (1)
164-187: LGTM! Limit order tools properly integrated.The three limit order tools (
createLimitOrderTool,getLimitOrdersTool,cancelLimitOrderTool) follow the established pattern: each wraps execution with logging, passeswalletContext, and maintains consistent structure with existing tools likeinitiateSwapToolandsendTool.apps/agentic-chat/src/components/tools/CancelLimitOrderUI.tsx (1)
13-124: LGTM! Well-structured cancel UI with proper error handling.The component correctly handles historical-skipped cases, validates all required steps, computes completion progress, and provides clear user feedback through status icons, tracking links, and error messages. The step rendering is clean and follows established patterns.
apps/agentic-chat/src/stores/chatStore.ts (1)
22-31: LGTM! Store types properly extended for limit orders.The
PersistedToolStateinterface now includes'limit_order'and'cancel_limit_order'tool types, their corresponding output types (CreateLimitOrderOutput,CancelLimitOrderOutput), and an optionalwalletAddressfield. These extensions follow the established pattern and enable proper state persistence for limit order flows.apps/agentic-server/src/tools/limitOrder/index.ts (1)
1-24: LGTM! Clean barrel export consolidating limit order APIs.The file properly re-exports tools, execution functions, schemas, and types from all three limit order modules (
createLimitOrder,getLimitOrders,cancelLimitOrder), providing a clean public API surface.apps/agentic-chat/src/components/tools/GetLimitOrdersUI.tsx (1)
1-257: LGTM! UI now handles human-readable amounts and symbols correctly.The UI receives properly formatted data (symbols, decimals, human amounts) from the server's
GetLimitOrdersOutputand renders them correctly. The previous issue about displaying base-unit amounts and addresses has been addressed.apps/agentic-server/src/tools/limitOrder/getLimitOrders.ts (1)
32-180: LGTM! Server now returns enriched order data with symbols and human-readable amounts.The tool correctly resolves token metadata (symbol, precision) and converts base-unit amounts to human-readable format. The previous issue about returning raw base units has been addressed.
apps/agentic-chat/src/components/Portfolio/ActivityRow/components/LimitOrderDetails.tsx
Show resolved
Hide resolved
apps/agentic-chat/src/components/Portfolio/ActivityRow/components/SendDetails.tsx
Show resolved
Hide resolved
9a40ec3 to
839d0ba
Compare
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: 5
♻️ Duplicate comments (16)
apps/agentic-chat/src/lib/stepUtils.ts (1)
14-20: Step status ordering can mark a completed current step as IN_PROGRESS.
CheckcompletedSteps.has(step)before thecurrentStep === stepIN_PROGRESS branch.apps/agentic-server/src/tools/limitOrder/createLimitOrder.ts (4)
108-120: Blocker: limit-order pricing usesparseFloat(precision loss) + display amount can disagree with signed amount.
UseBigNumberend-to-end and derive bothbuyAmountBaseUnitandestimatedBuyAmountfrom the same precise calculation.function calculateBuyAmount(buyAsset: Asset, sellAmount: string, limitPrice: string): string { - const sellAmountNum = parseFloat(sellAmount) - const limitPriceNum = parseFloat(limitPrice) - - if (isNaN(sellAmountNum) || isNaN(limitPriceNum)) { - throw new Error('Invalid sellAmount or limitPrice') - } - - const buyAmountNum = sellAmountNum * limitPriceNum - return toBaseUnit(buyAmountNum.toString(), buyAsset.precision) + const bnSell = new BigNumber(sellAmount) + const bnPrice = new BigNumber(limitPrice) + if (!bnSell.isFinite() || !bnPrice.isFinite() || bnSell.lte(0) || bnPrice.lte(0)) { + throw new Error('Invalid sellAmount or limitPrice') + } + const buyAmountHuman = bnSell.times(bnPrice) + // avoid scientific notation + return toBaseUnit(buyAmountHuman.toFixed(buyAsset.precision), buyAsset.precision) } ... - const estimatedBuyAmount = new BigNumber(input.sellAmount).times(input.limitPrice).toFixed(6) + const estimatedBuyAmount = new BigNumber(input.sellAmount).times(input.limitPrice).toFixed(6)(After the change,
estimatedBuyAmountandbuyAmountBaseUnitwill agree up to rounding rules.)Also applies to: 209-210, 237-239
122-129: ValidateparseIntresult ingetEvmChainId(avoid NaN propagation).
parseIntcan returnNaNfor malformed chain IDs.function getEvmChainId(chainId: string): number { const parts = chainId.split(':') if (parts.length !== 2 || !parts[1]) { throw new Error(`Invalid chainId format: ${chainId}`) } - return parseInt(parts[1], 10) + const numericId = parseInt(parts[1].trim(), 10) + if (Number.isNaN(numericId)) throw new Error(`Invalid numeric chainId: ${chainId}`) + return numericId }
135-136: Remove/redact tool input logging (console.log(JSON.stringify(input))).
This can leak amounts and user intent.
258-274: Return a real/consistenttrackingUrl(don’t hardcode'pending').
You already haveorderResult.trackingUrl(andorderResult.orderId); the current return is guaranteed to be wrong/broken.return { summary, signingData: orderResult.signingData, orderParams: { sellToken, buyToken, sellAmount: sellAmountBaseUnit, buyAmount: buyAmountBaseUnit, validTo: orderResult.orderToSign.validTo, receiver: userAddress, chainId: evmChainId, }, needsApproval, approvalTx, approvalTarget, - trackingUrl: getCowExplorerUrl('pending'), + trackingUrl: orderResult.trackingUrl, }apps/agentic-server/src/tools/limitOrder/cancelLimitOrder.ts (2)
34-48: Don’t treat allgetCowOrderfailures as “order not found” (distinguish 404 vs transient).
Catching everything will misreport CoW outages/timeouts as “could not find order”.
54-55: Remove/redactconsole.log+ don’t include fullorder.ownerin errors.
Log redacted metadata at most, and report “ownership mismatch” without leaking addresses.Also applies to: 79-81
apps/agentic-chat/src/components/Portfolio/ActivityRow/components/LimitOrderDetails.tsx (1)
25-26: Guard against invalid/ambiguous expiry rendering.
new Date(details.expiresAt).toLocaleString()can produce “Invalid Date” and varies by locale/timezone; add a safe formatter/fallback.apps/agentic-chat/src/lib/activityNormalizer.ts (1)
110-110: Hardcoded mainnet URL ignores network-specific explorer paths.The hardcoded
https://explorer.cow.fi/orders/${orderId}will always produce mainnet format regardless of which chain the order is on. ThegetCowExplorerUrlutility (or network-aware logic) should be used instead.Consider using the trackingUrl from output if available:
- trackingUrl: `https://explorer.cow.fi/orders/${orderId}`, + trackingUrl: output.trackingUrl ?? `https://explorer.cow.fi/orders/${orderId}`,Based on past review comments, this issue was previously flagged.
apps/agentic-server/src/lib/cow/types.ts (2)
74-95: Incomplete sepolia support creates inconsistent behavior.Sepolia (chainId 11155111) appears in
COW_SUPPORTED_CHAINS(line 78) andgetCowApiUrl(lines 110-111), but is completely missing fromNETWORK_TO_CHAIN_ID(lines 83-87) andCHAIN_ID_TO_NETWORK(lines 90-94). This inconsistency means:
isCowSupportedChain(11155111)returns true- But
CHAIN_ID_TO_NETWORK[11155111]returns undefined- Tool schemas in other files explicitly exclude sepolia from valid networks
Either fully support sepolia by adding it to both mappings, or remove it from
COW_SUPPORTED_CHAINSto avoid API-level inconsistency:export const NETWORK_TO_CHAIN_ID: Record<string, number> = { ethereum: 1, gnosis: 100, arbitrum: 42161, + sepolia: 11155111, } export const CHAIN_ID_TO_NETWORK: Record<number, string> = { + 11155111: 'sepolia', }Based on past review comments, this issue was previously flagged.
104-117: getCowApiUrl silently defaults to mainnet for unknown chains.The default fallback to mainnet (line 115) could cause orders to be submitted to the wrong network if called with an unsupported chain ID. This creates a data integrity risk.
Throw an error for unsupported chains instead of silently defaulting:
export function getCowApiUrl(chainId: number): string { switch (chainId) { case 1: return 'https://api.cow.fi/mainnet' case 100: return 'https://api.cow.fi/gnosis' case 11155111: return 'https://api.cow.fi/sepolia' case 42161: return 'https://api.cow.fi/arbitrum_one' default: - return 'https://api.cow.fi/mainnet' + throw new Error(`Unsupported chain ID for CoW Protocol: ${chainId}`) } }Based on past review comments, this issue was previously flagged.
apps/agentic-server/src/tools/limitOrder/getLimitOrders.ts (3)
124-185: Parallelize per-chain fetches and use structured logging.Sequential chain iteration increases worst-case latency (3-4x slower than parallel). Additionally,
console.warnon line 182 should use structured logging.Refactor to use
Promise.allSettledfor concurrent chain queries:const chainResults = await Promise.allSettled( chainsToQuery.map(async (chainId) => { const chainIdString = `eip155:${chainId}` let userAddress: string try { userAddress = getAddressForChain(walletContext, chainIdString) } catch { return [] // Skip if wallet not connected } try { const orders = await getCowOrders(userAddress, chainId, { limit: 50 }) const networkName = CHAIN_ID_TO_NETWORK[chainId] || 'unknown' return orders .filter(order => { const orderStatus = formatOrderStatus(order.status) if (statusFilter && orderStatus !== statusFilter) return false return order.class === 'limit' }) .map(order => { // ... existing enrichment logic ... }) } catch (error) { // Use structured logger instead of console.warn return [] } }) ) const allOrders = chainResults .filter(result => result.status === 'fulfilled') .flatMap(result => result.value)
82-87: Remove thepresignaturePending→openmapping; preserve distinct order states.Per CoW Protocol semantics,
presignaturePending(order awaiting on-chain pre-signature, not tradable) andopen(signed and active in orderbook) are fundamentally different states. Collapsing them prevents filtering logic and user visibility of orders awaiting pre-signature workflow.Based on learnings, CoW Protocol distinguishes these states for valid operational reasons. Either remove the mapping or document the intentional simplification.
101-101: Replaceconsole.logwith structured logging or remove.Logging user input with
console.logis noisy in production and may leak user intent. Use a structured logger with appropriate redaction or remove the log statement.apps/agentic-chat/src/hooks/useCancelLimitOrderExecution.tsx (1)
41-103: Persist and restorefailedStepandcurrentStepto maintain post-refresh UI state.The hydration logic in
persistedStateToCancelOrderStatealways forcescurrentSteptoCOMPLETEand never restoresfailedStep, causing the UI to lose failure state after page refresh. The persisting function also doesn't write these fields tometa.Apply this diff to persist and restore both fields:
function cancelOrderStateToPersistedState( toolCallId: string, state: CancelOrderState, conversationId: string, orderOutput: CancelLimitOrderOutput | null, networkName?: string ): PersistedToolState { const phases: string[] = [] if (state.completedSteps.has(CancelOrderStep.PREPARE)) { phases.push('prepare_complete') } if (state.completedSteps.has(CancelOrderStep.NETWORK_SWITCH)) { phases.push('network_switched') } if (state.completedSteps.has(CancelOrderStep.SIGN)) { phases.push('signed') } if (state.completedSteps.has(CancelOrderStep.SUBMIT)) { phases.push('submitted') } if (state.error) { phases.push('error') } return { toolCallId, toolType: 'cancel_limit_order', conversationId, timestamp: Date.now(), phases, meta: { ...(orderOutput?.orderId && { orderId: orderOutput.orderId }), ...(state.error && { error: state.error }), + ...(state.failedStep !== undefined && { failedStep: state.failedStep }), + currentStep: state.currentStep, ...(networkName && { networkName }), }, ...(orderOutput && { toolOutput: orderOutput }), } } function persistedStateToCancelOrderState(persisted: PersistedToolState): CancelOrderState { const completedSteps = new Set<CancelOrderStep>() - const currentStep = CancelOrderStep.COMPLETE + const currentStep = (persisted.meta.currentStep as CancelOrderStep | undefined) ?? CancelOrderStep.COMPLETE if (persisted.phases.includes('prepare_complete')) { completedSteps.add(CancelOrderStep.PREPARE) } if (persisted.phases.includes('network_switched')) { completedSteps.add(CancelOrderStep.NETWORK_SWITCH) } if (persisted.phases.includes('signed')) { completedSteps.add(CancelOrderStep.SIGN) } if (persisted.phases.includes('submitted')) { completedSteps.add(CancelOrderStep.SUBMIT) } return { currentStep, completedSteps, error: persisted.meta.error as string | undefined, + failedStep: persisted.meta.failedStep as CancelOrderStep | undefined, } }apps/agentic-server/src/lib/cow/index.ts (1)
144-144:fromfield must be the order signer, not receiver.The
fromfield in the CoW API payload represents the order owner/signer address. Usingorder.receiveris incorrect when the receiver differs from the signer (e.g., when sending tokens to another address).Apply this diff to accept the signer address as a parameter:
export async function submitCowOrder( chainId: number, order: CowOrderQuote, signature: string, - signingScheme: 'eip712' | 'ethsign' = 'eip712' + signingScheme: 'eip712' | 'ethsign' = 'eip712', + from: string ): Promise<string> { const apiUrl = getCowApiUrl(chainId) const orderPayload = { sellToken: order.sellToken, buyToken: order.buyToken, receiver: order.receiver, sellAmount: order.sellAmount, buyAmount: order.buyAmount, validTo: order.validTo, appData: order.appData, feeAmount: order.feeAmount, kind: order.kind, partiallyFillable: order.partiallyFillable, sellTokenBalance: 'erc20', buyTokenBalance: 'erc20', signingScheme, signature, - from: order.receiver, // owner of the order + from, // owner/signer of the order }
🧹 Nitpick comments (5)
apps/agentic-server/src/index.ts (1)
91-110: Consider consolidating the threefrom './tools/limitOrder'export blocks.
Non-blocking, but reduces repetition and makes merge conflicts less likely.apps/agentic-chat/src/hooks/useLimitOrderExecution.tsx (1)
285-307: Consider warning when publicClient is unavailable.When
publicClientis null (line 290), the code silently skips waiting for the transaction receipt but still marksAPPROVAL_CONFIRMATIONas complete. This could lead to race conditions where approval hasn't been confirmed on-chain yet.Add a warning log when skipping confirmation:
if (needsApproval && approvalTxHash) { const publicClient = getPublicClient(wagmiConfig, { chainId: orderParams.chainId, }) - if (publicClient) { + if (!publicClient) { + console.warn(`[useLimitOrderExecution] No public client for chain ${orderParams.chainId}, skipping receipt confirmation`) + } else { await publicClient.waitForTransactionReceipt({ hash: approvalTxHash as `0x${string}`, confirmations: 1, }) }Based on past review comments, this concern was previously raised.
apps/agentic-chat/src/hooks/useCancelLimitOrderExecution.tsx (1)
228-236: Use current runtime state for persistence instead of manual construction.The success path manually constructs a state object for persistence, while the error path (line 243) correctly captures the current runtime state with
current(draft). This inconsistency creates maintenance risk—future state fields could be missed.Apply this diff:
- toast.success(<span>Your limit order has been cancelled</span>) - - persistState({ - currentStep: CancelOrderStep.COMPLETE, - completedSteps: new Set([ - CancelOrderStep.PREPARE, - CancelOrderStep.NETWORK_SWITCH, - CancelOrderStep.SIGN, - CancelOrderStep.SUBMIT, - ]), - }) + let finalState: CancelOrderState | undefined + setState(draft => { + finalState = current(draft) + }) + + if (finalState) persistState(finalState) + + toast.success(<span>Your limit order has been cancelled</span>)apps/agentic-server/src/lib/cow/index.ts (2)
160-162: Fragile string quote removal; parse response properly.Using regex to strip quotes from the response is brittle. Check the Content-Type header and parse accordingly, or handle both quoted and unquoted string responses explicitly.
// Response is the order UID as a string - const orderId = await response.text() - return orderId.replace(/"/g, '') // Remove quotes if present + const contentType = response.headers.get('content-type') + if (contentType?.includes('application/json')) { + return await response.json() + } + const orderId = await response.text() + return orderId.trim().replace(/^"|"$/g, '')
236-259: Note: Unused cancellation endpoint differs from the hook implementation.This function uses
DELETE /api/v1/orders/{orderId}(singular), whileuseCancelLimitOrderExecutionusesDELETE /api/v1/orders(plural, batch endpoint) with orderUids in the body. Both are valid CoW API endpoints, but this function is currently unused. Consider removing it or documenting when to use each endpoint.Run this script to verify usage of
cancelCowOrder:#!/bin/bash # Search for usages of cancelCowOrder rg -n "cancelCowOrder" --type ts
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (28)
apps/agentic-chat/src/components/Portfolio/ActivityList.tsx(2 hunks)apps/agentic-chat/src/components/Portfolio/ActivityRow.tsx(0 hunks)apps/agentic-chat/src/components/Portfolio/ActivityRow/ActivityRow.tsx(1 hunks)apps/agentic-chat/src/components/Portfolio/ActivityRow/components/LimitOrderDetails.tsx(1 hunks)apps/agentic-chat/src/components/Portfolio/ActivityRow/components/SendDetails.tsx(1 hunks)apps/agentic-chat/src/components/Portfolio/ActivityRow/components/SwapDetails.tsx(1 hunks)apps/agentic-chat/src/components/Portfolio/ActivityRow/index.ts(1 hunks)apps/agentic-chat/src/components/toolUIRegistry.tsx(2 hunks)apps/agentic-chat/src/components/tools/CancelLimitOrderUI.tsx(1 hunks)apps/agentic-chat/src/components/tools/GetLimitOrdersUI.tsx(1 hunks)apps/agentic-chat/src/components/tools/LimitOrderUI.tsx(1 hunks)apps/agentic-chat/src/hooks/useCancelLimitOrderExecution.tsx(1 hunks)apps/agentic-chat/src/hooks/useLimitOrderExecution.tsx(1 hunks)apps/agentic-chat/src/hooks/useToolExecutionEffect.ts(1 hunks)apps/agentic-chat/src/lib/activityNormalizer.ts(3 hunks)apps/agentic-chat/src/lib/cow-config.ts(1 hunks)apps/agentic-chat/src/lib/eventHandlers.ts(1 hunks)apps/agentic-chat/src/lib/stepUtils.ts(1 hunks)apps/agentic-chat/src/stores/chatStore.ts(2 hunks)apps/agentic-chat/src/types/activity.ts(2 hunks)apps/agentic-server/src/index.ts(1 hunks)apps/agentic-server/src/lib/cow/index.ts(1 hunks)apps/agentic-server/src/lib/cow/types.ts(1 hunks)apps/agentic-server/src/routes/chat.ts(4 hunks)apps/agentic-server/src/tools/limitOrder/cancelLimitOrder.ts(1 hunks)apps/agentic-server/src/tools/limitOrder/createLimitOrder.ts(1 hunks)apps/agentic-server/src/tools/limitOrder/getLimitOrders.ts(1 hunks)apps/agentic-server/src/tools/limitOrder/index.ts(1 hunks)
💤 Files with no reviewable changes (1)
- apps/agentic-chat/src/components/Portfolio/ActivityRow.tsx
🚧 Files skipped from review as they are similar to previous changes (8)
- apps/agentic-chat/src/hooks/useToolExecutionEffect.ts
- apps/agentic-chat/src/lib/eventHandlers.ts
- apps/agentic-chat/src/lib/cow-config.ts
- apps/agentic-chat/src/components/toolUIRegistry.tsx
- apps/agentic-chat/src/components/tools/LimitOrderUI.tsx
- apps/agentic-server/src/tools/limitOrder/index.ts
- apps/agentic-chat/src/components/Portfolio/ActivityRow/components/SendDetails.tsx
- apps/agentic-chat/src/types/activity.ts
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-22T20:32:10.539Z
Learnt from: kaladinlight
Repo: shapeshift/agentic-chat PR: 76
File: apps/agentic-server/src/mastra/tools/asset/coingecko/constants.ts:1-10
Timestamp: 2025-09-22T20:32:10.539Z
Learning: When cross-app dependencies are identified (like server code importing from chat packages), the user prefers to extract shared constants into dedicated packages in the packages/ directory following the existing pattern of shapeshiftoss/types and shapeshiftoss/utils.
Applied to files:
apps/agentic-chat/src/stores/chatStore.ts
🧬 Code graph analysis (11)
apps/agentic-chat/src/lib/stepUtils.ts (1)
apps/agentic-chat/src/hooks/useWalletConnection.ts (1)
useWalletConnection(23-48)
apps/agentic-chat/src/stores/chatStore.ts (2)
apps/agentic-server/src/tools/limitOrder/createLimitOrder.ts (1)
CreateLimitOrderOutput(90-106)apps/agentic-server/src/tools/limitOrder/cancelLimitOrder.ts (1)
CancelLimitOrderOutput(26-32)
apps/agentic-chat/src/hooks/useCancelLimitOrderExecution.tsx (8)
apps/agentic-server/src/tools/limitOrder/cancelLimitOrder.ts (1)
CancelLimitOrderOutput(26-32)apps/agentic-chat/src/stores/chatStore.ts (2)
PersistedToolState(22-31)useChatStore(76-195)apps/agentic-chat/src/lib/cow-config.ts (1)
getCowApiUrl(8-14)apps/agentic-server/src/lib/cow/types.ts (1)
getCowApiUrl(104-117)apps/agentic-chat/src/hooks/useWalletConnection.ts (1)
useWalletConnection(23-48)apps/agentic-chat/src/providers/ChatProvider.tsx (1)
useChatContext(33-39)apps/agentic-chat/src/hooks/useToolExecutionEffect.ts (1)
useToolExecutionEffect(10-60)apps/agentic-chat/src/lib/stepUtils.ts (2)
signTypedDataWithWallet(32-48)getStepStatus(14-20)
apps/agentic-chat/src/components/tools/GetLimitOrdersUI.tsx (8)
apps/agentic-chat/src/components/ui/Amount.tsx (1)
Amount(88-92)apps/agentic-chat/src/lib/eventHandlers.ts (1)
stopPropagationHandler(4-6)apps/agentic-chat/src/stores/chatStore.ts (2)
PersistedToolState(22-31)useChatStore(76-195)apps/agentic-server/src/index.ts (2)
CreateLimitOrderOutput(95-95)GetLimitOrdersOutput(102-102)apps/agentic-server/src/tools/limitOrder/createLimitOrder.ts (1)
CreateLimitOrderOutput(90-106)apps/agentic-server/src/tools/limitOrder/index.ts (2)
CreateLimitOrderOutput(6-6)GetLimitOrdersOutput(15-15)apps/agentic-chat/src/components/tools/toolUIHelpers.tsx (1)
useToolStateRender(10-31)apps/agentic-server/src/tools/limitOrder/getLimitOrders.ts (1)
GetLimitOrdersOutput(62-65)
apps/agentic-server/src/tools/limitOrder/cancelLimitOrder.ts (3)
apps/agentic-server/src/lib/cow/types.ts (4)
CowEIP712Domain(37-42)NETWORK_TO_CHAIN_ID(83-87)CHAIN_ID_TO_NETWORK(90-94)getCowExplorerUrl(100-102)apps/agentic-server/src/lib/cow/index.ts (2)
getCowOrder(193-208)prepareCowOrderCancellation(214-231)apps/agentic-server/src/utils/walletContextSimple.ts (2)
WalletContext(4-6)getAddressForChain(13-26)
apps/agentic-server/src/tools/limitOrder/createLimitOrder.ts (9)
apps/agentic-server/src/utils/chains/helpers.ts (1)
isEvmChain(3-6)packages/caip/src/assetId.ts (1)
fromAssetId(87-106)apps/agentic-server/src/utils/transactionHelpers.ts (1)
createTransaction(17-33)apps/agentic-server/src/lib/cow/types.ts (3)
CowOrderSigningData(48-53)isCowSupportedChain(96-98)getCowExplorerUrl(100-102)packages/utils/src/index.ts (1)
toBaseUnit(43-46)apps/agentic-server/src/utils/walletContextSimple.ts (2)
WalletContext(4-6)getAddressForChain(13-26)apps/agentic-server/src/utils/assetHelpers.ts (1)
resolveAsset(8-25)apps/agentic-server/src/lib/cow/index.ts (2)
COW_VAULT_RELAYER_ADDRESS(30-30)prepareCowLimitOrder(70-114)apps/agentic-server/src/utils/getAllowance.ts (1)
getAllowance(24-57)
apps/agentic-chat/src/components/Portfolio/ActivityRow/components/SwapDetails.tsx (7)
apps/agentic-chat/src/types/activity.ts (1)
SwapActivityDetails(9-26)apps/agentic-chat/src/lib/explorers.ts (1)
getExplorerUrl(1-15)apps/agentic-chat/src/components/ui/ToolCard.tsx (1)
ToolCard(71-78)apps/agentic-chat/src/components/ui/Amount.tsx (1)
Amount(88-92)apps/agentic-chat/src/lib/eventHandlers.ts (1)
stopPropagationHandler(4-6)apps/agentic-chat/src/lib/utils.ts (1)
truncateAddress(9-12)apps/agentic-chat/src/components/Portfolio/ActivityRow.tsx (1)
SwapDetails(30-83)
apps/agentic-server/src/lib/cow/index.ts (2)
apps/agentic-server/src/lib/cow/types.ts (10)
CowEIP712Types(44-46)CowEIP712Domain(37-42)CreateCowOrderParams(55-64)CreateCowOrderResult(66-72)isCowSupportedChain(96-98)CowOrderQuote(24-35)CowOrderSigningData(48-53)getCowExplorerUrl(100-102)getCowApiUrl(104-117)CowOrder(7-22)apps/agentic-chat/src/lib/cow-config.ts (1)
getCowApiUrl(8-14)
apps/agentic-chat/src/hooks/useLimitOrderExecution.tsx (7)
apps/agentic-server/src/tools/limitOrder/createLimitOrder.ts (1)
CreateLimitOrderOutput(90-106)apps/agentic-chat/src/stores/chatStore.ts (2)
PersistedToolState(22-31)useChatStore(76-195)apps/agentic-chat/src/lib/cow-config.ts (1)
getCowApiUrl(8-14)apps/agentic-chat/src/hooks/useWalletConnection.ts (1)
useWalletConnection(23-48)apps/agentic-chat/src/hooks/useToolExecutionEffect.ts (1)
useToolExecutionEffect(10-60)apps/agentic-chat/src/utils/swapExecutor.ts (1)
executeApproval(34-44)apps/agentic-chat/src/lib/wagmi-config.ts (1)
wagmiConfig(15-19)
apps/agentic-chat/src/components/Portfolio/ActivityRow/components/LimitOrderDetails.tsx (4)
apps/agentic-chat/src/types/activity.ts (1)
LimitOrderActivityDetails(39-52)apps/agentic-chat/src/components/ui/ToolCard.tsx (1)
ToolCard(71-78)apps/agentic-chat/src/components/ui/Amount.tsx (1)
Amount(88-92)apps/agentic-chat/src/lib/eventHandlers.ts (1)
stopPropagationHandler(4-6)
apps/agentic-server/src/lib/cow/types.ts (1)
apps/agentic-chat/src/lib/cow-config.ts (1)
getCowApiUrl(8-14)
⏰ 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: main
🔇 Additional comments (21)
apps/agentic-chat/src/components/Portfolio/ActivityList.tsx (1)
13-20: LGTM — includeslimit_order+ sorts latest-first + updated empty state copy.
Good incremental update for the new activity type.Also applies to: 22-27
apps/agentic-chat/src/components/Portfolio/ActivityRow/index.ts (1)
1-1: LGTM — clean barrel re-export.apps/agentic-chat/src/components/Portfolio/ActivityRow/components/SwapDetails.tsx (1)
13-66: LGTM — clean extraction, and link click propagation is handled.apps/agentic-chat/src/stores/chatStore.ts (1)
2-31: LGTM! Clean type extensions for limit order support.The type extensions correctly add limit order support to the persisted state:
- New tool types ('limit_order', 'cancel_limit_order') properly extend the toolType union
- Output types correctly include CreateLimitOrderOutput and CancelLimitOrderOutput
- walletAddress field appropriately optional for backward compatibility
apps/agentic-chat/src/components/Portfolio/ActivityRow/ActivityRow.tsx (3)
26-35: LGTM! Activity title formatting handles all activity types correctly.The formatActivityTitle function appropriately handles all three activity types (swap, send, limit_order) with correct amount and symbol formatting. The limit_order case correctly uses
estimatedAmountfor the buy asset, which is appropriate for limit orders where the exact buy amount may vary.
37-64: LGTM! ActivityDetails conditional rendering is correct.The component correctly:
- Excludes txHash for limit_order activities (line 38), as they use orderId instead
- Conditionally renders TX ID link only when both explorerUrl and txHash exist
- Routes to appropriate detail components based on activity type
1-1: date-fns dependency is properly declared at the workspace level.The library is included in the root package.json with version ^4.1.0 and is available to all packages in this monorepo structure, including apps/agentic-chat. No action required.
apps/agentic-chat/src/components/tools/CancelLimitOrderUI.tsx (1)
13-124: LGTM! Solid multi-step cancellation UI with proper error handling.The CancelLimitOrderUI component correctly:
- Handles historical data skipping gracefully (lines 25-33)
- Validates all 4 required steps are present before rendering (lines 35-44)
- Calculates completed steps including SKIPPED status (lines 46-48)
- Provides clear error messages via footer (lines 50-54, 115-121)
- Displays truncated order ID in monospaced font for readability (lines 79-81)
- Shows CoW Explorer tracking link when available (lines 103-113)
apps/agentic-chat/src/lib/activityNormalizer.ts (1)
92-122: LGTM! Limit order normalization correctly structured.The normalizeLimitOrderActivity function properly:
- Validates required output and orderId presence (line 96)
- Extracts limit order details from CreateLimitOrderOutput
- Returns correctly typed ActivityItem for limit_order type
apps/agentic-server/src/routes/chat.ts (2)
164-187: LGTM! Limit order tools follow established patterns.The three new tool wrappers (createLimitOrderTool, getLimitOrdersTool, cancelLimitOrderTool) correctly:
- Follow the same structure as existing tools (initiateSwapTool, sendTool, etc.)
- Log arguments for debugging
- Pass walletContext for address resolution
- Maintain consistent error handling
304-310: LGTM! System prompt clearly documents limit order capabilities.The limit order documentation in the system prompt appropriately:
- Describes when to use limit orders (specific price points)
- Notes gasless execution via EIP-712 signatures
- Lists supported chains (Ethereum, Gnosis, Arbitrum)
- Clarifies same-chain only limitation
- Documents order lifecycle (getLimitOrders, cancelLimitOrder, auto-execution)
apps/agentic-server/src/lib/cow/types.ts (1)
5-72: LGTM! CoW Protocol type definitions are well-structured.The type definitions correctly model:
- Order status union with all valid CoW states
- CowOrder interface with all required order fields
- EIP-712 signing data structures (domain, types, message)
- Order creation parameters and results
apps/agentic-chat/src/components/tools/GetLimitOrdersUI.tsx (3)
19-41: LGTM! Status badge configuration and component are well-designed.The status configuration correctly:
- Maps all valid CoW order statuses to appropriate icons and colors
- Provides a fallback to 'open' status for safety (line 32)
- Uses semantic colors (blue for open, green for filled, red for cancelled, etc.)
56-136: LGTM! OrderListItem properly formats amounts and displays order details.The component correctly:
- Uses BigNumber for precise rate calculation (lines 71-80) to avoid floating-point errors
- Formats amounts via Amount.Crypto component for proper decimal handling
- Shows filled percentage for fulfilled orders (lines 114-119)
- Displays expiration date for open orders (lines 108-113)
- Truncates wallet address when present (lines 82, 102-107)
- Includes external link to tracking URL with proper rel attributes
175-254: LGTM! GetLimitOrdersUI handles both history and live modes correctly.The main component properly:
- Detects history mode via accountScope parameter (line 178)
- Loads historical orders from persisted transactions for history mode (lines 180, 191)
- Uses server data for live mode with status validation (lines 189-195)
- Displays empty state when no orders exist (lines 197-214)
- Shows open order count for live mode (line 226)
- Maps orders to OrderListItem components with appropriate props (lines 234-248)
apps/agentic-chat/src/hooks/useLimitOrderExecution.tsx (2)
144-185: LGTM! submitSignedOrder correctly constructs and POSTs the CoW order.The function properly:
- Constructs order payload with all required CoW Protocol fields
- Uses dynamic API URL based on chainId via getCowApiUrl
- Sets correct headers and serializes payload as JSON
- Handles error responses with descriptive messages
- Strips quotes from returned orderId
216-366: LGTM! Execution flow properly orchestrates all limit order steps.The hook correctly:
- Validates wallet connection and address consistency (lines 236-242)
- Marks PREPARE complete before starting execution (lines 245-249)
- Switches network via Dynamic wallet connector (lines 252-266)
- Conditionally executes approval if needed (lines 269-283)
- Waits for approval confirmation before signing (lines 286-307)
- Signs EIP-712 message with wallet (line 310)
- Submits signed order to CoW API (line 320)
- Persists state after completion and on errors (lines 336-348, 358)
- Shows user-friendly toast notifications (lines 329-334, 360-364)
apps/agentic-server/src/tools/limitOrder/getLimitOrders.ts (2)
11-60: LGTM! Schema and type definitions are well-structured.The schema includes appropriate filters and the OrderInfo interface properly includes enriched token metadata (symbols, decimals, human-readable amounts) addressing the need for UI-friendly data.
148-179: LGTM! Defensive token metadata handling is solid.The fallback to
DEFAULT_DECIMALSfor missing precision and truncated address for missing symbols ensures the function remains robust when token metadata is unavailable.apps/agentic-server/src/lib/cow/index.ts (2)
19-64: LGTM! EIP-712 domain and constants are correctly structured.The settlement contract addresses, vault relayer address, and EIP-712 type definitions align with CoW Protocol specifications.
70-114: LGTM! Order preparation logic is correct.The function properly constructs CoW limit orders with appropriate EIP-712 signing data. The receiver fallback to
userAddresshandles the case where no explicit receiver is specified.
839d0ba to
3f4bba7
Compare
3f4bba7 to
13f15d2
Compare
13f15d2 to
c16e597
Compare
c16e597 to
abc898f
Compare
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: 5
♻️ Duplicate comments (11)
apps/agentic-chat/src/lib/stepUtils.ts (2)
47-53: Step status logic may incorrectly report completed current step as IN_PROGRESS.When
currentStep === stepand the step is already incompletedSteps, the function returnsIN_PROGRESS(Line 50) before checkingcompletedSteps.has(step)(Line 51). This could cause a completed step to display as in-progress.Consider reordering the checks as previously suggested:
export function getStepStatus<TStep extends number>(step: TStep, state: StepState<TStep>): StepStatus { if (state.failedStep === step) return StepStatus.FAILED + if (state.completedSteps.has(step)) return StepStatus.COMPLETE if (state.currentStep < step) return StepStatus.NOT_STARTED if (state.currentStep === step && !state.error) return StepStatus.IN_PROGRESS - if (state.completedSteps.has(step)) return StepStatus.COMPLETE return StepStatus.SKIPPED }
1-1: Type-only import used withtypeofmay cause issues.The
import type { useWalletConnection }is used withtypeof useWalletConnectionat line 66. While this may work in type positions, it's non-idiomatic and fragile. Consider exporting and importing the return type directly from the hook file.-import type { useWalletConnection } from '@/hooks/useWalletConnection' +import type { WalletConnectionResult } from '@/hooks/useWalletConnection' ... export async function signTypedDataWithWallet( - evmWallet: NonNullable<ReturnType<typeof useWalletConnection>['evmWallet']>, + evmWallet: NonNullable<WalletConnectionResult['evmWallet']>, signingData: Eip712SigningData ): Promise<string> {apps/agentic-server/src/lib/cow/types.ts (3)
104-117:getCowApiUrlsilently defaults to mainnet for unsupported chains.The default fallback to mainnet (line 115) could cause orders to be submitted to the wrong network if called with an unsupported chain ID. Note that
apps/agentic-chat/src/lib/cow-config.tsalready has a safer implementation that throws on unsupported chains.Consider aligning with the safer pattern:
export function getCowApiUrl(chainId: number): string { switch (chainId) { case 1: return 'https://api.cow.fi/mainnet' case 100: return 'https://api.cow.fi/gnosis' case 11155111: return 'https://api.cow.fi/sepolia' case 42161: return 'https://api.cow.fi/arbitrum_one' default: - return 'https://api.cow.fi/mainnet' + throw new Error(`Unsupported chain ID for CoW Protocol: ${chainId}`) } }
100-102:getCowExplorerUrlhardcodes mainnet format for all networks.CoW Explorer uses network-specific paths (e.g.,
explorer.cow.fi/gnosis/orders/...). Orders from other networks will link to incorrect explorer pages.Accept chainId parameter and route to correct network path:
-export function getCowExplorerUrl(orderId: string): string { - return `https://explorer.cow.fi/orders/${orderId}` +export function getCowExplorerUrl(chainId: number, orderId: string): string { + const network = CHAIN_ID_TO_NETWORK[chainId] ?? 'mainnet' + return `https://explorer.cow.fi/${network}/orders/${orderId}` }
74-94: Inconsistent sepolia support across mappings.
COW_SUPPORTED_CHAINSincludes sepolia (11155111) butNETWORK_TO_CHAIN_IDandCHAIN_ID_TO_NETWORKomit it. This inconsistency meansisCowSupportedChain(11155111)returnstrue, butCHAIN_ID_TO_NETWORK[11155111]returnsundefined.Either add sepolia to all mappings or remove it from
COW_SUPPORTED_CHAINS:export const NETWORK_TO_CHAIN_ID: Record<string, number> = { ethereum: 1, gnosis: 100, arbitrum: 42161, + sepolia: 11155111, } export const CHAIN_ID_TO_NETWORK: Record<number, string> = { + 11155111: 'sepolia', }apps/agentic-chat/src/lib/activityNormalizer.ts (1)
110-110: Hardcoded mainnet URL ignores the order's network.This was flagged in a previous review. The URL
https://explorer.cow.fi/orders/${orderId}is the mainnet format, but orders on Gnosis, Arbitrum, or Sepolia need network-specific paths likehttps://explorer.cow.fi/gnosis/orders/${orderId}.Consider using
output.trackingUrlif the server provides it, or construct a network-aware URL:- trackingUrl: `https://explorer.cow.fi/orders/${orderId}`, + trackingUrl: output.trackingUrl ?? `https://explorer.cow.fi/orders/${orderId}`,apps/agentic-chat/src/hooks/useCancelLimitOrderExecution.tsx (1)
47-75:failedStepandcurrentStepnot persisted, breaking post-refresh UI state.The hydration function
persistedStateToCancelOrderStatealways setscurrentSteptoCOMPLETEand doesn't restorefailedStep. After a page refresh, the UI cannot show which step failed.As suggested in the previous review, persist these fields in
metaand restore on hydration:function cancelOrderStateToPersistedState( // ... ): PersistedToolState { return { // ... meta: { ...(orderOutput?.orderId && { orderId: orderOutput.orderId }), ...(state.error && { error: state.error }), + ...(state.failedStep !== undefined && { failedStep: state.failedStep }), + ...(state.currentStep !== undefined && { currentStep: state.currentStep }), ...(networkName && { networkName }), }, // ... } } function persistedStateToCancelOrderState(persisted: PersistedToolState): CancelOrderState { + const currentStep = (persisted.meta.currentStep as CancelOrderStep | undefined) ?? CancelOrderStep.COMPLETE return { - currentStep: CancelOrderStep.COMPLETE, + currentStep, completedSteps: CANCEL_ORDER_PHASES.fromPhases(persisted.phases), error: persisted.meta.error as string | undefined, + failedStep: persisted.meta.failedStep as CancelOrderStep | undefined, } }apps/agentic-chat/src/components/tools/GetLimitOrdersUI.tsx (1)
154-169: Add type assertion to fix TypeScript errors.The pipeline fails with multiple
TS2339: Property 'summary' does not exist on typeerrors. Casttx.toolOutputtoCreateLimitOrderOutputto resolve.const toDisplayOrder = (tx: PersistedToolState): DisplayOrder => { - const output = tx.toolOutput + const output = tx.toolOutput as CreateLimitOrderOutput | undefined const orderId = tx.meta.orderId as string + const network = output?.summary?.sellAsset?.network ?? 'ethereum' return { orderId, - network: output?.summary?.sellAsset?.network ?? 'unknown', + network, sellTokenSymbol: output?.summary?.sellAsset?.symbol ?? 'Unknown', buyTokenSymbol: output?.summary?.buyAsset?.symbol ?? 'Unknown', sellAmount: output?.summary?.sellAsset?.amount ?? '0', buyAmount: output?.summary?.buyAsset?.estimatedAmount ?? '0', filledPercent: 0, expiresAt: output?.summary?.expiresAt ?? new Date().toISOString(), - trackingUrl: `https://explorer.cow.fi/orders/${orderId}`, + trackingUrl: output?.trackingUrl ?? `https://explorer.cow.fi/${network !== 'ethereum' ? `${network}/` : ''}orders/${orderId}`, walletAddress: tx.walletAddress, } }This also addresses the hardcoded mainnet URL issue flagged in previous reviews by constructing a network-aware URL.
apps/agentic-server/src/tools/limitOrder/cancelLimitOrder.ts (2)
34-48: Differentiate 404 (not found) from transient errors in chain detection.The catch-all error handling prevents distinguishing between "order doesn't exist on this chain" (expected, continue to next chain) and actual failures like API downtime or network errors (should propagate). Users see "Could not find order" even when the real issue is a CoW API incident.
Based on past review comments, this issue has already been raised.
72-79: Avoid exposing order owner address in error messages.Line 78 includes
order.ownerin the error message, which exposes the owner's wallet address. This is a privacy concern and should be avoided.Based on past review comments, this issue has already been raised.
apps/agentic-server/src/lib/cow/index.ts (1)
121-163:fromfield should be the order signer, not receiver.Line 144 uses
order.receiveras thefromfield, but the CoW API expects the order signer/owner address. This is incorrect when the receiver differs from the signer (e.g., when sending tokens to another address).Based on past review comments, this issue has already been raised.
🧹 Nitpick comments (5)
apps/agentic-chat/src/components/tools/InitiateSwapUI.tsx (1)
85-94: LGTM! Consider adding nested optional chaining for extra safety.The refactor to inline
TxStepCard.SwapPairandTxStepCard.Amountis excellent—it consolidates loading state handling and removes local subcomponents. TheisLoadingprop is correctly derived and passed through.For defensive coding, consider extending optional chaining to nested properties:
<TxStepCard.SwapPair - fromSymbol={swap?.sellAsset.symbol.toUpperCase()} - toSymbol={swap?.buyAsset.symbol.toUpperCase()} + fromSymbol={swap?.sellAsset?.symbol?.toUpperCase()} + toSymbol={swap?.buyAsset?.symbol?.toUpperCase()} isLoading={isLoading} /> <TxStepCard.Amount value={swap?.buyAmountCryptoPrecision} - symbol={swap?.buyAsset.symbol.toUpperCase()} + symbol={swap?.buyAsset?.symbol?.toUpperCase()} isLoading={isLoading} />This prevents potential runtime errors if
sellAssetorbuyAssetare unexpectedly undefined, though if types guarantee these properties exist whenswapis defined, the current code is fine.apps/agentic-chat/src/hooks/useSwapExecution.tsx (1)
67-70: Clarify intent:approval_skippedis metadata-only, not round-trippable.The
'approval_skipped'phase is added conditionally but isn't inSWAP_PHASESmapping, so it won't be reconstructed viafromPhases. This appears intentional for tracking purposes only, but consider adding a brief comment to clarify this is one-way metadata.const phases = [ ...SWAP_PHASES.toPhases(state.completedSteps, state.error), + // 'approval_skipped' is metadata-only; not reconstructed on hydration state.currentStep > SwapStep.APPROVAL && !state.completedSteps.has(SwapStep.APPROVAL) && 'approval_skipped', ].filter(Boolean) as string[]apps/agentic-chat/src/components/tools/GetLimitOrdersUI.tsx (1)
71-80: Potential division by zero handled, but consider edge case.The rate calculation guards against division by zero with
sellBn.gt(0). However, if both amounts are'0'(default fallback), the rate displays as'0'which may be confusing. Consider showing "N/A" or hiding the rate row when amounts are invalid.apps/agentic-server/src/tools/limitOrder/getLimitOrders.ts (2)
11-28: Consider adding 'presignaturePending' to the status enum.The schema currently allows filtering by 'open', 'fulfilled', 'cancelled', 'expired', and 'all', but not 'presignaturePending' (which is a valid CoW order status). Orders with this status will be included in 'all' results but users cannot filter specifically for them.
Apply this diff if you want to support filtering by presignaturePending orders:
export const getLimitOrdersSchema = z.object({ status: z - .enum(['open', 'fulfilled', 'cancelled', 'expired', 'all']) + .enum(['open', 'fulfilled', 'cancelled', 'expired', 'presignaturePending', 'all']) .optional() .default('all') .describe('Filter orders by status. Default is "all".'),
93-96: Consider extracting hardcoded testnet ID to a constant.Line 95 hardcodes the Sepolia testnet chain ID
11155111for exclusion. While functional, extracting this to a named constant would improve maintainability.const TESTNET_CHAIN_IDS = [11155111] // Sepolia // Then in the filter: .filter(id => !TESTNET_CHAIN_IDS.includes(id))
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (33)
apps/agentic-chat/src/components/Portfolio/ActivityList.tsx(2 hunks)apps/agentic-chat/src/components/Portfolio/ActivityRow.tsx(0 hunks)apps/agentic-chat/src/components/Portfolio/ActivityRow/ActivityRow.tsx(1 hunks)apps/agentic-chat/src/components/Portfolio/ActivityRow/components/LimitOrderDetails.tsx(1 hunks)apps/agentic-chat/src/components/Portfolio/ActivityRow/components/SendDetails.tsx(1 hunks)apps/agentic-chat/src/components/Portfolio/ActivityRow/components/SwapDetails.tsx(1 hunks)apps/agentic-chat/src/components/Portfolio/ActivityRow/index.ts(1 hunks)apps/agentic-chat/src/components/toolUIRegistry.tsx(2 hunks)apps/agentic-chat/src/components/tools/CancelLimitOrderUI.tsx(1 hunks)apps/agentic-chat/src/components/tools/GetLimitOrdersUI.tsx(1 hunks)apps/agentic-chat/src/components/tools/InitiateSwapUI.tsx(1 hunks)apps/agentic-chat/src/components/tools/LimitOrderUI.tsx(1 hunks)apps/agentic-chat/src/components/tools/SendUI.tsx(1 hunks)apps/agentic-chat/src/components/ui/TxStepCard.tsx(3 hunks)apps/agentic-chat/src/hooks/useCancelLimitOrderExecution.tsx(1 hunks)apps/agentic-chat/src/hooks/useLimitOrderExecution.tsx(1 hunks)apps/agentic-chat/src/hooks/useSendExecution.ts(4 hunks)apps/agentic-chat/src/hooks/useSwapExecution.tsx(4 hunks)apps/agentic-chat/src/hooks/useToolExecutionEffect.ts(1 hunks)apps/agentic-chat/src/lib/activityNormalizer.ts(3 hunks)apps/agentic-chat/src/lib/cow-config.ts(1 hunks)apps/agentic-chat/src/lib/eventHandlers.ts(1 hunks)apps/agentic-chat/src/lib/stepUtils.ts(1 hunks)apps/agentic-chat/src/stores/chatStore.ts(2 hunks)apps/agentic-chat/src/types/activity.ts(2 hunks)apps/agentic-server/src/index.ts(1 hunks)apps/agentic-server/src/lib/cow/index.ts(1 hunks)apps/agentic-server/src/lib/cow/types.ts(1 hunks)apps/agentic-server/src/routes/chat.ts(4 hunks)apps/agentic-server/src/tools/limitOrder/cancelLimitOrder.ts(1 hunks)apps/agentic-server/src/tools/limitOrder/createLimitOrder.ts(1 hunks)apps/agentic-server/src/tools/limitOrder/getLimitOrders.ts(1 hunks)apps/agentic-server/src/tools/limitOrder/index.ts(1 hunks)
💤 Files with no reviewable changes (1)
- apps/agentic-chat/src/components/Portfolio/ActivityRow.tsx
🚧 Files skipped from review as they are similar to previous changes (15)
- apps/agentic-chat/src/components/Portfolio/ActivityRow/index.ts
- apps/agentic-chat/src/components/toolUIRegistry.tsx
- apps/agentic-chat/src/components/Portfolio/ActivityList.tsx
- apps/agentic-chat/src/components/Portfolio/ActivityRow/components/LimitOrderDetails.tsx
- apps/agentic-chat/src/components/Portfolio/ActivityRow/components/SendDetails.tsx
- apps/agentic-chat/src/lib/cow-config.ts
- apps/agentic-chat/src/lib/eventHandlers.ts
- apps/agentic-server/src/tools/limitOrder/index.ts
- apps/agentic-chat/src/stores/chatStore.ts
- apps/agentic-chat/src/hooks/useLimitOrderExecution.tsx
- apps/agentic-server/src/routes/chat.ts
- apps/agentic-server/src/index.ts
- apps/agentic-chat/src/components/tools/LimitOrderUI.tsx
- apps/agentic-server/src/tools/limitOrder/createLimitOrder.ts
- apps/agentic-chat/src/components/tools/CancelLimitOrderUI.tsx
🧰 Additional context used
🧬 Code graph analysis (13)
apps/agentic-chat/src/components/tools/SendUI.tsx (1)
apps/agentic-chat/src/components/ui/TxStepCard.tsx (1)
TxStepCard(178-191)
apps/agentic-chat/src/components/Portfolio/ActivityRow/components/SwapDetails.tsx (7)
apps/agentic-chat/src/types/activity.ts (1)
SwapActivityDetails(9-26)apps/agentic-chat/src/lib/explorers.ts (1)
getExplorerUrl(1-15)apps/agentic-chat/src/components/ui/ToolCard.tsx (1)
ToolCard(71-78)apps/agentic-chat/src/components/ui/Amount.tsx (1)
Amount(88-92)apps/agentic-chat/src/lib/eventHandlers.ts (1)
stopPropagationHandler(4-6)apps/agentic-chat/src/lib/utils.ts (1)
truncateAddress(9-12)apps/agentic-chat/src/components/Portfolio/ActivityRow.tsx (1)
SwapDetails(30-83)
apps/agentic-chat/src/components/tools/InitiateSwapUI.tsx (1)
apps/agentic-chat/src/components/ui/TxStepCard.tsx (1)
TxStepCard(178-191)
apps/agentic-chat/src/components/Portfolio/ActivityRow/ActivityRow.tsx (9)
apps/agentic-chat/src/types/activity.ts (1)
ActivityItem(70-70)apps/agentic-chat/src/lib/explorers.ts (1)
getExplorerUrl(1-15)apps/agentic-chat/src/components/ui/ToolCard.tsx (1)
ToolCard(71-78)apps/agentic-chat/src/lib/eventHandlers.ts (1)
stopPropagationHandler(4-6)apps/agentic-chat/src/lib/utils.ts (1)
truncateAddress(9-12)apps/agentic-chat/src/components/Portfolio/ActivityRow/components/SwapDetails.tsx (1)
SwapDetails(13-66)apps/agentic-chat/src/components/Portfolio/ActivityRow/components/SendDetails.tsx (1)
SendDetails(10-24)apps/agentic-chat/src/components/Portfolio/ActivityRow/components/LimitOrderDetails.tsx (1)
LimitOrderDetails(10-43)apps/agentic-chat/src/components/ui/DrawerListItem.tsx (1)
DrawerListItem(13-41)
apps/agentic-chat/src/lib/activityNormalizer.ts (2)
apps/agentic-chat/src/stores/chatStore.ts (1)
PersistedToolState(22-31)apps/agentic-chat/src/types/activity.ts (2)
ActivityItem(70-70)LimitOrderActivityDetails(39-52)
apps/agentic-server/src/tools/limitOrder/cancelLimitOrder.ts (3)
apps/agentic-server/src/lib/cow/types.ts (4)
CowEIP712Domain(37-42)NETWORK_TO_CHAIN_ID(83-87)CHAIN_ID_TO_NETWORK(90-94)getCowExplorerUrl(100-102)apps/agentic-server/src/lib/cow/index.ts (2)
getCowOrder(193-208)prepareCowOrderCancellation(214-231)apps/agentic-server/src/utils/walletContextSimple.ts (2)
WalletContext(4-6)getAddressForChain(13-26)
apps/agentic-chat/src/lib/stepUtils.ts (3)
apps/agentic-chat/src/hooks/useSendExecution.ts (1)
StepStatus(30-30)apps/agentic-chat/src/hooks/useSwapExecution.tsx (1)
StepStatus(36-36)apps/agentic-chat/src/hooks/useWalletConnection.ts (1)
useWalletConnection(23-48)
apps/agentic-chat/src/hooks/useSendExecution.ts (1)
apps/agentic-chat/src/lib/stepUtils.ts (1)
createStepPhaseMap(12-36)
apps/agentic-chat/src/hooks/useSwapExecution.tsx (1)
apps/agentic-chat/src/lib/stepUtils.ts (1)
createStepPhaseMap(12-36)
apps/agentic-server/src/lib/cow/types.ts (1)
apps/agentic-chat/src/lib/cow-config.ts (1)
getCowApiUrl(8-14)
apps/agentic-server/src/tools/limitOrder/getLimitOrders.ts (5)
apps/agentic-server/src/lib/cow/types.ts (6)
CowOrderStatus(5-5)CHAIN_ID_TO_NETWORK(90-94)CowOrder(7-22)NETWORK_TO_CHAIN_ID(83-87)COW_SUPPORTED_CHAINS(75-80)getCowExplorerUrl(100-102)packages/types/src/network.ts (1)
Network(44-44)apps/agentic-server/src/utils/walletContextSimple.ts (2)
WalletContext(4-6)getAddressForChain(13-26)apps/agentic-server/src/lib/cow/index.ts (1)
getCowOrders(168-188)packages/utils/src/index.ts (1)
fromBaseUnit(38-41)
apps/agentic-chat/src/components/tools/GetLimitOrdersUI.tsx (8)
apps/agentic-chat/src/lib/utils.ts (2)
cn(5-7)truncateAddress(9-12)apps/agentic-chat/src/components/ui/Amount.tsx (1)
Amount(88-92)apps/agentic-chat/src/lib/eventHandlers.ts (1)
stopPropagationHandler(4-6)apps/agentic-chat/src/stores/chatStore.ts (1)
PersistedToolState(22-31)apps/agentic-chat/src/components/tools/toolUIHelpers.tsx (1)
useToolStateRender(10-31)apps/agentic-server/src/index.ts (1)
GetLimitOrdersOutput(102-102)apps/agentic-server/src/tools/limitOrder/getLimitOrders.ts (1)
GetLimitOrdersOutput(62-65)apps/agentic-chat/src/components/ui/ToolCard.tsx (1)
ToolCard(71-78)
apps/agentic-server/src/lib/cow/index.ts (1)
apps/agentic-server/src/lib/cow/types.ts (10)
CowEIP712Types(44-46)CowEIP712Domain(37-42)CreateCowOrderParams(55-64)CreateCowOrderResult(66-72)isCowSupportedChain(96-98)CowOrderQuote(24-35)CowOrderSigningData(48-53)getCowExplorerUrl(100-102)getCowApiUrl(104-117)CowOrder(7-22)
🪛 GitHub Actions: CI
apps/agentic-chat/src/lib/activityNormalizer.ts
[error] 1-1: TS6196: 'CreateLimitOrderOutput' is declared but never used.
[error] 96-96: TS2339: Property 'summary' does not exist on type '{ summary: { sellAsset: { symbol: string; amount: string; network: string; chainName: string; valueUSD: string; priceUSD: string; }; buyAsset: { symbol: string; estimatedAmount: string; network: string; chainName: string; estimatedValueUSD: string; priceUSD: string; }; exchange: { ...; }; isCrossChain: boolean; }; n...'.
[error] 100-100: TS2339: Property 'summary' does not exist on type '{ summary: { sellAsset: { symbol: string; amount: string; network: string; chainName: string; valueUSD: string; priceUSD: string; }; buyAsset: { symbol: string; estimatedAmount: string; network: string; chainName: string; estimatedValueUSD: string; priceUSD: string; }; exchange: { ...; }; isCrossChain: boolean; }; n...'.
[error] 101-101: TS2339: Property 'summary' does not exist on type '{ summary: { sellAsset: { symbol: string; amount: string; network: string; chainName: string; valueUSD: string; priceUSD: string; }; buyAsset: { symbol: string; estimatedAmount: string; network: string; chainName: string; estimatedValueUSD: string; priceUSD: string; }; exchange: { ...; }; isCrossChain: boolean; }; n...'.
[error] 104-104: TS2339: Property 'summary' does not exist on type '{ summary: { sellAsset: { symbol: string; amount: string; network: string; chainName: string; valueUSD: string; priceUSD: string; }; buyAsset: { symbol: string; estimatedAmount: string; network: string; chainName: string; estimatedValueUSD: string; priceUSD: string; }; exchange: { ...; }; isCrossChain: boolean; }; n...'.
[error] 105-105: TS2339: Property 'summary' does not exist on type '{ summary: { sellAsset: { symbol: string; amount: string; network: string; chainName: string; valueUSD: string; priceUSD: string; }; buyAsset: { symbol: string; estimatedAmount: string; network: string; chainName: string; estimatedValueUSD: string; priceUSD: string; }; exchange: { ...; }; isCrossChain: boolean; }; n...'.
[error] 107-107: TS2339: Property 'summary' does not exist on type '{ summary: { sellAsset: { symbol: string; amount: string; network: string; chainName: string; valueUSD: string; priceUSD: string; }; buyAsset: { symbol: string; estimatedAmount: string; network: string; chainName: string; estimatedValueUSD: string; priceUSD: string; }; exchange: { ...; }; isCrossChain: boolean; }; n...'.
[error] 108-108: TS2339: Property 'summary' does not exist on type '{ summary: { sellAsset: { symbol: string; amount: string; network: string; chainName: string; valueUSD: string; priceUSD: string; }; buyAsset: { symbol: string; estimatedAmount: string; network: string; chainName: string; estimatedValueUSD: string; priceUSD: string; }; exchange: { ...; }; isCrossChain: boolean; }; n...'.
[error] 109-109: TS2339: Property 'summary' does not exist on type '{ summary: { sellAsset: { symbol: string; amount: string; network: string; chainName: string; valueUSD: string; priceUSD: string; }; buyAsset: { symbol: string; estimatedAmount: string; network: string; chainName: string; estimatedValueUSD: string; priceUSD: string; }; exchange: { ...; }; isCrossChain: boolean; }; n...'.
[error] 118-118: TS2339: Property 'summary' does not exist on type '{ summary: { sellAsset: { symbol: string; amount: string; network: string; chainName: string; valueUSD: string; priceUSD: string; }; buyAsset: { symbol: string; estimatedAmount: string; network: string; chainName: string; estimatedValueUSD: string; priceUSD: string; }; exchange: { ...; }; isCrossChain: boolean; }; n...'.
[error] 119-119: TS2339: Property 'summary' does not exist on type '{ summary: { sellAsset: { symbol: string; amount: string; network: string; chainName: string; valueUSD: string; priceUSD: string; }; buyAsset: { symbol: string; estimatedAmount: string; network: string; chainName: string; estimatedValueUSD: string; priceUSD: string; }; exchange: { ...; }; isCrossChain: boolean; }; n...'.
apps/agentic-chat/src/components/tools/GetLimitOrdersUI.tsx
[error] 1-1: TS6196: 'CreateLimitOrderOutput' is declared but never used.
[error] 159-159: TS2339: Property 'summary' does not exist on type '{ summary: { sellAsset: { symbol: string; amount: string; network: string; chainName: string; valueUSD: string; priceUSD: string; }; buyAsset: { symbol: string; estimatedAmount: string; network: string; chainName: string; estimatedValueUSD: string; priceUSD: string; }; exchange: { ...; }; isCrossChain: boolean; }; n...'.
[error] 160-160: TS2339: Property 'summary' does not exist on type '{ summary: { sellAsset: { symbol: string; amount: string; network: string; chainName: string; valueUSD: string; priceUSD: string; }; buyAsset: { symbol: string; estimatedAmount: string; network: string; chainName: string; estimatedValueUSD: string; priceUSD: string; }; exchange: { ...; }; isCrossChain: boolean; }; n...'.
[error] 161-161: TS2339: Property 'summary' does not exist on type '{ summary: { sellAsset: { symbol: string; amount: string; network: string; chainName: string; valueUSD: string; priceUSD: string; }; buyAsset: { symbol: string; estimatedAmount: string; network: string; chainName: string; estimatedValueUSD: string; priceUSD: string; }; exchange: { ...; }; isCrossChain: boolean; }; n...'.
[error] 162-162: TS2339: Property 'summary' does not exist on type '{ summary: { sellAsset: { symbol: string; amount: string; network: string; chainName: string; valueUSD: string; priceUSD: string; }; buyAsset: { symbol: string; estimatedAmount: string; network: string; chainName: string; estimatedValueUSD: string; priceUSD: string; }; exchange: { ...; }; isCrossChain: boolean; }; n...'.
[error] 163-163: TS2339: Property 'summary' does not exist on type '{ summary: { sellAsset: { symbol: string; amount: string; network: string; chainName: string; valueUSD: string; priceUSD: string; }; buyAsset: { symbol: string; estimatedAmount: string; network: string; chainName: string; estimatedValueUSD: string; priceUSD: string; }; exchange: { ...; }; isCrossChain: boolean; }; n...'.
[error] 165-165: TS2339: Property 'summary' does not exist on type '{ summary: { sellAsset: { symbol: string; amount: string; network: string; chainName: string; valueUSD: string; priceUSD: string; }; buyAsset: { symbol: string; estimatedAmount: string; network: string; chainName: string; estimatedValueUSD: string; priceUSD: string; }; exchange: { ...; }; isCrossChain: boolean; }; n...'.
🔇 Additional comments (32)
apps/agentic-chat/src/components/tools/SendUI.tsx (1)
70-70: LGTM! Clean consolidation of loading state.The refactor to use
TxStepCard.AmountwithisLoading={!summary}simplifies the code by delegating loading state handling to the component. This eliminates conditional rendering logic and maintains consistent behavior across the UI.apps/agentic-chat/src/components/ui/TxStepCard.tsx (3)
5-5: LGTM! New imports properly utilized.The imports for
formatCryptoAmountandSkeletonare correctly added and used throughout the updated components.Also applies to: 8-8
127-150: LGTM! Excellent loading state implementation.The updated
TxStepCardSwapPaircomponent properly handles three states:
- Loading: Renders
SkeletonwhenisLoadingis true- Missing data: Renders a dash placeholder when symbols are undefined
- Data available: Renders the swap pair display
This provides consistent UX across different data availability scenarios.
152-176: LGTM! Well-structured amount component with proper formatting.The new
TxStepCardAmountcomponent provides a robust implementation:
- Loading state: Renders
SkeletonwhenisLoadingis true- Missing value: Renders dash placeholder when
valueis undefined- Formatted display: Uses
formatCryptoAmountfor consistent number formatting with symbol- Flexible prefix: Supports optional prefix (e.g., "-" for sends)
- Consistent styling: Uses green color and tabular-nums for amount displays
The early returns ensure type safety, and TypeScript correctly narrows the
valuetype before formatting.apps/agentic-chat/src/components/Portfolio/ActivityRow/components/SwapDetails.tsx (1)
1-66: Clean component extraction with proper event handling.This component correctly extracts the swap details rendering logic with proper handling of optional fields and consistent use of the shared
stopPropagationHandlerutility. The structure aligns well with similar detail components in the codebase.apps/agentic-chat/src/hooks/useSwapExecution.tsx (1)
38-44: SWAP_PHASES mapping is well-structured.The phase mapping correctly covers all actionable steps (QUOTE through SWAP) while excluding the terminal COMPLETE step. The separation allows for clean serialization of step progress.
apps/agentic-chat/src/hooks/useSendExecution.ts (2)
30-36: Consistent phase mapping pattern.The
SEND_PHASESmapping follows the same pattern asSWAP_PHASES, correctly mapping actionable steps while excluding the terminal COMPLETE state. This maintains consistency across execution hooks.
74-82: State hydration is correct.Setting
currentSteptoCOMPLETEwhen hydrating from persisted state is appropriate since persisted transactions represent completed flows. ThecompletedStepsreconstruction viafromPhasescorrectly restores the step completion set.apps/agentic-chat/src/lib/stepUtils.ts (1)
11-36: Well-designed generic phase mapping utility.The
createStepPhaseMapfunction provides a clean bidirectional mapping between step enums and phase strings. The generic constraintTStep extends numbercorrectly accommodates TypeScript enum values. Good use of type guards in the filter operations.apps/agentic-server/src/lib/cow/types.ts (1)
5-72: Type definitions are well-structured.The CoW Protocol types are comprehensive and correctly model the order lifecycle, EIP-712 signing data, and creation parameters. The use of literal types for
kind,class, andstatusprovides good type safety.apps/agentic-chat/src/types/activity.ts (1)
39-70: LGTM!The type definitions are well-structured.
LimitOrderActivityItemcorrectly omitstxHashfromBaseActivityItemsince limit orders identify byorderIdrather than transaction hash. TheestimatedAmountfield appropriately reflects that buy amounts are estimates until the order fills.apps/agentic-chat/src/components/Portfolio/ActivityRow/ActivityRow.tsx (3)
26-35: LGTM!The
formatActivityTitleswitch statement exhaustively handles all activity types in theActivityItemunion. TypeScript will enforce this if new types are added to the union.
37-64: LGTM!The
ActivityDetailscomponent correctly handles the absence oftxHashfor limit orders by conditionally checkingactivity.type !== 'limit_order'. The type-specific detail components are rendered appropriately.
66-85: LGTM!Clean component implementation. The
DrawerListItempattern withexpandedChildrenprovides a consistent expandable UI. Icon selection viaACTIVITY_ICONS[activity.type]is type-safe.apps/agentic-chat/src/hooks/useCancelLimitOrderExecution.tsx (2)
90-109: LGTM!The
submitCancellationfunction correctly sends the DELETE request to CoW API with proper headers and body structure. Error handling extracts the response text for meaningful error messages.
153-226: Execution flow is well-structured with proper step transitions.The multi-step flow correctly handles:
- Wallet validation before proceeding
- Network switching when needed
- EIP-712 signing
- Submission to CoW API
- State persistence on success and error
The error handler properly captures
failedStepviadraft.currentStepbefore the error occurred.apps/agentic-server/src/tools/limitOrder/cancelLimitOrder.ts (5)
9-15: LGTM!The schema is well-structured with clear descriptions and appropriate validation for the cancellation flow.
17-32: LGTM!Type definitions are comprehensive and correctly model the EIP-712 cancellation data structure required by CoW Protocol.
81-90: LGTM!The status checks properly cover all non-cancellable states (fulfilled, cancelled, expired) with clear error messages for each scenario.
92-106: LGTM!The cancellation signing data is correctly prepared using
prepareCowOrderCancellationand properly augmented withprimaryTypefor EIP-712 compliance.
109-128: LGTM!The tool export is well-documented with clear constraints and UI guidance for proper usage in the chat flow.
apps/agentic-server/src/lib/cow/index.ts (6)
1-34: LGTM!Constants are well-documented with clear explanations of their purpose (settlement contracts, vault relayer, default app data).
36-64: LGTM!EIP-712 type fields and domain construction correctly implement the CoW Protocol signing requirements.
70-114: LGTM!The limit order preparation correctly constructs the order parameters and EIP-712 signing data. The receiver fallback to userAddress (line 89) is appropriate.
168-188: LGTM!The
getCowOrdersfunction correctly fetches orders with pagination support and proper error handling.
193-208: LGTM!The
getCowOrderfunction correctly fetches a specific order by ID. Note that error handling doesn't differentiate HTTP status codes, but this is addressed in context of the chain detection function incancelLimitOrder.ts.
214-259: LGTM!The cancellation functions correctly implement CoW Protocol's EIP-712 cancellation flow with proper signing data preparation and API submission.
apps/agentic-server/src/tools/limitOrder/getLimitOrders.ts (5)
30-60: LGTM!The
OrderInfointerface is comprehensive, including enriched data (token symbols, human-readable amounts) as mentioned in past review feedback. TheresolveTokenMetadatahelper properly handles missing token data with null returns.
62-73: LGTM!The
calculateFilledPercentfunction uses BigInt for precise percentage calculation and properly handles the zero sellAmount edge case.
98-106: LGTM!The use of
Promise.allSettledfor parallel chain queries is excellent—it allows fetching from multiple chains concurrently while gracefully handling failures. The try-catch for missing wallets (lines 102-106) ensures chains without connected wallets are skipped without breaking the entire operation.Note: This addresses previous feedback about parallel fetching.
111-140: LGTM!The order enrichment logic is comprehensive:
- Proper filtering by status and class (limit orders only)
- Token metadata resolution with sensible fallbacks (lines 116-119, 128-129)
- Human-readable amounts using
fromBaseUnit- Fill percentage calculation and timestamps
This addresses previous feedback about enriching the output with symbols and decimals.
159-184: LGTM!The tool export provides clear documentation with UI guidance, account scope explanation, and concrete use cases for when to invoke the tool.
abc898f to
35c93c7
Compare
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)
apps/agentic-chat/src/hooks/useSwapExecution.tsx (1)
88-95:failedStepis not persisted or restored, which affects error display after page refresh.The
SwapState.failedStepfield is set when an error occurs (line 324) but is not included in themetaobject during persistence (lines 78-83) or restored during hydration (lines 88-95). ThegetStepStatusfunction explicitly relies onfailedStepto determineStepStatus.FAILED(stepUtils.ts line 48), so after a page refresh, the UI cannot determine which specific step failed—it can only infer fromcompletedStepswhich steps succeeded.Apply this diff to persist and restore
failedStep:function swapStateToPersistedState( toolCallId: string, state: SwapState, conversationId: string, swapOutput: InitiateSwapOutput | null, networkName?: string ): PersistedToolState { const phases = [ ...SWAP_PHASES.toPhases(state.completedSteps, state.error), state.currentStep > SwapStep.APPROVAL && !state.completedSteps.has(SwapStep.APPROVAL) && 'approval_skipped', ].filter(Boolean) as string[] return { toolCallId, toolType: 'swap', conversationId, timestamp: Date.now(), phases, meta: { ...(state.approvalTxHash && { approvalTxHash: state.approvalTxHash }), ...(state.swapTxHash && { swapTxHash: state.swapTxHash }), ...(state.error && { error: state.error }), + ...(state.failedStep !== undefined && { failedStep: state.failedStep }), ...(networkName && { networkName }), }, ...(swapOutput && { toolOutput: swapOutput }), } }function persistedStateToSwapState(persisted: PersistedToolState): SwapState { return { currentStep: SwapStep.COMPLETE, completedSteps: SWAP_PHASES.fromPhases(persisted.phases), approvalTxHash: persisted.meta.approvalTxHash as string | undefined, swapTxHash: persisted.meta.swapTxHash as string | undefined, error: persisted.meta.error as string | undefined, + failedStep: persisted.meta.failedStep as SwapStep | undefined, } }
♻️ Duplicate comments (2)
apps/agentic-server/src/tools/limitOrder/createLimitOrder.ts (1)
221-221:trackingUrlwith placeholder 'pending' may confuse users.The tracking URL is set to
getCowExplorerUrl('pending')which creates a URL likehttps://explorer.cow.fi/orders/pending. This links to a non-existent order page. Consider either omittingtrackingUrluntil a real order ID is available, or documenting that the client should update this URL after order submission.This was flagged in a previous review. Consider making
trackingUrloptional in the output type and setting it toundefinedhere, or returning a generic explorer URL without an order ID.apps/agentic-chat/src/components/tools/GetLimitOrdersUI.tsx (1)
166-166: Hardcoded mainnet tracking URL ignores order's network.The
trackingUrlis hardcoded tohttps://explorer.cow.fi/orders/${orderId}which is the mainnet explorer. For orders on Gnosis or Arbitrum, this will link to the wrong explorer page.Consider using a network-aware URL construction:
const toDisplayOrder = (tx: PersistedToolState): DisplayOrder => { const output = tx.toolOutput as CreateLimitOrderOutput | undefined const orderId = tx.meta.orderId as string + const network = output?.summary?.network ?? 'ethereum' + const networkPath = network === 'ethereum' ? '' : `/${network}` return { orderId, network: output?.summary?.network ?? 'unknown', // ... - trackingUrl: `https://explorer.cow.fi/orders/${orderId}`, + trackingUrl: `https://explorer.cow.fi${networkPath}/orders/${orderId}`, walletAddress: tx.walletAddress, } }
🧹 Nitpick comments (3)
apps/agentic-chat/src/components/tools/CancelLimitOrderUI.tsx (1)
67-68: Consider icon color semantics for cancellation completion.The
XCircleicon turns green whenisCompleteis true (cancellation succeeded). While technically correct (the operation succeeded), users might find a green "X" confusing since cancellation is typically associated with red. Consider usingCheckCirclewith green for success, or keepingXCirclered with a different success indicator.- <XCircle className={`h-5 w-5 ${isComplete ? 'text-green-500' : 'text-red-500'}`} /> - <span className="text-lg font-semibold">{isComplete ? 'Order Cancelled' : 'Cancelling Order'}</span> + {isComplete ? ( + <CheckCircle className="h-5 w-5 text-green-500" /> + ) : ( + <XCircle className="h-5 w-5 text-red-500" /> + )} + <span className="text-lg font-semibold">{isComplete ? 'Order Cancelled' : 'Cancelling Order'}</span>You'd need to add
CheckCircleto the imports fromlucide-react.apps/agentic-server/src/routes/chat.ts (1)
164-187: Consider reducing verbosity of tool argument logging.The
console.logstatements log the fullJSON.stringify(args, null, 2)for each tool call. While helpful for debugging, this could expose user intent/amounts in logs at production level. Consider:
- Using a structured logger with log levels
- Logging only non-sensitive fields (e.g., network, asset symbols but not amounts)
- Redacting sensitive values
This pattern exists for other tools too, so this may be a broader refactor to consider.
apps/agentic-server/src/tools/limitOrder/getLimitOrders.ts (1)
84-96: Consider extracting testnet exclusion to a constant.Line 95 hardcodes the Sepolia testnet ID
11155111. While functional, extracting this to a named constant (e.g.,COW_TESTNET_CHAIN_IDS) would improve maintainability and make the exclusion intent clearer.Apply this diff:
+const COW_TESTNET_CHAIN_IDS = [11155111] // Sepolia + export async function executeGetLimitOrders( input: GetLimitOrdersInput, walletContext?: WalletContext ): Promise<GetLimitOrdersOutput> { // ... } else { chainsToQuery = Object.keys(COW_SUPPORTED_CHAINS) .map(Number) - .filter(id => id !== 11155111) // Exclude testnet + .filter(id => !COW_TESTNET_CHAIN_IDS.includes(id)) }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (33)
apps/agentic-chat/src/components/Portfolio/ActivityList.tsx(2 hunks)apps/agentic-chat/src/components/Portfolio/ActivityRow.tsx(0 hunks)apps/agentic-chat/src/components/Portfolio/ActivityRow/ActivityRow.tsx(1 hunks)apps/agentic-chat/src/components/Portfolio/ActivityRow/components/LimitOrderDetails.tsx(1 hunks)apps/agentic-chat/src/components/Portfolio/ActivityRow/components/SendDetails.tsx(1 hunks)apps/agentic-chat/src/components/Portfolio/ActivityRow/components/SwapDetails.tsx(1 hunks)apps/agentic-chat/src/components/Portfolio/ActivityRow/index.ts(1 hunks)apps/agentic-chat/src/components/toolUIRegistry.tsx(2 hunks)apps/agentic-chat/src/components/tools/CancelLimitOrderUI.tsx(1 hunks)apps/agentic-chat/src/components/tools/GetLimitOrdersUI.tsx(1 hunks)apps/agentic-chat/src/components/tools/InitiateSwapUI.tsx(1 hunks)apps/agentic-chat/src/components/tools/LimitOrderUI.tsx(1 hunks)apps/agentic-chat/src/components/tools/SendUI.tsx(1 hunks)apps/agentic-chat/src/components/ui/TxStepCard.tsx(3 hunks)apps/agentic-chat/src/hooks/useCancelLimitOrderExecution.tsx(1 hunks)apps/agentic-chat/src/hooks/useLimitOrderExecution.tsx(1 hunks)apps/agentic-chat/src/hooks/useSendExecution.ts(4 hunks)apps/agentic-chat/src/hooks/useSwapExecution.tsx(4 hunks)apps/agentic-chat/src/hooks/useToolExecutionEffect.ts(1 hunks)apps/agentic-chat/src/lib/activityNormalizer.ts(3 hunks)apps/agentic-chat/src/lib/cow-config.ts(1 hunks)apps/agentic-chat/src/lib/eventHandlers.ts(1 hunks)apps/agentic-chat/src/lib/stepUtils.ts(1 hunks)apps/agentic-chat/src/stores/chatStore.ts(2 hunks)apps/agentic-chat/src/types/activity.ts(2 hunks)apps/agentic-server/src/index.ts(1 hunks)apps/agentic-server/src/lib/cow/index.ts(1 hunks)apps/agentic-server/src/lib/cow/types.ts(1 hunks)apps/agentic-server/src/routes/chat.ts(4 hunks)apps/agentic-server/src/tools/limitOrder/cancelLimitOrder.ts(1 hunks)apps/agentic-server/src/tools/limitOrder/createLimitOrder.ts(1 hunks)apps/agentic-server/src/tools/limitOrder/getLimitOrders.ts(1 hunks)apps/agentic-server/src/tools/limitOrder/index.ts(1 hunks)
💤 Files with no reviewable changes (1)
- apps/agentic-chat/src/components/Portfolio/ActivityRow.tsx
🚧 Files skipped from review as they are similar to previous changes (14)
- apps/agentic-chat/src/components/tools/SendUI.tsx
- apps/agentic-chat/src/hooks/useToolExecutionEffect.ts
- apps/agentic-server/src/index.ts
- apps/agentic-chat/src/types/activity.ts
- apps/agentic-chat/src/components/Portfolio/ActivityRow/ActivityRow.tsx
- apps/agentic-chat/src/components/Portfolio/ActivityRow/index.ts
- apps/agentic-chat/src/lib/stepUtils.ts
- apps/agentic-chat/src/components/toolUIRegistry.tsx
- apps/agentic-server/src/lib/cow/types.ts
- apps/agentic-chat/src/components/Portfolio/ActivityRow/components/SendDetails.tsx
- apps/agentic-chat/src/hooks/useCancelLimitOrderExecution.tsx
- apps/agentic-server/src/tools/limitOrder/index.ts
- apps/agentic-chat/src/components/tools/InitiateSwapUI.tsx
- apps/agentic-chat/src/components/Portfolio/ActivityRow/components/LimitOrderDetails.tsx
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-22T20:32:10.539Z
Learnt from: kaladinlight
Repo: shapeshift/agentic-chat PR: 76
File: apps/agentic-server/src/mastra/tools/asset/coingecko/constants.ts:1-10
Timestamp: 2025-09-22T20:32:10.539Z
Learning: When cross-app dependencies are identified (like server code importing from chat packages), the user prefers to extract shared constants into dedicated packages in the packages/ directory following the existing pattern of shapeshiftoss/types and shapeshiftoss/utils.
Applied to files:
apps/agentic-chat/src/stores/chatStore.ts
🧬 Code graph analysis (10)
apps/agentic-chat/src/hooks/useSwapExecution.tsx (1)
apps/agentic-chat/src/lib/stepUtils.ts (1)
createStepPhaseMap(12-36)
apps/agentic-chat/src/components/ui/TxStepCard.tsx (3)
apps/agentic-chat/src/components/ui/Skeleton.tsx (1)
Skeleton(7-7)apps/agentic-chat/src/lib/number.ts (1)
formatCryptoAmount(5-5)apps/agentic-chat/src/lib/utils.ts (1)
cn(5-7)
apps/agentic-chat/src/lib/cow-config.ts (1)
apps/agentic-server/src/lib/cow/types.ts (1)
getCowApiUrl(104-117)
apps/agentic-chat/src/hooks/useSendExecution.ts (1)
apps/agentic-chat/src/lib/stepUtils.ts (1)
createStepPhaseMap(12-36)
apps/agentic-server/src/tools/limitOrder/createLimitOrder.ts (8)
packages/caip/src/assetId.ts (1)
fromAssetId(87-106)apps/agentic-server/src/utils/transactionHelpers.ts (1)
createTransaction(17-33)apps/agentic-server/src/lib/cow/types.ts (3)
CowOrderSigningData(48-53)NETWORK_TO_CHAIN_ID(83-87)getCowExplorerUrl(100-102)packages/utils/src/index.ts (1)
toBaseUnit(43-46)apps/agentic-server/src/utils/walletContextSimple.ts (2)
WalletContext(4-6)getAddressForChain(13-26)apps/agentic-server/src/utils/assetHelpers.ts (1)
resolveAsset(8-25)apps/agentic-server/src/lib/cow/index.ts (2)
COW_VAULT_RELAYER_ADDRESS(30-30)prepareCowLimitOrder(70-114)apps/agentic-server/src/utils/getAllowance.ts (1)
getAllowance(24-57)
apps/agentic-chat/src/components/Portfolio/ActivityRow/components/SwapDetails.tsx (7)
apps/agentic-chat/src/types/activity.ts (1)
SwapActivityDetails(9-26)apps/agentic-chat/src/lib/explorers.ts (1)
getExplorerUrl(1-15)apps/agentic-chat/src/components/ui/ToolCard.tsx (1)
ToolCard(71-78)apps/agentic-chat/src/components/ui/Amount.tsx (1)
Amount(88-92)apps/agentic-chat/src/lib/eventHandlers.ts (1)
stopPropagationHandler(4-6)apps/agentic-chat/src/lib/utils.ts (1)
truncateAddress(9-12)apps/agentic-chat/src/components/Portfolio/ActivityRow.tsx (1)
SwapDetails(30-83)
apps/agentic-chat/src/components/tools/GetLimitOrdersUI.tsx (6)
apps/agentic-chat/src/lib/utils.ts (2)
cn(5-7)truncateAddress(9-12)apps/agentic-chat/src/components/ui/Amount.tsx (1)
Amount(88-92)apps/agentic-chat/src/lib/eventHandlers.ts (1)
stopPropagationHandler(4-6)apps/agentic-chat/src/stores/chatStore.ts (1)
PersistedToolState(22-31)apps/agentic-server/src/tools/limitOrder/createLimitOrder.ts (1)
CreateLimitOrderOutput(87-103)apps/agentic-server/src/tools/limitOrder/getLimitOrders.ts (1)
GetLimitOrdersOutput(62-65)
apps/agentic-server/src/tools/limitOrder/cancelLimitOrder.ts (2)
apps/agentic-server/src/lib/cow/types.ts (3)
CowEIP712Domain(37-42)NETWORK_TO_CHAIN_ID(83-87)CHAIN_ID_TO_NETWORK(90-94)apps/agentic-server/src/lib/cow/index.ts (2)
getCowOrder(193-208)prepareCowOrderCancellation(214-231)
apps/agentic-server/src/routes/chat.ts (5)
apps/agentic-server/src/index.ts (3)
createLimitOrderTool(92-92)getLimitOrdersTool(99-99)cancelLimitOrderTool(105-105)apps/agentic-server/src/tools/limitOrder/createLimitOrder.ts (1)
createLimitOrderTool(225-247)apps/agentic-server/src/tools/limitOrder/index.ts (3)
createLimitOrderTool(2-2)getLimitOrdersTool(11-11)cancelLimitOrderTool(19-19)apps/agentic-server/src/tools/limitOrder/getLimitOrders.ts (1)
getLimitOrdersTool(159-184)apps/agentic-server/src/tools/limitOrder/cancelLimitOrder.ts (1)
cancelLimitOrderTool(109-128)
apps/agentic-chat/src/components/tools/CancelLimitOrderUI.tsx (4)
apps/agentic-chat/src/components/tools/toolUIHelpers.tsx (1)
ToolUIComponentProps(6-8)apps/agentic-server/src/tools/limitOrder/cancelLimitOrder.ts (1)
CancelLimitOrderOutput(26-32)apps/agentic-chat/src/hooks/useCancelLimitOrderExecution.tsx (1)
useCancelLimitOrderExecution(111-247)apps/agentic-chat/src/components/ui/TruncateText.tsx (1)
TruncateText(9-26)
⏰ 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: main
🔇 Additional comments (40)
apps/agentic-chat/src/hooks/useSendExecution.ts (4)
32-36: LGTM!The phase mapping is well-structured. The omission of
SendStep.COMPLETEfrom the mapping is correct since it represents a terminal state rather than an actionable phase.
51-72: LGTM!The refactor to use
SEND_PHASES.toPhases()correctly delegates phase serialization to the centralized utility. Error handling is properly integrated via the second parameter.
74-82: LGTM!The hydration logic correctly reconstructs state from persisted phases. Setting
currentSteptoCOMPLETEis appropriate for displaying historical transactions, while error information is preserved separately viafailedStepanderrorfields.
254-270: LGTM with verification pending.The step status computation is well-structured:
- Preparation step status is derived from the AI
toolState(streaming/availability)- Network switch and send steps delegate to
getStepStatusutilityThis separation is appropriate given the different state sources.
apps/agentic-chat/src/hooks/useSwapExecution.tsx (5)
14-14: LGTM: Centralized step utilities improve maintainability.The import refactor consolidates StepStatus and step management utilities from
@/lib/stepUtils, reducing code duplication across different tool execution hooks (swap, send, limit orders).
36-36: LGTM: Re-export maintains backward compatibility.The re-export preserves the public API for consumers importing
StepStatusfrom this hook.
38-44: LGTM: SWAP_PHASES mapping is complete and consistent.The phase mapping correctly covers all execution steps (QUOTE through SWAP) and excludes COMPLETE, which is the terminal state rather than a persisted phase.
67-70: LGTM: Phase construction correctly handles approval skip case.The logic for adding
'approval_skipped'is correct: it's only included when the swap progressed past the approval step without completing it (i.e., whenneedsApprovalwas false).
362-365: LGTM: Consistent use of importedgetStepStatusutility.The refactor correctly delegates step status calculation to the centralized utility, maintaining consistency across different tool execution flows.
apps/agentic-chat/src/components/ui/TxStepCard.tsx (1)
127-176: LGTM! Well-structured loading and empty states.The updated
SwapPairandAmountcomponents handle loading, missing, and present data states cleanly:
- Skeleton shown during loading
- Dash placeholder for missing data
- Formatted output with
formatCryptoAmountwhen data is availableThis provides a consistent and polished UX across transaction steps.
apps/agentic-chat/src/hooks/useLimitOrderExecution.tsx (3)
106-147: LGTM! Clean CoW API submission logic.The
submitSignedOrderfunction correctly:
- Constructs the order payload with all required CoW Protocol fields
- POSTs to the network-specific API endpoint
- Handles errors with descriptive messages
- Strips quotes from the returned orderId
162-176: LGTM! Proper hydration guard prevents duplicate initialization.The
hasHydratedRefandlastToolCallIdRefpattern correctly:
- Resets hydration state when toolCallId changes
- Prevents re-hydration if runtime state already exists
- Fetches persisted state only once per toolCallId
This avoids race conditions and redundant state initialization.
195-328: LGTM! Comprehensive error handling and state persistence.The execution flow properly:
- Validates wallet connection and address consistency
- Steps through each phase with state updates
- Catches errors at any step and persists the failed state
- Shows user-friendly toast notifications for success and failure
apps/agentic-chat/src/components/Portfolio/ActivityList.tsx (1)
15-26: LGTM! Limit order activity integration is clean.The filtering logic correctly includes
limit_orderalongsideswapandsend, and the empty state message accurately reflects all three transaction types.apps/agentic-chat/src/lib/eventHandlers.ts (1)
1-6: LGTM! Simple and reusable event utility.The
stopPropagationHandlerprovides a clean, type-safe way to prevent event bubbling on clickable elements within interactive rows.apps/agentic-chat/src/components/tools/LimitOrderUI.tsx (2)
27-37: LGTM! Proper handling of historical skipped executions.The check for
isHistoricalSkippedcorrectly identifies and displays a message when no persisted data exists for a historical execution, preventing errors downstream.
39-48: LGTM! Defensive guard against incomplete step data.Validating that all six steps are present before rendering prevents undefined access errors and provides a clear error message if step data is malformed.
apps/agentic-chat/src/components/Portfolio/ActivityRow/components/SwapDetails.tsx (1)
1-66: LGTM! Clean extraction of swap detail rendering.The
SwapDetailscomponent properly:
- Renders asset amounts with fiat equivalents using the
Amountcomponent- Conditionally shows fee and approval transaction details
- Uses
stopPropagationHandlerto prevent link clicks from triggering parent row handlers- Generates network-specific explorer URLs via
getExplorerUrlThis extraction improves modularity and follows the pattern established by other activity detail components.
apps/agentic-chat/src/components/tools/CancelLimitOrderUI.tsx (1)
13-124: LGTM!The component follows existing UI patterns consistently, with proper guard clauses for missing data, good separation of computed values, and appropriate use of the shared
TxStepCardprimitives. The historical skipped handling and error states are well-implemented.apps/agentic-chat/src/stores/chatStore.ts (1)
22-31: LGTM!The type extensions to
PersistedToolStateare backward-compatible and properly align with the new limit order tooling. The addition ofwalletAddressenables the history mode functionality inGetLimitOrdersUI.apps/agentic-server/src/tools/limitOrder/createLimitOrder.ts (2)
105-111: LGTM on precision handling.The
calculateBuyAmountfunction correctly usesBigNumberfor all arithmetic, addressing the previous precision concerns. The NaN check viaisNaN()provides appropriate validation.
113-186: LGTM!The
executeCreateLimitOrderfunction is well-structured with clear separation of concerns: asset resolution, chain ID extraction, native token handling, amount calculation, approval checking, and order preparation. The native token validation provides helpful user guidance for wrapping requirements.apps/agentic-server/src/routes/chat.ts (1)
304-310: LGTM!The system prompt additions for limit orders are clear and comprehensive, documenting the gasless nature, supported networks, and appropriate tool usage. This will help the AI assistant guide users correctly.
apps/agentic-chat/src/components/tools/GetLimitOrdersUI.tsx (2)
71-80: LGTM on rate calculation.Good use of
BigNumberfor precision-safe division. The trailing zero removal via regex is a nice touch for cleaner display.
174-253: LGTM!The component cleanly handles both history mode (from persisted transactions) and current mode (from server data). The status validation with fallback, empty state handling, and conditional wallet address display are well-implemented.
apps/agentic-server/src/tools/limitOrder/cancelLimitOrder.ts (6)
1-15: LGTM – Clean schema design with auto-detection.The schema validation and type inference are well-structured. The optional network parameter with fallback to auto-detection provides good developer experience.
17-32: LGTM – Well-defined type surface.The type definitions clearly model the cancellation flow. The
CancellationSigningDatacorrectly includesprimaryTypeas a literal for EIP-712 compliance.
54-70: LGTM – Solid chain resolution logic.The chain ID determination handles both explicit network parameters and auto-detection gracefully with clear error messages.
81-90: LGTM – Comprehensive status validation.The status checks correctly prevent cancellation of fulfilled, already-cancelled, and expired orders with clear user-facing error messages.
92-107: LGTM – Complete cancellation output structure.The signing data preparation and output assembly are correct. The fallback to
'unknown'at line 95 is defensive programming for unsupported chains.
109-128: LGTM – Clear tool description for LLM consumption.The tool description provides excellent guidance on UI behavior, constraints, and usage patterns. The note about gasless EIP-712 signatures is helpful context.
apps/agentic-server/src/tools/limitOrder/getLimitOrders.ts (5)
1-28: LGTM – Well-designed schema with dual data sources.The schema elegantly handles both live API data (
connected) and local history data with clear descriptions. The status enum and optional network filtering provide good flexibility.
32-73: LGTM – Robust token metadata resolution and fill calculation.The
resolveTokenMetadatafunction provides sensible fallbacks, andcalculateFilledPercentcorrectly usesBigIntarithmetic to avoid floating-point precision issues.
98-144: LGTM – Excellent parallel fetching pattern.The use of
Promise.allSettledcorrectly handles per-chain failures without blocking other chains. The token metadata enrichment with address-based fallbacks (lines 128-129) ensures the UI always has displayable data.
146-157: LGTM – Clean result aggregation and sorting.The result filtering and newest-first sorting provide the expected user experience for order lists.
159-184: LGTM – Comprehensive tool documentation.The description provides clear guidance on
accountScopeusage and appropriate invocation contexts, helping the LLM make informed tool choices.apps/agentic-server/src/lib/cow/index.ts (4)
1-51: LGTM – Well-documented CoW Protocol constants.The settlement contract addresses, vault relayer, and EIP-712 type definitions align with CoW Protocol specifications. The documentation clearly explains the purpose of each constant.
70-114: LGTM – Solid limit order preparation logic.The function correctly validates chain support, constructs EIP-712 signing data, and sets sensible defaults. Line 88's
partiallyFillable: trueis appropriate for limit orders (enables better price execution), though future use cases might benefit from making this configurable.
168-208: LGTM – Clean order retrieval functions.Both
getCowOrdersandgetCowOrderimplement standard REST API patterns with appropriate error handling and type safety.
214-259: LGTM – Correct EIP-712 cancellation implementation.The cancellation functions correctly implement CoW Protocol's gasless cancellation workflow with proper EIP-712 signing data structure.
35c93c7 to
8a3a315
Compare
8a3a315 to
8fe318a
Compare
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 (2)
apps/agentic-chat/src/hooks/useSwapExecution.tsx (2)
78-83: PersistfailedStepin transaction metadata.The
metaobject doesn't includefailedStep, which is set during error handling (line 324). Without persisting this field, failed transactions cannot be correctly reconstructed when hydrated from storage, preventing the UI from accurately displaying which step failed.Apply this diff to persist
failedStep:meta: { ...(state.approvalTxHash && { approvalTxHash: state.approvalTxHash }), ...(state.swapTxHash && { swapTxHash: state.swapTxHash }), ...(state.error && { error: state.error }), + ...(state.failedStep !== undefined && { failedStep: state.failedStep }), ...(networkName && { networkName }), },
88-96: Reconstruct failure state correctly when hydrating persisted transactions.The function unconditionally sets
currentSteptoCOMPLETEand doesn't reconstructfailedStep, even when the persisted state contains an error. This causes failed transactions to display as complete when hydrated.Apply this diff to properly reconstruct failed transaction state:
function persistedStateToSwapState(persisted: PersistedToolState): SwapState { + const completedSteps = SWAP_PHASES.fromPhases(persisted.phases) + const failedStep = persisted.meta.failedStep as SwapStep | undefined + const hasError = Boolean(persisted.meta.error) + return { - currentStep: SwapStep.COMPLETE, - completedSteps: SWAP_PHASES.fromPhases(persisted.phases), + currentStep: hasError && failedStep !== undefined ? failedStep : SwapStep.COMPLETE, + completedSteps, approvalTxHash: persisted.meta.approvalTxHash as string | undefined, swapTxHash: persisted.meta.swapTxHash as string | undefined, error: persisted.meta.error as string | undefined, + ...(failedStep !== undefined && { failedStep }), } }
♻️ Duplicate comments (15)
apps/agentic-chat/src/components/Portfolio/ActivityRow/components/SendDetails.tsx (1)
19-21: Don't gate Fee row on truthiness (details.fee &&)—it can hide valid "0 fee" cases.If
feecan ever be0(number) or an empty-ish value, the row won't render. Prefer a nullish check and (optionally) guardfeeSymbolat the same time.Apply this diff to fix the conditional:
- {details.fee && ( - <ToolCard.DetailItem label="Fee" value={<Amount.Crypto value={details.fee} symbol={details.feeSymbol} />} /> - )} + {details.fee != null && details.feeSymbol && ( + <ToolCard.DetailItem + label="Fee" + value={<Amount.Crypto value={details.fee} symbol={details.feeSymbol} />} + /> + )}apps/agentic-chat/src/hooks/useToolExecutionEffect.ts (1)
44-52: Error handling and cleanup concerns remain unaddressed.The previous review comments about (1) missing cleanup/cancellation support and (2) error handling not updating runtime state are still applicable. Currently:
- Errors are only logged to console, leaving the UI unaware of failures
- No
AbortControlleror cleanup function to cancel long-running operations on unmount/re-renderSince this PR focuses on "long running tools" infrastructure, these patterns become more important for limit orders that may take time to process.
apps/agentic-server/src/lib/cow/types.ts (3)
74-95: Inconsistent sepolia support across chain mappings.
COW_SUPPORTED_CHAINSincludes sepolia (chainId 11155111), butNETWORK_TO_CHAIN_IDandCHAIN_ID_TO_NETWORKomit it. This causes inconsistent behavior where:
isCowSupportedChain(11155111)returnstrueCHAIN_ID_TO_NETWORK[11155111]returnsundefinedEither fully support sepolia by adding it to all mappings, or remove it from
COW_SUPPORTED_CHAINSto avoid API-level inconsistency.export const NETWORK_TO_CHAIN_ID: Record<string, number> = { ethereum: 1, gnosis: 100, arbitrum: 42161, + sepolia: 11155111, } export const CHAIN_ID_TO_NETWORK: Record<number, string> = { + 11155111: 'sepolia', }
100-102:getCowExplorerUrlignores chain-specific explorer paths.CoW Explorer uses network-specific paths (e.g.,
explorer.cow.fi/gnosis/orders/...). The current implementation always generates mainnet URLs, which will show incorrect data for orders on other networks.-export function getCowExplorerUrl(orderId: string): string { - return `https://explorer.cow.fi/orders/${orderId}` +export function getCowExplorerUrl(chainId: number, orderId: string): string { + const network = CHAIN_ID_TO_NETWORK[chainId] + const networkPath = network && network !== 'ethereum' ? `${network}/` : '' + return `https://explorer.cow.fi/${networkPath}orders/${orderId}` }
104-117:getCowApiUrlsilently defaults to mainnet for unknown chains.Falling back to mainnet could cause orders to be submitted to the wrong network if called with an unsupported chain ID. Consider throwing an error for unsupported chains to fail fast.
default: - return 'https://api.cow.fi/mainnet' + throw new Error(`Unsupported chain ID for CoW Protocol: ${chainId}`) }apps/agentic-chat/src/lib/activityNormalizer.ts (1)
92-122: Implementation follows established patterns correctly.The type assertion on line 93 and overall structure mirrors
normalizeSwapActivityandnormalizeSendActivity. Good consistency.However, line 110 hardcodes the mainnet explorer URL format, which will produce incorrect links for orders on Gnosis or Arbitrum. Consider using
output.trackingUrldirectly if the server provides the correct chain-aware URL, or construct it with the chainId.- trackingUrl: `https://explorer.cow.fi/orders/${orderId}`, + trackingUrl: output.trackingUrl ?? `https://explorer.cow.fi/orders/${orderId}`,apps/agentic-chat/src/components/tools/LimitOrderUI.tsx (1)
47-54:completedCountcalculation is inconsistent with rendered step statuses when approval is skipped.The stepper (lines 128-137) renders approval steps with
needsApproval ? approvalStep.status : StepStatus.SKIPPED, butcompletedCountuses the raw status values. WhenneedsApprovalis false, the UI shows these steps as SKIPPED (which counts toward completion), butcompletedCountuses the actual status (likelyNOT_STARTED), causing a mismatch in the progress indicator.Apply this diff to compute
completedCountusing the effective statuses:+ const effectiveApprovalStatus = needsApproval ? approvalStep.status : StepStatus.SKIPPED + const effectiveApprovalConfirmStatus = needsApproval ? approvalConfirmStep.status : StepStatus.SKIPPED const completedCount = [ prepareStep.status, networkStep.status, - approvalStep.status, - approvalConfirmStep.status, + effectiveApprovalStatus, + effectiveApprovalConfirmStatus, signStep.status, submitStep.status, ].filter(s => s === StepStatus.COMPLETE || s === StepStatus.SKIPPED).lengthapps/agentic-server/src/lib/cow/index.ts (2)
53-64: Dead code: fallback to ethereum prevents error detection.Line 54 uses
COW_SETTLEMENT_CONTRACT[chainId] ?? COW_SETTLEMENT_CONTRACT[1], which means unsupported chains silently fall back to Ethereum's contract instead of throwing an error. This can lead to incorrect EIP-712 domain data for signatures on unsupported chains.Apply this diff to throw immediately on unsupported chains:
function getEIP712Domain(chainId: number): CowEIP712Domain { - const verifyingContract = COW_SETTLEMENT_CONTRACT[chainId] ?? COW_SETTLEMENT_CONTRACT[1] + const verifyingContract = COW_SETTLEMENT_CONTRACT[chainId] if (!verifyingContract) { throw new Error(`No settlement contract for chain ${chainId}`) }
121-163:fromfield should be the order signer, not receiver.Line 144 sets
from: order.receiver, but the CoW API expects the order owner/signer address in thefromfield. When the receiver differs from the signer (e.g., sending to another address), this will cause the order submission to fail or be rejected.The function signature should accept a
fromparameter for the signer address:export async function submitCowOrder( chainId: number, order: CowOrderQuote, signature: string, - signingScheme: 'eip712' | 'ethsign' = 'eip712' + signingScheme: 'eip712' | 'ethsign' = 'eip712', + from: string ): Promise<string> { // ... const orderPayload = { // ... - from: order.receiver, // owner of the order + from, // owner/signer of the order }apps/agentic-chat/src/hooks/useLimitOrderExecution.tsx (3)
84-92: Hydration always sets currentStep to COMPLETE, losing progress state.Line 86 hardcodes
currentStep: LimitOrderStep.COMPLETEregardless of which phases are present in persisted state. This means hydrated state will always appear complete even if execution failed midway, preventing the UI from showing the actual failed step.Derive currentStep from completed phases or persist it explicitly:
function persistedStateToLimitOrderState(persisted: PersistedToolState): LimitOrderState { + const completedSteps = LIMIT_ORDER_PHASES.fromPhases(persisted.phases) + let currentStep = LimitOrderStep.PREPARE + + if (completedSteps.has(LimitOrderStep.SUBMIT)) { + currentStep = LimitOrderStep.COMPLETE + } else if (completedSteps.has(LimitOrderStep.SIGN)) { + currentStep = LimitOrderStep.SUBMIT + } else if (completedSteps.has(LimitOrderStep.APPROVAL_CONFIRMATION)) { + currentStep = LimitOrderStep.SIGN + } else if (completedSteps.has(LimitOrderStep.APPROVAL)) { + currentStep = LimitOrderStep.APPROVAL_CONFIRMATION + } else if (completedSteps.has(LimitOrderStep.NETWORK_SWITCH)) { + currentStep = LimitOrderStep.APPROVAL + } else if (completedSteps.has(LimitOrderStep.PREPARE)) { + currentStep = LimitOrderStep.NETWORK_SWITCH + } + return { - currentStep: LimitOrderStep.COMPLETE, - completedSteps: LIMIT_ORDER_PHASES.fromPhases(persisted.phases), + currentStep, + completedSteps, orderId: persisted.meta.orderId as string | undefined, approvalTxHash: persisted.meta.approvalTxHash as string | undefined, error: persisted.meta.error as string | undefined, + failedStep: persisted.meta.failedStep as LimitOrderStep | undefined, } }
247-268: Missing publicClient silently skips approval confirmation.Lines 252-257 skip waiting for the transaction receipt when
publicClientis null, but still mark theAPPROVAL_CONFIRMATIONstep as complete. This could lead to race conditions where the approval hasn't been confirmed on-chain when signing begins.Consider logging a warning or throwing an error when publicClient is unavailable:
if (needsApproval && approvalTxHash) { const publicClient = getPublicClient(wagmiConfig, { chainId: orderParams.chainId, }) - if (publicClient) { + if (!publicClient) { + console.warn(`[useLimitOrderExecution] No public client for chain ${orderParams.chainId}, skipping receipt confirmation`) + } else { await publicClient.waitForTransactionReceipt({ hash: approvalTxHash as `0x${string}`, confirmations: 1, }) }
364-364: Hardcoded mainnet tracking URL ignores order's actual network.Line 364 uses
https://explorer.cow.fi/orders/${state.orderId}as a fallback, which assumes mainnet regardless of the order's actual chain. This will produce incorrect links for orders on Gnosis, Arbitrum, or other networks.Rely solely on
orderData?.trackingUrlor construct a network-aware fallback:- trackingUrl: state.orderId ? `https://explorer.cow.fi/orders/${state.orderId}` : orderData?.trackingUrl, + trackingUrl: orderData?.trackingUrl,apps/agentic-server/src/tools/limitOrder/cancelLimitOrder.ts (2)
34-48: Chain detection treats all errors as "not found", masking API failures.Lines 42-44 catch all exceptions from
getCowOrderand treat them as "order not on this chain". This conflates genuine 404s (order doesn't exist) with transient failures (API down, network timeout), leading to misleading error messages when the CoW API is unavailable.Differentiate between 404 (not found) and other errors by checking response status in
getCowOrderor propagating distinct error types, then handle 404s separately in the loop while failing fast on transient errors.
77-79: Avoid echoing fullorder.owneraddress in error messages.Line 78 includes the full
order.owneraddress in the error message. This exposes potentially sensitive wallet address information in logs and client-facing error messages.Mask or omit the owner address:
if (order.owner.toLowerCase() !== userAddress.toLowerCase()) { - throw new Error(`You do not own this order. Order owner: ${order.owner}`) + throw new Error(`You do not own this order. Please check the order ID and try again.`) }apps/agentic-chat/src/hooks/useCancelLimitOrderExecution.tsx (1)
70-76: Hydration losesfailedStepand forcescurrentStepto COMPLETE.Line 72 hardcodes
currentStep: CancelOrderStep.COMPLETEand line 74 doesn't restorefailedStepfrom persisted state. This means after a page refresh, the UI can't show which step failed, and the step indicator will always appear complete even if execution failed midway.Persist and restore both
currentStepandfailedStep:function cancelOrderStateToPersistedState( // ... ): PersistedToolState { return { // ... meta: { ...(orderOutput?.orderId && { orderId: orderOutput.orderId }), ...(state.error && { error: state.error }), + ...(state.failedStep !== undefined && { failedStep: state.failedStep }), + currentStep: state.currentStep, ...(networkName && { networkName }), }, } } function persistedStateToCancelOrderState(persisted: PersistedToolState): CancelOrderState { return { - currentStep: CancelOrderStep.COMPLETE, + currentStep: (persisted.meta.currentStep as CancelOrderStep) ?? CancelOrderStep.COMPLETE, completedSteps: CANCEL_ORDER_PHASES.fromPhases(persisted.phases), error: persisted.meta.error as string | undefined, + failedStep: persisted.meta.failedStep as CancelOrderStep | undefined, } }
🧹 Nitpick comments (7)
apps/agentic-chat/src/components/Portfolio/ActivityRow/components/SwapDetails.tsx (3)
25-27: Remove unnecessary fragments.The empty fragments wrapping
<Amount.Fiat>components serve no purpose and can be removed for cleaner code.Apply this diff:
suffix={ - <> - (<Amount.Fiat value={details.sellAsset.valueUSD} />) - </> + (<Amount.Fiat value={details.sellAsset.valueUSD} />) }suffix={ - <> - (<Amount.Fiat value={details.buyAsset.valueUSD} />) - </> + (<Amount.Fiat value={details.buyAsset.valueUSD} />) }Also applies to: 39-41
48-48: Simplify redundant condition.Since
approvalExplorerUrlis alreadyundefinedwhendetails.approvalis falsy (line 14), checking both conditions is redundant.Apply this diff:
- {details.approval && approvalExplorerUrl && ( + {approvalExplorerUrl && ( <ToolCard.DetailItem
52-60: Consider adding an accessible label for the approval link.The truncated hash alone may not provide sufficient context for screen reader users. Adding an
aria-labelwould improve accessibility.Apply this diff:
<a href={approvalExplorerUrl} target="_blank" rel="noopener noreferrer" + aria-label={`View approval transaction ${details.approval.txHash} on block explorer`} className="font-mono text-sm text-blue-500 hover:text-blue-400 transition-colors" onClick={stopPropagationHandler} >apps/agentic-chat/src/components/tools/CancelLimitOrderUI.tsx (2)
66-70: Consider using a semantically appropriate icon for the completed state.
XCirclewith green color for "Order Cancelled" is semantically confusing sinceXtypically indicates error or failure. Consider usingCheckCirclefor the completed state to better convey success, or keepXCirclebut maintain red color since cancellation is inherently a "stop" action.-import { ExternalLink, XCircle } from 'lucide-react' +import { CheckCircle, ExternalLink, XCircle } from 'lucide-react'- <XCircle className={`h-5 w-5 ${isComplete ? 'text-green-500' : 'text-red-500'}`} /> + {isComplete ? ( + <CheckCircle className="h-5 w-5 text-green-500" /> + ) : ( + <XCircle className="h-5 w-5 text-red-500" /> + )}
73-87: Consider guarding against undefinednetworkNamein the details section.If
orderIdexists butnetworkNameis undefined, the Network detail item will render an empty span. Consider adding a fallback or includingnetworkNamein the guard condition.- {orderId && ( + {orderId && networkName && (Or provide a fallback:
- <TxStepCard.DetailItem label="Network" value={<span className="capitalize">{networkName}</span>} /> + <TxStepCard.DetailItem label="Network" value={<span className="capitalize">{networkName ?? 'Unknown'}</span>} />apps/agentic-chat/src/hooks/useSendExecution.ts (1)
196-207: Minor: Redundant step additions infinalCompletedSteps.
PREPARATIONis already added tostate.completedStepsat line 144, andSENDis added viadraft.completedSteps.add(draft.currentStep)at line 204. The explicit additions at lines 199-200 are redundant since the set already contains these values.- // Build final state with all completed steps - const finalCompletedSteps = new Set(state.completedSteps) - finalCompletedSteps.add(SendStep.PREPARATION) - finalCompletedSteps.add(SendStep.SEND) setState(draft => { draft.sendTxHash = sendTxHash draft.completedSteps.add(draft.currentStep) draft.currentStep = SendStep.COMPLETE draft.error = undefined }) + + // Use the accumulated completedSteps from state + const finalCompletedSteps = new Set(state.completedSteps) + finalCompletedSteps.add(SendStep.SEND)Or simply use
state.completedStepsdirectly infinalStatesince it should already contain all completed steps after thesetStatecall.apps/agentic-chat/src/hooks/useSwapExecution.tsx (1)
67-70: Consider clarifying theapproval_skippedconditional logic.The current implementation uses boolean short-circuit evaluation and relies on
filter(Boolean)to remove false values. While functionally correct, a more explicit approach would improve readability.Apply this diff for clearer intent:
- const phases = [ - ...SWAP_PHASES.toPhases(state.completedSteps, state.error), - state.currentStep > SwapStep.APPROVAL && !state.completedSteps.has(SwapStep.APPROVAL) && 'approval_skipped', - ].filter(Boolean) as string[] + const phases = [ + ...SWAP_PHASES.toPhases(state.completedSteps, state.error), + ...(state.currentStep > SwapStep.APPROVAL && !state.completedSteps.has(SwapStep.APPROVAL) ? ['approval_skipped'] : []), + ]
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (34)
apps/agentic-chat/src/components/Portfolio/ActivityList.tsx(2 hunks)apps/agentic-chat/src/components/Portfolio/ActivityRow.tsx(0 hunks)apps/agentic-chat/src/components/Portfolio/ActivityRow/ActivityRow.tsx(1 hunks)apps/agentic-chat/src/components/Portfolio/ActivityRow/components/LimitOrderDetails.tsx(1 hunks)apps/agentic-chat/src/components/Portfolio/ActivityRow/components/SendDetails.tsx(1 hunks)apps/agentic-chat/src/components/Portfolio/ActivityRow/components/SwapDetails.tsx(1 hunks)apps/agentic-chat/src/components/Portfolio/ActivityRow/index.ts(1 hunks)apps/agentic-chat/src/components/toolUIRegistry.tsx(2 hunks)apps/agentic-chat/src/components/tools/CancelLimitOrderUI.tsx(1 hunks)apps/agentic-chat/src/components/tools/GetLimitOrdersUI.tsx(1 hunks)apps/agentic-chat/src/components/tools/InitiateSwapUI.tsx(3 hunks)apps/agentic-chat/src/components/tools/LimitOrderUI.tsx(1 hunks)apps/agentic-chat/src/components/tools/SendUI.tsx(1 hunks)apps/agentic-chat/src/components/ui/TxStepCard.tsx(3 hunks)apps/agentic-chat/src/hooks/useCancelLimitOrderExecution.tsx(1 hunks)apps/agentic-chat/src/hooks/useLimitOrderExecution.tsx(1 hunks)apps/agentic-chat/src/hooks/useSendExecution.ts(4 hunks)apps/agentic-chat/src/hooks/useSwapExecution.tsx(4 hunks)apps/agentic-chat/src/hooks/useToolExecutionEffect.ts(1 hunks)apps/agentic-chat/src/lib/activityNormalizer.ts(3 hunks)apps/agentic-chat/src/lib/cow-config.ts(1 hunks)apps/agentic-chat/src/lib/eventHandlers.ts(1 hunks)apps/agentic-chat/src/lib/mixpanel.ts(1 hunks)apps/agentic-chat/src/lib/stepUtils.ts(1 hunks)apps/agentic-chat/src/stores/chatStore.ts(2 hunks)apps/agentic-chat/src/types/activity.ts(2 hunks)apps/agentic-server/src/index.ts(1 hunks)apps/agentic-server/src/lib/cow/index.ts(1 hunks)apps/agentic-server/src/lib/cow/types.ts(1 hunks)apps/agentic-server/src/routes/chat.ts(5 hunks)apps/agentic-server/src/tools/limitOrder/cancelLimitOrder.ts(1 hunks)apps/agentic-server/src/tools/limitOrder/createLimitOrder.ts(1 hunks)apps/agentic-server/src/tools/limitOrder/getLimitOrders.ts(1 hunks)apps/agentic-server/src/tools/limitOrder/index.ts(1 hunks)
💤 Files with no reviewable changes (1)
- apps/agentic-chat/src/components/Portfolio/ActivityRow.tsx
🚧 Files skipped from review as they are similar to previous changes (12)
- apps/agentic-chat/src/lib/eventHandlers.ts
- apps/agentic-chat/src/components/Portfolio/ActivityList.tsx
- apps/agentic-chat/src/components/Portfolio/ActivityRow/ActivityRow.tsx
- apps/agentic-chat/src/lib/cow-config.ts
- apps/agentic-server/src/index.ts
- apps/agentic-chat/src/components/tools/InitiateSwapUI.tsx
- apps/agentic-server/src/tools/limitOrder/createLimitOrder.ts
- apps/agentic-chat/src/lib/stepUtils.ts
- apps/agentic-chat/src/components/Portfolio/ActivityRow/components/LimitOrderDetails.tsx
- apps/agentic-chat/src/types/activity.ts
- apps/agentic-chat/src/components/toolUIRegistry.tsx
- apps/agentic-chat/src/components/tools/GetLimitOrdersUI.tsx
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-22T20:32:10.539Z
Learnt from: kaladinlight
Repo: shapeshift/agentic-chat PR: 76
File: apps/agentic-server/src/mastra/tools/asset/coingecko/constants.ts:1-10
Timestamp: 2025-09-22T20:32:10.539Z
Learning: When cross-app dependencies are identified (like server code importing from chat packages), the user prefers to extract shared constants into dedicated packages in the packages/ directory following the existing pattern of shapeshiftoss/types and shapeshiftoss/utils.
Applied to files:
apps/agentic-chat/src/stores/chatStore.ts
🧬 Code graph analysis (11)
apps/agentic-chat/src/components/tools/SendUI.tsx (1)
apps/agentic-chat/src/components/ui/TxStepCard.tsx (1)
TxStepCard(178-191)
apps/agentic-chat/src/components/tools/CancelLimitOrderUI.tsx (7)
apps/agentic-chat/src/components/tools/toolUIHelpers.tsx (1)
ToolUIComponentProps(6-8)apps/agentic-server/src/index.ts (1)
CancelLimitOrderOutput(108-108)apps/agentic-server/src/tools/limitOrder/index.ts (1)
CancelLimitOrderOutput(23-23)apps/agentic-chat/src/stores/chatStore.ts (1)
useChatStore(76-195)apps/agentic-chat/src/hooks/useCancelLimitOrderExecution.tsx (1)
useCancelLimitOrderExecution(112-253)apps/agentic-chat/src/components/ui/TxStepCard.tsx (1)
TxStepCard(178-191)apps/agentic-chat/src/components/ui/TruncateText.tsx (1)
TruncateText(9-26)
apps/agentic-chat/src/components/Portfolio/ActivityRow/components/SendDetails.tsx (5)
apps/agentic-chat/src/types/activity.ts (1)
SendActivityDetails(28-37)apps/agentic-chat/src/components/ui/ToolCard.tsx (1)
ToolCard(71-78)apps/agentic-chat/src/components/ui/Amount.tsx (1)
Amount(88-92)apps/agentic-chat/src/lib/utils.ts (1)
truncateAddress(9-12)apps/agentic-chat/src/components/Portfolio/ActivityRow.tsx (1)
SendDetails(85-99)
apps/agentic-chat/src/lib/activityNormalizer.ts (4)
apps/agentic-chat/src/stores/chatStore.ts (1)
PersistedToolState(22-31)apps/agentic-chat/src/types/activity.ts (1)
LimitOrderActivityDetails(39-52)apps/agentic-server/src/index.ts (1)
CreateLimitOrderOutput(95-95)apps/agentic-server/src/tools/limitOrder/index.ts (1)
CreateLimitOrderOutput(6-6)
apps/agentic-chat/src/hooks/useCancelLimitOrderExecution.tsx (11)
apps/agentic-server/src/tools/limitOrder/cancelLimitOrder.ts (1)
CancelLimitOrderOutput(26-32)apps/agentic-chat/src/lib/stepUtils.ts (2)
createStepPhaseMap(12-36)getStepStatus(47-53)apps/agentic-chat/src/stores/chatStore.ts (2)
PersistedToolState(22-31)useChatStore(76-195)apps/agentic-chat/src/hooks/useSendExecution.ts (1)
StepStatus(30-30)apps/agentic-chat/src/hooks/useSwapExecution.tsx (1)
StepStatus(36-36)apps/agentic-chat/src/lib/cow-config.ts (1)
getCowApiUrl(8-14)apps/agentic-server/src/lib/cow/types.ts (1)
getCowApiUrl(104-117)apps/agentic-chat/src/hooks/useWalletConnection.ts (1)
useWalletConnection(23-48)apps/agentic-chat/src/providers/ChatProvider.tsx (1)
useChatContext(33-39)apps/agentic-chat/src/hooks/useToolExecutionEffect.ts (1)
useToolExecutionEffect(10-60)apps/agentic-chat/src/lib/mixpanel.ts (1)
analytics(8-72)
apps/agentic-chat/src/hooks/useSendExecution.ts (1)
apps/agentic-chat/src/lib/stepUtils.ts (1)
createStepPhaseMap(12-36)
apps/agentic-chat/src/components/tools/LimitOrderUI.tsx (10)
apps/agentic-chat/src/components/tools/toolUIHelpers.tsx (1)
ToolUIComponentProps(6-8)apps/agentic-server/src/index.ts (1)
CreateLimitOrderOutput(95-95)apps/agentic-server/src/tools/limitOrder/index.ts (1)
CreateLimitOrderOutput(6-6)apps/agentic-chat/src/stores/chatStore.ts (1)
useChatStore(76-195)apps/agentic-chat/src/hooks/useLimitOrderExecution.tsx (1)
useLimitOrderExecution(150-366)apps/agentic-chat/src/components/ui/TxStepCard.tsx (1)
TxStepCard(178-191)apps/agentic-chat/src/hooks/useSwapExecution.tsx (1)
StepStatus(36-36)apps/agentic-chat/src/components/ui/Skeleton.tsx (1)
Skeleton(7-7)apps/agentic-chat/src/components/ui/Amount.tsx (1)
Amount(88-92)apps/agentic-chat/src/components/ui/TruncateText.tsx (1)
TruncateText(9-26)
apps/agentic-chat/src/hooks/useSwapExecution.tsx (1)
apps/agentic-chat/src/lib/stepUtils.ts (1)
createStepPhaseMap(12-36)
apps/agentic-server/src/tools/limitOrder/getLimitOrders.ts (4)
apps/agentic-server/src/lib/cow/types.ts (6)
CowOrderStatus(5-5)CHAIN_ID_TO_NETWORK(90-94)CowOrder(7-22)NETWORK_TO_CHAIN_ID(83-87)COW_SUPPORTED_CHAINS(75-80)getCowExplorerUrl(100-102)apps/agentic-server/src/utils/walletContextSimple.ts (2)
WalletContext(4-6)getAddressForChain(13-26)apps/agentic-server/src/lib/cow/index.ts (1)
getCowOrders(168-188)packages/utils/src/index.ts (1)
fromBaseUnit(38-41)
apps/agentic-server/src/lib/cow/index.ts (2)
apps/agentic-server/src/lib/cow/types.ts (10)
CowEIP712Types(44-46)CowEIP712Domain(37-42)CreateCowOrderParams(55-64)CreateCowOrderResult(66-72)isCowSupportedChain(96-98)CowOrderQuote(24-35)CowOrderSigningData(48-53)getCowExplorerUrl(100-102)getCowApiUrl(104-117)CowOrder(7-22)apps/agentic-chat/src/lib/cow-config.ts (1)
getCowApiUrl(8-14)
apps/agentic-chat/src/hooks/useLimitOrderExecution.tsx (9)
apps/agentic-server/src/tools/limitOrder/createLimitOrder.ts (1)
CreateLimitOrderOutput(87-103)apps/agentic-chat/src/lib/stepUtils.ts (2)
createStepPhaseMap(12-36)getStepStatus(47-53)apps/agentic-chat/src/stores/chatStore.ts (2)
PersistedToolState(22-31)useChatStore(76-195)apps/agentic-chat/src/lib/cow-config.ts (1)
getCowApiUrl(8-14)apps/agentic-server/src/lib/cow/types.ts (1)
getCowApiUrl(104-117)apps/agentic-chat/src/hooks/useWalletConnection.ts (1)
useWalletConnection(23-48)apps/agentic-chat/src/providers/ChatProvider.tsx (1)
useChatContext(33-39)apps/agentic-chat/src/hooks/useToolExecutionEffect.ts (1)
useToolExecutionEffect(10-60)apps/agentic-chat/src/lib/mixpanel.ts (1)
analytics(8-72)
🔇 Additional comments (14)
apps/agentic-chat/src/components/Portfolio/ActivityRow/components/SwapDetails.tsx (1)
8-11: Verify ifSwapDetailsPropsshould be exported.The AI summary states that props are "exposed as SwapDetailsProps," but the type is not explicitly exported. If this type is intended for reuse in other components or tests, add an export.
Apply this diff if the type should be exported:
-type SwapDetailsProps = { +export type SwapDetailsProps = { details: SwapActivityDetails network: string }apps/agentic-chat/src/components/Portfolio/ActivityRow/index.ts (1)
1-1: LGTM! Clean barrel export following best practices.The barrel export simplifies imports and supports the refactored directory-based component structure. All imports have been correctly updated to reference the new path.
apps/agentic-chat/src/lib/mixpanel.ts (1)
48-65: LGTM!The new analytics tracking methods follow the established pattern consistently, with appropriate props for each event type and proper
analyticsEnabledguards.apps/agentic-chat/src/components/tools/SendUI.tsx (1)
70-70: LGTM!Good simplification by delegating loading state handling to
TxStepCard.Amountvia theisLoadingprop, which is cleaner than separate conditional branches.apps/agentic-chat/src/stores/chatStore.ts (1)
22-31: LGTM!Clean type expansion to support the new limit order tools. The addition of
walletAddresstoPersistedToolStateenables proper wallet-sensitive display in activity history.apps/agentic-chat/src/components/tools/LimitOrderUI.tsx (1)
66-119: LGTM on the header and details rendering.The conditional rendering with optional chaining, Skeleton loading states, and proper symbol formatting are well implemented. The component handles both loading and error states gracefully.
apps/agentic-chat/src/components/tools/CancelLimitOrderUI.tsx (1)
13-56: LGTM on the component logic and state management.The step extraction, historical skipped handling, and footer message derivation follow the established pattern from LimitOrderUI. The 4-step flow (prepare, network, sign, submit) is correctly implemented without the approval complexity.
apps/agentic-server/src/tools/limitOrder/index.ts (1)
1-24: LGTM on the barrel file structure.Clean re-export hub that aggregates all limit order functionality. Consistent naming convention and proper use of
typekeyword for type-only exports.apps/agentic-chat/src/hooks/useSendExecution.ts (1)
30-36: LGTM on the SEND_PHASES abstraction.The
createStepPhaseMapusage provides a clean, centralized approach to step-phase serialization/deserialization. Re-exportingStepStatusmaintains backward compatibility for consumers.apps/agentic-chat/src/components/ui/TxStepCard.tsx (2)
127-150: LGTM on the SwapPair loading and empty state handling.Good defensive programming with early returns for loading and missing data states. The em dash fallback provides clear visual indication of missing data.
152-176: LGTM on the Amount component refactor.The component now properly handles loading states with Skeleton, undefined values with a dash fallback, and uses
formatCryptoAmountfor consistent number formatting. The optionalprefixprop adds flexibility for rendering contexts that need it.apps/agentic-server/src/routes/chat.ts (1)
28-28: LGTM! Clean integration of limit order tools.The limit order tools are properly imported, wrapped with wallet context, and documented in the system prompt. The integration is consistent with the existing tool patterns.
Also applies to: 115-117, 249-256
apps/agentic-server/src/tools/limitOrder/getLimitOrders.ts (1)
75-157: LGTM! Solid multi-chain order aggregation with proper error handling.The implementation correctly:
- Uses
Promise.allSettledfor parallel per-chain fetches (lines 98-144)- Enriches orders with token metadata and human-readable amounts
- Handles missing wallets gracefully with try-catch (lines 102-106)
- Filters and sorts results appropriately
apps/agentic-chat/src/hooks/useSwapExecution.tsx (1)
362-365: No compatibility issues found betweengetStepStatusandSwapState.The
getStepStatusfunction from@/lib/stepUtilsis compatible withSwapState. The function's genericStepState<TStep>interface requirescurrentStep,completedSteps,error, andfailedStepproperties—all of which are present inSwapStatewith compatible types. The extra properties inSwapState(approvalTxHash,swapTxHash) are not used by the function and do not cause issues.
8fe318a to
55df750
Compare
|
Regarding the CodeRabbit feedback on Error handling updating runtime state: Not necessary - errors ARE already being captured in state. The Add cleanup and cancellation support: Not worth implementing for this context:
|
55df750 to
9b99169
Compare
Summary by CodeRabbit
New Features
New Features (UI)
Improvements
✏️ Tip: You can customize this high-level summary in your review settings.