Skip to content

Conversation

@simcheolhwan
Copy link
Contributor

@simcheolhwan simcheolhwan commented Nov 27, 2025

  • Add getFeeDetails utility and useTxFee hook for consistent fee handling
  • Refactor TxRequest to use new fee calculation API
  • Integrate tx fee selection into auto-sign enable flow
  • Update auto-sign actions to accept fee parameter in enable mutation
  • Add fee denomination persistence and gas adjustment configuration
  • Rename TxMetaItem to TxMeta with composable Item subcomponent
  • Support darker variant for dropdown component in auto-sign UI
  • Fix auto-sign tx status tracking with internal flag (-1 for no notification)
  • Wrap page content with AsyncBoundary for better error handling

Summary by CodeRabbit

  • New Features

    • Expanded fee tooling and UI: selectable fee options, fee estimation/preview, and new fee APIs across send, send‑NFT, bridge and auto‑sign flows.
    • Dropdown gains an optional darker appearance.
    • New TxMeta container; TxFee now requires chain info via props.
  • Bug Fixes

    • Standardized transaction status tracking and polling for more reliable confirmations.
    • Drawer focus handling improved for better accessibility.
  • Style

    • Dropdown trigger lightened; icon color and transitions refined.
    • New compact transaction metadata layout.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Nov 27, 2025

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

Centralizes fee computation with new FeeDetails/getFeeDetails and useTxFee, integrates gas estimation into multiple tx flows and UI components, refactors transaction base types and internal status watching, adjusts Dropdown styling and Drawer focus, and wraps Page content with an AsyncBoundary.

Changes

Cohort / File(s) Summary
Dropdown styling & API
packages/interwovenkit-react/src/components/Dropdown.module.css, packages/interwovenkit-react/src/components/Dropdown.tsx
Trigger background changed (var(--gray-7)var(--gray-5)); added .darker class; expanded .icon transition and hover color; Dropdown gains optional darker?: boolean prop.
Fee utilities & hook
packages/interwovenkit-react/src/data/fee.ts
New FeeDetails interface, exported getFeeDetails({ ... }), and useTxFee hook: derives feeOptions from gasPrices/estimatedGas, manages feeDenom state, exposes getFee.
Transaction base & status
packages/interwovenkit-react/src/data/tx.ts
Added BaseTx (introduces `internal?: boolean
Page async boundary
packages/interwovenkit-react/src/components/Page.tsx
Wrapped Scrollable page content in AsyncBoundary.
TxMeta / TxFee UI changes
packages/interwovenkit-react/src/pages/tx/TxMeta.tsx, packages/interwovenkit-react/src/pages/tx/TxMeta.module.css, packages/interwovenkit-react/src/pages/tx/TxFee.tsx
Added TxMeta wrapper and .meta CSS; export TxMeta.Item = TxMetaItem; TxFee now accepts chain: NormalizedChain, removed internal chain hooks and BigNumber usage, adjusted label/decimal logic.
Auto-sign flow & builders
packages/interwovenkit-react/src/pages/autosign/EnableAutoSign.tsx, packages/interwovenkit-react/src/pages/autosign/data/actions.ts
Added gas estimation and useBuildEnableMessages to build enable messages (feegrant + authz); enable mutation now accepts { durationInMs, fee }; integrated useTxFee/getFeeDetails and fee UI/validation.
Auto-sign query keys / validation
packages/interwovenkit-react/src/pages/autosign/data/queries.ts, packages/interwovenkit-react/src/pages/autosign/data/validation.ts
Renamed query key grantsallGrants; added revokeMessages(chainId, address, grantee) key; updated usages.
Send / Send-NFT flows
packages/interwovenkit-react/src/pages/wallet/txs/send/SendFields.tsx, packages/interwovenkit-react/src/pages/wallet/txs/send-nft/SendNftFields.tsx
Switched to fee-centric flow: added estimatedGas queries, integrated useTxFee/getFeeDetails, compute fee-aware max amount, render TxMeta/TxFee, submit via submitTxSync with computed fee, and add insufficiency checks & aggregated loading states.
Tx request page integration
packages/interwovenkit-react/src/pages/tx/TxRequest.tsx
Replaced local fee logic with exported getFeeDetails({ ... }), switched to TxMeta + TxMeta.Item, and updated TxFee usage to pass chain prop.
Bridge transaction API & preview
packages/interwovenkit-react/src/pages/bridge/data/tx.ts, packages/interwovenkit-react/src/pages/bridge/BridgePreviewFooter.tsx
useBridgeTx(tx, fee?) signature change to accept optional fee: StdFee; Initia path now expects provided fee and uses submitTxSync({ messages, chainId, fee }); added fee-aware footer branching with gas estimation, useTxFee integration, TxFee UI, and insufficiency handling.
Drawer focus behavior
packages/interwovenkit-react/src/public/app/Drawer.tsx
Changed Dialog.Root modal prop from modal={false}modal="trap-focus" and updated inline comments regarding focus confinement and known beta issues.

Sequence Diagram(s)

sequenceDiagram
    participant User as User
    participant UI as UI (Tx form / TxMeta / TxFee)
    participant Hook as useTxFee
    participant Estimator as Gas Estimator (estimateGas)
    participant FeeSvc as getFeeDetails
    participant TxSvc as submitTxSync
    participant Watch as watchTxStatus

    Note over UI,Hook: Fee selection & estimation flow
    User->>UI: configure tx and open fee selector
    UI->>Hook: request fee options (messages, chain, balances, estimatedGas)
    Hook->>Estimator: estimateGas(messages)
    Estimator-->>Hook: estimatedGas
    Hook->>FeeSvc: compute feeOptions & FeeDetails (balances, spendAmount)
    FeeSvc-->>Hook: FeeDetails (isSufficient, options)
    Hook-->>UI: render fee selector and sufficiency state

    Note over UI,TxSvc: Submission flow
    User->>UI: confirm with chosen fee
    UI->>Hook: getFee() -> StdFee
    Hook->>TxSvc: submitTxSync({ messages, chainId, fee })
    TxSvc-->>Watch: txHash, chainId
    Watch->>Watch: poll status
    Watch-->>UI: status updates (pending / success / error)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐇 I hop through code with a cheerful spin,
Fees counted, gas guessed, and wallets grin,
Messages bundled, watchers on the beat,
Transactions stride on nimble rabbit feet,
Hooray — the chain and I both feel complete!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 28.57% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title 'feat(tx-fee): integrate fee calculation' directly summarizes the main objective: integrating fee calculation throughout the codebase via the new getFeeDetails utility and useTxFee hook.

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (8)
packages/interwovenkit-react/src/pages/autosign/data/actions.ts (1)

77-79: Throwing a generic Error() without a message reduces debuggability.

When pendingRequest is null, the thrown error provides no context for debugging.

  if (!pendingRequest) {
-   throw new Error()
+   throw new Error("No pending auto-sign request")
  }
packages/interwovenkit-react/src/pages/wallet/txs/send/SendFields.tsx (1)

120-124: Consider validating fee before form submission is enabled.

The mutation throws if getFee() returns undefined, but the error occurs during submission rather than being prevented by form validation. The isInsufficient check partially addresses this, but if feeOptions is empty (e.g., gas estimation failed), getFee() will return undefined even when the button is enabled.

Consider adding a guard in disabledMessage or the button's disabled state:

  const disabledMessage = useMemo(() => {
    if (!quantity) return "Enter amount"
    if (errors.quantity) return errors.quantity.message
    if (!recipient) return "Enter recipient address"
    if (errors.recipient) return errors.recipient.message
    if (errors.memo) return errors.memo.message
+   if (!getFee()) return "Unable to calculate fee"
    // Destructure error fields in deps to properly track each field change
- }, [quantity, recipient, errors.quantity, errors.recipient, errors.memo])
+ }, [quantity, recipient, errors.quantity, errors.recipient, errors.memo, getFee])
packages/interwovenkit-react/src/pages/tx/TxFee.tsx (1)

18-21: Consider adding a brief inline comment explaining the magic number.

The getDp function returns 8 decimal places when the formatted amount equals "0.000000" (i.e., when the amount is very small). A brief comment clarifying this edge-case handling for dust amounts would improve maintainability.

packages/interwovenkit-react/src/pages/wallet/txs/send-nft/SendNftFields.tsx (2)

91-102: Gas estimation query lacks error handling.

The gasQuery doesn't expose or handle potential errors. If gas estimation fails, the user won't see a meaningful error message, and the form will silently fail at mutation time when getFee() returns undefined.

Consider exposing error from gasQuery and integrating it into disabledMessage:

-  const { data: estimatedGas = 0, isLoading: isLoadingGas } = gasQuery
+  const { data: estimatedGas = 0, isLoading: isLoadingGas, error: gasError } = gasQuery

Then in disabledMessage:

   if (error) return "Route not found"
+  if (gasError) return "Failed to estimate gas"

157-172: TxFee section is rendered even when gas estimation is in progress.

The TxFee component displays immediately, even before estimatedGas is available (while isLoadingGas is true). This could show incorrect fee amounts based on zero gas. Consider conditionally rendering this section:

+        {estimatedGas > 0 && (
         <FormHelp.Stack>
           <TxMeta>
             <TxMeta.Item
               title="Tx fee"
               content={
                 <TxFee
                   chain={srcChain}
                   options={feeOptions}
                   value={feeDenom}
                   onChange={setFeeDenom}
                 />
               }
             />
           </TxMeta>
         </FormHelp.Stack>
+        )}
packages/interwovenkit-react/src/pages/autosign/EnableAutoSign.tsx (1)

61-66: Query key uses array reference directly; may cause unnecessary refetches.

The queryKeys.gas(messages, chainId) uses the messages array directly. Unlike SendNftFields.tsx which uses JSON.stringify, this could cause unstable query keys if messages is a new array reference on each render (from useAutoSignMessages).

Consider using a serialized version for stable query keys:

   const { data: estimatedGas = 0, isLoading: isEstimatingGas } = useQuery({
-    queryKey: queryKeys.gas(messages, chainId).queryKey,
+    queryKey: queryKeys.gas(
+      JSON.stringify(messages, (_, value: unknown) =>
+        typeof value === "bigint" ? value.toString() : value,
+      ),
+      chainId,
+    ).queryKey,
     queryFn: () => estimateGas({ messages, chainId }),
     enabled: !!messages.length,
     staleTime: STALE_TIMES.INFINITY,
   })
packages/interwovenkit-react/src/data/tx.ts (2)

26-33: Consider extracting the magic number -1 into a named constant.

The literal type -1 for disabling notifications works, but a named constant would improve readability and make the intent clearer at call sites.

+/** Sentinel value to disable tx status notifications */
+export const TX_INTERNAL_NO_NOTIFICATION = -1 as const
+
 export interface BaseTx {
   messages: EncodeObject[]
   memo?: string
   chainId?: string

   /** Internal use only */
-  internal?: boolean | string | -1 // -1 for disabling notification
+  internal?: boolean | string | typeof TX_INTERNAL_NO_NOTIFICATION
 }

173-182: The .catch() handler discards the error details.

When waitForTxConfirmation fails (e.g., timeout), the error status is set without the actual error object, unlike the catch blocks in requestTxSync and submitTxSync which preserve the error via error: error as Error.

   const watchTxStatus = (txHash: string, chainId: string) => {
     setTxStatus({ txHash, chainId, status: "loading" })
     waitForTxConfirmation({ txHash, chainId })
       .then((tx) => {
         setTxStatus({ status: tx.code === 0 ? "success" : "error", chainId, txHash })
       })
-      .catch(() => {
-        setTxStatus({ status: "error", chainId, txHash })
+      .catch((error) => {
+        setTxStatus({ status: "error", chainId, txHash, error: error as Error })
       })
   }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to Reviews -> Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between 02307bf and d9227f7.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (16)
  • packages/interwovenkit-react/package.json (2 hunks)
  • packages/interwovenkit-react/src/components/Dropdown.module.css (2 hunks)
  • packages/interwovenkit-react/src/components/Dropdown.tsx (2 hunks)
  • packages/interwovenkit-react/src/components/Page.tsx (2 hunks)
  • packages/interwovenkit-react/src/data/fee.ts (2 hunks)
  • packages/interwovenkit-react/src/data/tx.ts (4 hunks)
  • packages/interwovenkit-react/src/pages/autosign/EnableAutoSign.tsx (6 hunks)
  • packages/interwovenkit-react/src/pages/autosign/data/actions.ts (4 hunks)
  • packages/interwovenkit-react/src/pages/bridge/data/tx.ts (1 hunks)
  • packages/interwovenkit-react/src/pages/tx/TxFee.tsx (1 hunks)
  • packages/interwovenkit-react/src/pages/tx/TxMeta.module.css (1 hunks)
  • packages/interwovenkit-react/src/pages/tx/TxMeta.tsx (2 hunks)
  • packages/interwovenkit-react/src/pages/tx/TxRequest.tsx (5 hunks)
  • packages/interwovenkit-react/src/pages/wallet/txs/send-nft/SendNftFields.tsx (5 hunks)
  • packages/interwovenkit-react/src/pages/wallet/txs/send/SendFields.tsx (6 hunks)
  • packages/interwovenkit-react/src/public/app/Drawer.tsx (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (6)
packages/interwovenkit-react/src/pages/wallet/txs/send/SendFields.tsx (5)
packages/interwovenkit-react/src/public/data/hooks.ts (1)
  • useInterwovenKit (51-93)
packages/interwovenkit-react/src/data/account.ts (1)
  • useBalances (43-47)
packages/interwovenkit-react/src/data/assets.ts (1)
  • useFindAsset (76-88)
packages/interwovenkit-react/src/data/fee.ts (2)
  • useTxFee (136-161)
  • getFeeDetails (36-59)
packages/interwovenkit-react/src/pages/tx/TxMeta.tsx (1)
  • TxMeta (14-16)
packages/interwovenkit-react/src/pages/tx/TxFee.tsx (1)
packages/interwovenkit-react/src/data/chains.ts (1)
  • NormalizedChain (34-34)
packages/interwovenkit-react/src/pages/autosign/EnableAutoSign.tsx (4)
packages/interwovenkit-react/src/public/data/hooks.ts (1)
  • useInterwovenKit (51-93)
packages/interwovenkit-react/src/pages/autosign/data/actions.ts (2)
  • useEnableAutoSign (100-158)
  • useAutoSignMessages (70-97)
packages/interwovenkit-react/src/data/http.ts (1)
  • STALE_TIMES (4-8)
packages/interwovenkit-react/src/data/fee.ts (1)
  • useTxFee (136-161)
packages/interwovenkit-react/src/pages/wallet/txs/send-nft/SendNftFields.tsx (4)
packages/interwovenkit-react/src/public/data/hooks.ts (1)
  • useInterwovenKit (51-93)
packages/interwovenkit-react/src/data/http.ts (1)
  • STALE_TIMES (4-8)
packages/interwovenkit-react/src/data/fee.ts (1)
  • useTxFee (136-161)
packages/interwovenkit-react/src/pages/tx/TxMeta.tsx (1)
  • TxMeta (14-16)
packages/interwovenkit-react/src/pages/tx/TxRequest.tsx (2)
packages/interwovenkit-react/src/data/fee.ts (1)
  • getFeeDetails (36-59)
packages/interwovenkit-react/src/pages/tx/TxMeta.tsx (1)
  • TxMeta (14-16)
packages/interwovenkit-react/src/pages/autosign/data/actions.ts (5)
packages/interwovenkit-react/src/public/data/hooks.ts (1)
  • useInitiaAddress (16-20)
packages/interwovenkit-react/src/pages/autosign/data/wallet.ts (1)
  • useEmbeddedWalletAddress (20-23)
packages/interwovenkit-react/src/pages/autosign/data/validation.ts (1)
  • useAutoSignMessageTypes (23-45)
packages/interwovenkit-react/src/pages/autosign/data/store.ts (1)
  • pendingAutoSignRequestAtom (9-9)
packages/interwovenkit-react/src/data/tx.ts (1)
  • useTx (69-303)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Cloudflare Pages: interwovenkit-testnet
  • GitHub Check: Cloudflare Pages: interwovenkit-testnet
🔇 Additional comments (30)
packages/interwovenkit-react/src/public/app/Drawer.tsx (1)

83-90: modal="trap-focus" change looks consistent with the documented behavior, but keep it guarded by tests.

The switch from modal={false} to modal="trap-focus" matches the intent described in the comments (keep focus inside the drawer and avoid Tab closing it). Given the historical Base UI bugs you reference, I’d strongly recommend codifying this in regression tests (or at least a simple Cypress/playwright check) to ensure that in future Base UI bumps:

  • Backdrop clicks still close the drawer and invoke handleCloseDrawer correctly.
  • Focus never escapes the drawer on Tab/Shift+Tab.
  • AssetActions menus still close on outside click and don’t stack.

Functionally this change looks good from this file’s perspective; the main risk is future library regressions, so tests or a checklist around Base UI upgrades would help.

If you want, I can sketch a small e2e test scenario covering Tab navigation and outside-click close for this drawer.

packages/interwovenkit-react/src/components/Page.tsx (2)

3-3: Perfect! I've completed the verification. All components are properly implemented. Let me now generate the final rewritten review comment:


AsyncBoundary component exists with proper error handling and fallback UI implementation.

The AsyncBoundary component (packages/interwovenkit-react/src/components/AsyncBoundary.tsx) is correctly implemented with:

  • ErrorBoundary from react-error-boundary for error handling
  • Suspense for async/lazy loading fallback
  • Default fallback UIs: Status component showing error messages or "Loading..." state
  • Configurable props for customization (errorBoundaryProps, suspenseFallback)

The import statement at line 3 is correct and the component is properly available for use in Page.tsx.


28-30: AsyncBoundary wrapper pattern is sound—no conflicts detected.

The AsyncBoundary component is a transparent wrapper (ErrorBoundary + Suspense) that doesn't interfere with Scrollable's scroll functionality or styling. Scrollable is a simple div wrapper with CSS flex and overflow properties. The combination is safe and follows established React patterns already used elsewhere in the codebase (e.g., Drawer.tsx). The error fallback UI (Status component) is consistent with other AsyncBoundary implementations throughout the application. No verification issues identified.

packages/interwovenkit-react/package.json (1)

33-33: Based on my thorough verification of the codebase, I can now provide the rewritten review comment:


No action required—the version upgrade is compatible with the current codebase.

The concerns raised about breaking changes between beta.3 and beta.6 do not apply to this PR. The Dropdown component was newly created in this commit with beta.6 already specified as a dependency, and no deprecated props (like trackAnchor or alwaysSubmitOnEnter) are used anywhere in the codebase. AsyncBoundary does not depend on base-ui-components. All base-ui-components usage throughout the codebase (Select, Dialog, Popover, Menu, Tabs, Collapsible, Separator) uses the correct beta.6 API.

Likely an incorrect or invalid review comment.

packages/interwovenkit-react/src/components/Dropdown.tsx (2)

22-22: LGTM: Optional prop properly typed and destructured.

The darker prop is correctly added as an optional boolean and properly destructured in the component signature.

Also applies to: 29-29


45-47: Visual impact of lighter default Dropdown background is limited to one usage location.

The CSS changes are confirmed: .trigger now defaults to var(--gray-5) (lighter), while the .darker class restores var(--gray-7). However, only one existing Dropdown usage will be visually affected:

  • TxFee.tsx (line 54): No darker prop → renders with new lighter background
  • EnableAutoSign.tsx (line 141): Explicitly uses darker prop → maintains previous darker appearance

The code implementation is correct. Manual visual verification of TxFee.tsx is recommended to confirm the lighter background is intentional.

packages/interwovenkit-react/src/components/Dropdown.module.css (2)

14-20: Clarify hover behavior consistency.

The .darker class includes a background-color hover effect (transitions to var(--gray-6)), but the default .trigger class (lines 1-12) does not define a hover background-color. Is this intentional, or should the default trigger also have a hover state for consistency?


22-35: LGTM: Enhanced icon styling with smooth transitions.

The icon styling updates are well-implemented:

  • Expanded transition covers both color and transform for smooth animations
  • Hover state provides clear visual feedback by changing icon color to var(--gray-1)
  • Preserved rotation behavior on popup open
packages/interwovenkit-react/src/pages/autosign/data/actions.ts (4)

23-67: LGTM! Clean extraction of message building logic.

The buildAutoSignMessages helper properly encapsulates the construction of feegrant and authz messages. The use of a typed interface for parameters improves maintainability.


70-97: useMemo for now prevents recalculation but may cause stale expiration.

The memoized now value will persist across re-renders, which is correct for preventing expiration drift during a single session. However, if durationInMs changes significantly after the initial render, the expiration will be calculated from the original timestamp.

Also note that this hook throws synchronously if pendingRequest is null (line 78), which violates React's rules of hooks if the condition can change between renders. Ensure callers only invoke this hook when a pending request exists.


160-209: No issue found: The asymmetry between requestTxBlock and submitTxBlock is intentional and correct.

The two functions serve different purposes by design:

  • submitTxBlock requires an explicit fee parameter (type TxParams) because it's used for user-initiated transactions where the user explicitly chooses and pays the fee (e.g., enabling AutoSign with user-selected fee).
  • requestTxBlock accepts TxRequest which has optional gas-related fields (gas, gasAdjustment, gasPrices, spendCoins) but no explicit fee, as it's designed for internal/admin operations where fees are auto-calculated from gas parameters (e.g., revoking AutoSign as an internal operation).

Using requestTxBlock for useDisableAutoSign is the correct choice since disabling AutoSign is an internal system operation that doesn't require user-specified fees. This is not a bug or oversight.


119-141: Based on my verification, I've confirmed the breaking change and validated that all call sites have been properly updated. Here's the rewritten review comment:


Breaking change in mutation signature has been completed correctly.

The mutation function signature was changed from async (durationInMs: number) to async ({ durationInMs, fee }: { durationInMs: number; fee: StdFee }). The only call site in EnableAutoSign.tsx (line 89) has been correctly updated to pass both parameters: mutate({ durationInMs: duration, fee }). The migration is complete.

packages/interwovenkit-react/src/pages/tx/TxMeta.module.css (1)

1-9: LGTM! Clean CSS for the new TxMeta wrapper.

The .meta class provides consistent styling using existing CSS variables (--gray-7) and establishes a clear visual container for transaction metadata.

packages/interwovenkit-react/src/pages/bridge/data/tx.ts (1)

96-107: Verify the semantic meaning of internal: -1 for bridge transactions.

The internal flag was changed from 1 to -1. Per the PR summary, -1 indicates "no notification" for transaction status tracking. This change affects how Initia bridge transactions are handled in the notification system.

Ensure this is the intended behavior—bridge transactions will no longer trigger internal status notifications, likely because useBridgeTx handles its own notification flow (lines 162-166, 200, 214-218).

packages/interwovenkit-react/src/pages/wallet/txs/send/SendFields.tsx (3)

70-87: LGTM! Fee calculation logic is well-structured.

The useTxFee hook integration and the helper functions getSpendAmount and calcFeeDetails properly handle the case where the sending token matches the fee token, ensuring accurate balance sufficiency checks.


185-201: LGTM! Clean integration of fee UI components.

The TxMeta and TxFee components are properly integrated, providing users with visibility and control over fee denomination selection. The isInsufficient error message is appropriately placed.


90-100: Based on my verification, I can now provide the rewritten review comment:


Confirm edge case where empty feeOptions causes incorrect maxAmount calculation.

The concern is valid. When feeOptions is empty (e.g., for deleted chains with fee_tokens: []), the calculation feeOptions.find(...)?.[...]?.amount ?? "0" correctly defaults feeAmount to "0". However, this causes maxAmount to return the full balance without fee deduction.

While the transaction submission includes a guard (if (!fee) throw new Error("Fee not found") at line 116), the maxAmount calculation is misleading: users see a max amount they can send, but the transaction will fail at submission if no fee is available. Consider explicitly handling the empty feeOptions case—either by validating upstream that chains used for sending have at least one fee token, or by having maxAmount return "0" when feeOptions is empty to prevent user confusion.

packages/interwovenkit-react/src/pages/tx/TxMeta.tsx (1)

14-20: Correct the breaking change concern – this is not a breaking change.

The refactoring to TxMeta with TxMeta.Item as a subcomponent follows React's compound component pattern well and is properly implemented. Verification confirms:

  • TxMetaItem was never directly exported/imported; it's an internal component within TxMeta.tsx
  • All usages across the codebase (TxRequest.tsx, SendFields.tsx, SendNftFields.tsx, EnableAutoSign.tsx) correctly use the new TxMeta.Item pattern
  • No direct imports of TxMetaItem exist elsewhere in the codebase

This is not a breaking change since TxMetaItem was never part of the public API.

packages/interwovenkit-react/src/pages/tx/TxFee.tsx (1)

8-15: LGTM! Clean refactor to accept explicit chain prop.

The change to accept chain as a prop instead of internal resolution improves component reusability and aligns with the broader fee-handling refactor across the codebase.

packages/interwovenkit-react/src/data/fee.ts (4)

18-26: LGTM! Well-structured FeeDetails interface.

The interface cleanly captures all necessary fee-related information for UI rendering and validation, with clear field semantics.


36-59: Clean utility function with proper null safety.

The getFeeDetails function correctly handles missing balances/fees by defaulting to "0" and uses BigNumber for safe arithmetic. The optional spend (null when not positive) is a nice touch for conditional rendering.


148-156: Potential stale closure: getInitialFeeDenom may not react to feeOptions updates.

getInitialFeeDenom captures feeOptions from the outer scope, but since it's passed as a lazy initializer to useState, it only runs on the first render. If gasPrices or gas changes (causing feeOptions to update), feeDenom won't be re-initialized.

This is likely acceptable if estimatedGas is stable after initial computation, but worth noting for future maintainers.


158-158: getFee returns undefined when feeDenom doesn't match any option.

If feeDenom state becomes stale (e.g., if feeOptions changes and no longer contains the current feeDenom), getFee() returns undefined. Callers must handle this case, which they do via if (!fee) throw new Error("Fee not found").

This is correctly handled at call sites, but consider adding a fallback or logging if this edge case occurs unexpectedly.

packages/interwovenkit-react/src/pages/autosign/EnableAutoSign.tsx (1)

190-203: LGTM! TxMeta fee display integration is clean.

The fee UI is properly integrated into the footer section, consistent with other transaction flows in this PR.

packages/interwovenkit-react/src/pages/tx/TxRequest.tsx (3)

46-58: Clean abstraction: local helpers wrap shared utility.

The getSpendAmount and calcFeeDetails helpers provide a clean interface over the shared getFeeDetails utility, properly injecting local context (balances, feeOptions, findAsset). This maintains consistency with the centralized fee calculation while adapting to component-specific needs.


60-73: Well-designed initial fee denom selection with sufficiency preference.

The logic correctly prioritizes:

  1. Last used fee denom (if sufficient)
  2. First sufficient fee denom from options
  3. Falls back to first fee denom (even if insufficient)

This provides a good UX by preferring user's previous choice while ensuring a usable default.


103-111: LGTM! Consistent use of TxMeta.Item and updated TxFee with chain prop.

The refactor to use TxMeta.Item and pass chain to TxFee aligns with the pattern established across other components in this PR.

packages/interwovenkit-react/src/data/tx.ts (3)

35-44: LGTM!

Clean interface inheritance pattern. The separation between TxRequest (pre-fee-calculation) and TxParams (with calculated fee) is a good abstraction.


196-206: LGTM!

The typeof txRequest.internal !== "number" check correctly excludes the -1 sentinel value while allowing both true (boolean) and string paths to trigger status updates.


231-247: LGTM!

The sequencing is correct: when internal is a string, both status watching and navigation occur. The error handling properly distinguishes between status updates (raw error) and thrown errors (formatted).

alecande11
alecande11 previously approved these changes Nov 27, 2025
@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Nov 30, 2025

Deploying interwovenkit-testnet with  Cloudflare Pages  Cloudflare Pages

Latest commit: 81581a7
Status: ✅  Deploy successful!
Preview URL: https://04f3acac.interwovenkit-testnet.pages.dev
Branch Preview URL: https://feat-tx-fee.interwovenkit-testnet.pages.dev

View logs

@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Nov 30, 2025

Deploying interwovenkit with  Cloudflare Pages  Cloudflare Pages

Latest commit: 81581a7
Status: ✅  Deploy successful!
Preview URL: https://ed359bdc.interwovenkit.pages.dev
Branch Preview URL: https://feat-tx-fee.interwovenkit.pages.dev

View logs

@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Nov 30, 2025

Deploying interwovenkit-staging with  Cloudflare Pages  Cloudflare Pages

Latest commit: 81581a7
Status: ✅  Deploy successful!
Preview URL: https://e985ddaf.interwovenkit-staging.pages.dev
Branch Preview URL: https://feat-tx-fee.interwovenkit-staging.pages.dev

View logs

Copy link

@coderabbitai coderabbitai bot left a 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

♻️ Duplicate comments (1)
packages/interwovenkit-react/src/pages/wallet/txs/send-nft/SendNftFields.tsx (1)

106-113: Zero-gas fee calculation during loading.

This issue was flagged in a previous review: useTxFee is called with estimatedGas = 0 before gas estimation completes, which will compute fees with zero gas. While the button is disabled during loading, the fee display may show incorrect values momentarily.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to Reviews -> Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between d9227f7 and 7e44fcf.

📒 Files selected for processing (2)
  • packages/interwovenkit-react/src/data/fee.ts (2 hunks)
  • packages/interwovenkit-react/src/pages/wallet/txs/send-nft/SendNftFields.tsx (5 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
packages/interwovenkit-react/src/data/fee.ts (2)
packages/interwovenkit-react/src/data/chains.ts (1)
  • NormalizedChain (34-34)
packages/interwovenkit-react/src/public/data/constants.ts (1)
  • DEFAULT_GAS_ADJUSTMENT (3-3)
⏰ 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: Cloudflare Pages: interwovenkit-testnet
🔇 Additional comments (7)
packages/interwovenkit-react/src/data/fee.ts (3)

18-26: LGTM!

The FeeDetails interface is well-structured with clear property names and appropriate types for representing fee calculation results.


36-59: LGTM!

The getFeeDetails function correctly computes fee details with proper null-safe access patterns. The logic for calculating total required amount and sufficiency is sound.


136-161: Well-structured hook with clear responsibilities.

The useTxFee hook cleanly encapsulates gas price fetching, fee calculation, and denomination selection. The memoization of feeOptions with appropriate dependencies is correct.

packages/interwovenkit-react/src/pages/wallet/txs/send-nft/SendNftFields.tsx (4)

94-104: LGTM!

The gas estimation query correctly guards execution with enabled: !!messages and uses appropriate stale time. The JSON.stringify with bigint handler is a pragmatic solution for creating stable query keys from message objects.


116-126: LGTM!

The mutation correctly validates that both messages and fee exist before submission, with appropriate error throwing for missing values. The query invalidation on success ensures UI consistency.


165-181: LGTM!

The fee display UI is cleanly integrated with proper error messaging for insufficient balances and a composable structure using TxMeta and TxFee components.


184-194: LGTM!

The button correctly handles multiple loading states with informative messages and properly disables submission when fees are insufficient or validation fails.

Copy link

@coderabbitai coderabbitai bot left a 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

♻️ Duplicate comments (1)
packages/interwovenkit-react/src/pages/autosign/EnableAutoSign.tsx (1)

93-96: Avoid throwing in handleEnable and rely fully on the disabled/loading guards

handleEnable still throws synchronously when getFee() returns undefined, even though isEnableDisabled already includes !getFee() in its condition and the button is also gated by account/fee checks. This mirrors the concern from the previous review and can still surface as an uncaught error if state changes between render and click. Instead, treat the missing‑fee case as a no‑op (or surface a user‑friendly error) and let the disabled state remain the single source of truth.

For example:

 const handleEnable = () => {
   const fee = getFee()
-  if (!fee) throw new Error("Fee not found")
-  mutate({ durationInMs: duration, fee })
+  if (!fee) return // Should be prevented by disabled state; defensive guard
+  mutate({ durationInMs: duration, fee })
 }

Also, confirm that Button.White cannot actually invoke onClick while loading={isEstimatingGas || isPending} is true; if it can, consider folding isEstimatingGas into isEnableDisabled as well to avoid acting on a pre‑estimation fee.

#!/bin/bash
# Inspect Button.White behavior to confirm whether loading implies disabled
fd 'Button\.tsx' -t f | xargs -I{} sed -n '1,260p' {}

Also applies to: 105-105, 217-218

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to Reviews -> Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between 7e44fcf and 7880f8c.

📒 Files selected for processing (1)
  • packages/interwovenkit-react/src/pages/autosign/EnableAutoSign.tsx (7 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
packages/interwovenkit-react/src/pages/autosign/EnableAutoSign.tsx (9)
examples/vite/src/data.ts (1)
  • chainId (6-6)
packages/interwovenkit-react/src/public/data/hooks.ts (1)
  • useInterwovenKit (51-93)
packages/interwovenkit-react/src/pages/autosign/data/actions.ts (2)
  • useEnableAutoSign (100-158)
  • useAutoSignMessages (70-97)
packages/interwovenkit-react/src/data/ui.ts (1)
  • useDrawer (9-28)
packages/interwovenkit-react/src/data/http.ts (1)
  • STALE_TIMES (4-8)
packages/interwovenkit-react/src/data/account.ts (1)
  • useBalances (43-47)
packages/interwovenkit-react/src/data/assets.ts (1)
  • useFindAsset (76-88)
packages/interwovenkit-react/src/data/fee.ts (2)
  • useTxFee (136-161)
  • getFeeDetails (36-59)
packages/interwovenkit-react/src/pages/tx/TxMeta.tsx (1)
  • TxMeta (14-16)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Cloudflare Pages: interwovenkit-testnet
🔇 Additional comments (2)
packages/interwovenkit-react/src/pages/autosign/EnableAutoSign.tsx (2)

14-23: Imports and query key setup look consistent

The new data/fee imports and the queryKeys definition (including the gas key) are wired coherently and match how createQueryKeys is typically used alongside useQuery. No issues from a structure/typing perspective.

Also applies to: 28-38


150-150: Footer UX changes (dropdown styling, insufficient-fee message, and TxMeta/TxFee) look good

Passing darker into Dropdown and adding the “Insufficient balance for fee” message when either the account is missing or isInsufficient is true aligns the UI with the new fee logic, and embedding TxFee inside TxMeta.Item cleanly exposes fee selection/visibility in the footer. Assuming TxFee is already used similarly in other flows, this is consistent and maintainable.

Also applies to: 184-210

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
packages/interwovenkit-react/src/data/fee.ts (1)

122-165: feeDenom can be undefined when no fee options exist (type says string)

In useTxFee, initialFeeDenom can be undefined:

  • Line 153: return feeOptions[0]?.amount[0]?.denom
  • If gasPrices comes back empty (e.g., API/chain misconfig, transient failure, or unusual chain config), feeOptions.length === 0, so feeOptions[0] is undefined and initialFeeDenom becomes undefined.

Then:

  • Line 156: const [feeDenom, setFeeDenom] = useState(initialFeeDenom) → state type is string | undefined at runtime.
  • Line 164: return { ..., feeDenom, ... } but UseTxFeeResult (Line 131) declares feeDenom: string.

So at runtime feeDenom may be undefined while the public type says it is always a string, and downstream consumers may not handle the “no fee options” case correctly. This matches the concern raised in the previous review comment about the initial fee denom.

Consider either:

  1. Widen the public type and state to reflect reality (and handle it in consumers):

interface UseTxFeeResult {
gasPrices: Coin[]
gas: number
feeOptions: StdFee[]

  • feeDenom: string
  • setFeeDenom: (denom: string) => void
  • feeDenom: string | undefined
  • setFeeDenom: (denom: string | undefined) => void
    getFee: () => StdFee | undefined
    }
    
    And make the state explicit:
    
    ```diff
    
  • const [feeDenom, setFeeDenom] = useState(initialFeeDenom)
  • const [feeDenom, setFeeDenom] = useState<string | undefined>(initialFeeDenom)
    
    
  1. Or guarantee a non-undefined fallback when fee options are empty, e.g. by:
    • Falling back to chain.fees.fee_tokens[0]?.denom if available, or
    • Throwing/early-returning a clear error when feeOptions.length === 0 so consumers never see an inconsistent state.

Either approach removes the mismatch between the runtime value and the declared UseTxFeeResult type and makes empty-gas-price situations explicit rather than silently returning undefined.

🧹 Nitpick comments (1)
packages/interwovenkit-react/src/data/fee.ts (1)

18-59: Defensively handle StdFee.amount shape and tighten spendAmount typing

Overall shape of FeeDetails and getFeeDetails looks good and matches how the rest of the code uses it, but there are two small robustness/type nits:

  1. On Line 45, fee.amount[0].denom will throw if any StdFee has an empty amount array:

    feeOptions.find((fee) => fee.amount[0].denom === feeDenom)

    Even if current callers always use calculateFee (which returns a single-coin fee), this helper is exported and could be reused with other StdFees. Consider guarding with optional chaining:

  • const feeAmount =
  • feeOptions.find((fee) => fee.amount[0].denom === feeDenom)?.amount[0]?.amount ?? "0"
  • const feeAmount =
  • feeOptions.find((fee) => fee.amount[0]?.denom === feeDenom)?.amount[0]?.amount ?? "0"

2. Since `spendAmount` has a default (`BigNumber(0)`), inside the function it’s effectively non-optional. You can simplify the type and avoid potential `undefined` creep by dropping the `?`:

```diff
-  spendAmount?: BigNumber
+  spendAmount: BigNumber

(Callers already pass a BigNumber; the default handles the “no spend” case.)

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to Reviews -> Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between 7880f8c and ed2c4cd.

📒 Files selected for processing (1)
  • packages/interwovenkit-react/src/data/fee.ts (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
packages/interwovenkit-react/src/data/fee.ts (2)
packages/interwovenkit-react/src/data/chains.ts (1)
  • NormalizedChain (34-34)
packages/interwovenkit-react/src/public/data/constants.ts (1)
  • DEFAULT_GAS_ADJUSTMENT (3-3)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Cloudflare Pages: interwovenkit-testnet
  • GitHub Check: Cloudflare Pages: interwovenkit-testnet
🔇 Additional comments (1)
packages/interwovenkit-react/src/data/fee.ts (1)

4-15: Imports and new dependencies look correct

calculateFee, GasPrice, StdFee, React hooks, and DEFAULT_GAS_ADJUSTMENT / DEFAULT_GAS_PRICE_MULTIPLIER are all used consistently below; no obvious unused or mis-typed imports here.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (3)
packages/interwovenkit-react/src/pages/wallet/txs/send-nft/SendNftFields.tsx (1)

94-113: Gas estimation flow looks correct, but initial zero gas still produces placeholder fees.

The gas query implementation is solid with proper dependencies and stale time. However, as noted in a previous review, when estimatedGas defaults to 0 before gasQuery completes, useTxFee computes fees with gas = 0, momentarily displaying zero-amount fee options.

Consider conditionally computing fee details only when estimatedGas > 0, or showing a loading state for the fee section until gas is available.

packages/interwovenkit-react/src/pages/autosign/EnableAutoSign.tsx (1)

64-69: Include isEstimatingGas in button disabled condition for explicit state handling.

A past review comment noted that estimatedGas defaults to 0 while loading, which flows into useTxFee and produces a zero-fee object. The getFee() check remains truthy even during gas estimation. While the button displays a loading spinner (Line 219), the disabled condition (Line 218) does not explicitly check isEstimatingGas, only the loading state does.

For clarity and to prevent any edge-case interactions, add isEstimatingGas to the disabled condition:

-          disabled={isEnableDisabled || !isAccountCreated || isCheckingAccount || isInsufficient}
+          disabled={isEnableDisabled || !isAccountCreated || isCheckingAccount || isInsufficient || isEstimatingGas}
           loading={isEstimatingGas || isPending}

Based on learnings, this aligns with the previous review recommendation.

Also applies to: 218-219

packages/interwovenkit-react/src/data/fee.ts (1)

148-156: Type mismatch: feeDenom can be undefined at runtime.

Line 153 returns feeOptions[0]?.amount[0]?.denom, which is string | undefined when gasPrices is empty (e.g., during network errors). However, UseTxFeeResult (Line 131) declares feeDenom: string, creating a type mismatch.

Consider either:

  1. Updating the type to feeDenom: string | undefined and handling it in consumers, or
  2. Throwing an error or providing a guaranteed fallback when no fee options exist.

This matches a previous review concern about runtime vs. TypeScript type expectations.

🧹 Nitpick comments (1)
packages/interwovenkit-react/src/pages/wallet/txs/send/SendFields.tsx (1)

160-160: Minor: Unnecessary null coalescing.

maxAmount is always a string from the useMemo (returns "0" or a computed value), so ?? "0" is redundant. Not harmful, just a nit.

Suggested fix
-                {formatAmount(maxAmount ?? "0", { decimals })}
+                {formatAmount(maxAmount, { decimals })}
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to Reviews -> Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between ed2c4cd and c212dc7.

📒 Files selected for processing (17)
  • packages/interwovenkit-react/src/components/Dropdown.module.css (2 hunks)
  • packages/interwovenkit-react/src/components/Dropdown.tsx (2 hunks)
  • packages/interwovenkit-react/src/components/Page.tsx (2 hunks)
  • packages/interwovenkit-react/src/data/fee.ts (2 hunks)
  • packages/interwovenkit-react/src/data/tx.ts (4 hunks)
  • packages/interwovenkit-react/src/pages/autosign/EnableAutoSign.tsx (7 hunks)
  • packages/interwovenkit-react/src/pages/autosign/data/actions.ts (4 hunks)
  • packages/interwovenkit-react/src/pages/autosign/data/queries.ts (1 hunks)
  • packages/interwovenkit-react/src/pages/autosign/data/validation.ts (1 hunks)
  • packages/interwovenkit-react/src/pages/bridge/data/tx.ts (1 hunks)
  • packages/interwovenkit-react/src/pages/tx/TxFee.tsx (1 hunks)
  • packages/interwovenkit-react/src/pages/tx/TxMeta.module.css (1 hunks)
  • packages/interwovenkit-react/src/pages/tx/TxMeta.tsx (2 hunks)
  • packages/interwovenkit-react/src/pages/tx/TxRequest.tsx (5 hunks)
  • packages/interwovenkit-react/src/pages/wallet/txs/send-nft/SendNftFields.tsx (5 hunks)
  • packages/interwovenkit-react/src/pages/wallet/txs/send/SendFields.tsx (6 hunks)
  • packages/interwovenkit-react/src/public/app/Drawer.tsx (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • packages/interwovenkit-react/src/pages/tx/TxMeta.module.css
🚧 Files skipped from review as they are similar to previous changes (3)
  • packages/interwovenkit-react/src/components/Page.tsx
  • packages/interwovenkit-react/src/components/Dropdown.module.css
  • packages/interwovenkit-react/src/public/app/Drawer.tsx
🧰 Additional context used
🧬 Code graph analysis (8)
packages/interwovenkit-react/src/pages/autosign/data/queries.ts (1)
packages/interwovenkit-react/src/pages/autosign/data/validation.ts (1)
  • autoSignQueryKeys (13-24)
packages/interwovenkit-react/src/data/tx.ts (1)
examples/vite/src/data.ts (1)
  • chainId (6-6)
packages/interwovenkit-react/src/pages/wallet/txs/send-nft/SendNftFields.tsx (5)
packages/interwovenkit-react/src/data/http.ts (1)
  • STALE_TIMES (4-8)
packages/interwovenkit-react/src/data/account.ts (1)
  • useBalances (43-47)
packages/interwovenkit-react/src/data/assets.ts (1)
  • useFindAsset (76-88)
packages/interwovenkit-react/src/data/fee.ts (2)
  • useTxFee (136-165)
  • getFeeDetails (36-59)
packages/interwovenkit-react/src/pages/tx/TxMeta.tsx (1)
  • TxMeta (14-16)
packages/interwovenkit-react/src/pages/autosign/EnableAutoSign.tsx (5)
packages/interwovenkit-react/src/data/account.ts (2)
  • accountQueryKeys (14-19)
  • useBalances (43-47)
packages/interwovenkit-react/src/public/data/hooks.ts (1)
  • useInterwovenKit (51-93)
packages/interwovenkit-react/src/pages/autosign/data/actions.ts (1)
  • useBuildEnableMessages (60-123)
packages/interwovenkit-react/src/data/assets.ts (1)
  • useFindAsset (76-88)
packages/interwovenkit-react/src/data/fee.ts (2)
  • useTxFee (136-165)
  • getFeeDetails (36-59)
packages/interwovenkit-react/src/pages/tx/TxFee.tsx (1)
packages/interwovenkit-react/src/data/chains.ts (1)
  • NormalizedChain (34-34)
packages/interwovenkit-react/src/pages/autosign/data/actions.ts (4)
packages/interwovenkit-react/src/public/data/hooks.ts (1)
  • useInitiaAddress (16-20)
packages/interwovenkit-react/src/pages/autosign/data/wallet.ts (1)
  • useEmbeddedWalletAddress (20-23)
packages/interwovenkit-react/src/pages/autosign/data/store.ts (1)
  • pendingAutoSignRequestAtom (9-9)
packages/interwovenkit-react/src/data/tx.ts (1)
  • useTx (69-303)
packages/interwovenkit-react/src/pages/autosign/data/validation.ts (1)
examples/vite/src/data.ts (1)
  • chainId (6-6)
packages/interwovenkit-react/src/pages/wallet/txs/send/SendFields.tsx (7)
packages/interwovenkit-react/src/public/data/hooks.ts (1)
  • useInterwovenKit (51-93)
packages/interwovenkit-react/src/data/chains.ts (1)
  • useChain (97-100)
examples/vite/src/data.ts (1)
  • chainId (6-6)
packages/interwovenkit-react/src/data/account.ts (1)
  • useBalances (43-47)
packages/interwovenkit-react/src/data/assets.ts (1)
  • useFindAsset (76-88)
packages/interwovenkit-react/src/data/fee.ts (2)
  • useTxFee (136-165)
  • getFeeDetails (36-59)
packages/interwovenkit-react/src/pages/tx/TxMeta.tsx (1)
  • TxMeta (14-16)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Cloudflare Pages: interwovenkit-testnet
🔇 Additional comments (11)
packages/interwovenkit-react/src/components/Dropdown.tsx (1)

22-22: LGTM! Clean implementation of the darker variant.

The prop is properly typed, destructured, and conditionally applied using clsx. This is a non-breaking addition.

Also applies to: 29-29, 45-47

packages/interwovenkit-react/src/pages/bridge/data/tx.ts (1)

102-102: The change to internal: -1 is correct and requires no further verification. The -1 value is explicitly documented in the TxRequest type definition as "disabling notification" and is properly handled by the requestTx logic, which skips watchTxStatus calls for numeric internal values. This is the only appropriate use of -1 for the Initia chain path, while other callsites correctly use string or boolean values for their respective contexts (navigation redirects and modal displays).

Likely an incorrect or invalid review comment.

packages/interwovenkit-react/src/pages/autosign/data/validation.ts (1)

18-23: LGTM!

The query key rename from grants to allGrants improves clarity, and the new revokeMessages key correctly includes the grantee parameter for cache specificity.

packages/interwovenkit-react/src/pages/autosign/data/queries.ts (1)

18-18: LGTM!

Query key reference correctly updated to match the renamed allGrants key in validation.ts.

packages/interwovenkit-react/src/pages/wallet/txs/send-nft/SendNftFields.tsx (3)

117-126: LGTM!

Good error handling for missing fee, and submitTxSync is correctly invoked with the computed fee and internal navigation path.


165-181: LGTM!

Clean integration of TxMeta and TxFee components with proper insufficient balance feedback using FormHelp.Stack.


184-195: LGTM!

The loading state aggregation providing contextual messages ("Finding route...", "Estimating gas...") is a nice UX improvement. Correctly disables submission when isInsufficient.

packages/interwovenkit-react/src/pages/wallet/txs/send/SendFields.tsx (3)

70-100: LGTM!

The fee-centric refactor is well-designed:

  • getSpendAmount correctly considers spend only when fee denom matches sending denom
  • calcFeeDetails properly computes sufficiency including both fee and spend
  • maxAmount deducts fee from balance when sending the fee token, preventing over-spending

107-126: LGTM!

Clean mutation implementation with proper fee retrieval and error handling. Using submitTxSync with explicit fee aligns with the new fee-centric flow.


181-201: LGTM!

Proper integration of fee UI with TxMeta and TxFee components, along with contextual warnings for missing memo and insufficient balance.

packages/interwovenkit-react/src/pages/tx/TxMeta.tsx (1)

14-20: Clean composable component pattern using TxMeta.Item for a cohesive API. The refactor from direct TxMetaItem export to a namespace-style compound component improves the consumer experience. Verify the CSS module TxMeta.module.css includes both .meta and .item classes before approving.

Copy link

@coderabbitai coderabbitai bot left a 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

🧹 Nitpick comments (1)
packages/interwovenkit-react/src/pages/bridge/BridgePreviewFooter.tsx (1)

79-88: Extract message conversion to a shared utility function.

This amino message conversion logic is duplicated from tx.ts (lines 92-100). Consider extracting it into a shared utility function to improve maintainability and reduce duplication.

🔎 Suggested refactor

Create a shared utility in a new file packages/interwovenkit-react/src/pages/bridge/data/messages.ts:

import { aminoConverters, aminoTypes } from "@initia/amino-converter"
import type { TxJson } from "@skip-go/client"

export function convertCosmosTxMessages(tx: TxJson) {
  if (!("cosmos_tx" in tx) || !tx.cosmos_tx.msgs) {
    throw new Error("Invalid transaction data")
  }
  
  return tx.cosmos_tx.msgs.map(({ msg_type_url, msg }) => {
    if (!(msg_type_url && msg)) throw new Error("Invalid transaction data")
    return aminoTypes.fromAmino({
      type: aminoConverters[msg_type_url].aminoType,
      value: JSON.parse(msg),
    })
  })
}

Then use it in both files:

-const messages =
-  "cosmos_tx" in tx && tx.cosmos_tx.msgs
-    ? tx.cosmos_tx.msgs.map(({ msg_type_url, msg }) => {
-        if (!(msg_type_url && msg)) throw new Error("Invalid transaction data")
-        return aminoTypes.fromAmino({
-          type: aminoConverters[msg_type_url].aminoType,
-          value: JSON.parse(msg),
-        })
-      })
-    : []
+const messages = "cosmos_tx" in tx ? convertCosmosTxMessages(tx) : []
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to Reviews -> Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between c212dc7 and 51efc8a.

📒 Files selected for processing (2)
  • packages/interwovenkit-react/src/pages/bridge/BridgePreviewFooter.tsx (2 hunks)
  • packages/interwovenkit-react/src/pages/bridge/data/tx.ts (5 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
packages/interwovenkit-react/src/pages/bridge/BridgePreviewFooter.tsx (9)
packages/interwovenkit-react/src/pages/bridge/data/tx.ts (2)
  • useBridgePreviewState (53-55)
  • useBridgeTx (61-249)
packages/interwovenkit-react/src/pages/bridge/data/chains.ts (2)
  • useSkipChain (80-83)
  • useChainType (33-36)
packages/interwovenkit-react/src/public/data/hooks.ts (1)
  • useInterwovenKit (51-93)
packages/interwovenkit-react/src/data/chains.ts (1)
  • useFindChain (65-95)
packages/interwovenkit-react/src/data/account.ts (1)
  • useBalances (43-47)
packages/interwovenkit-react/src/data/assets.ts (1)
  • useFindAsset (76-88)
packages/interwovenkit-react/src/pages/bridge/data/assets.ts (1)
  • useFindSkipAsset (35-42)
packages/interwovenkit-react/src/data/fee.ts (2)
  • useTxFee (136-165)
  • getFeeDetails (36-59)
packages/interwovenkit-react/src/pages/tx/TxMeta.tsx (1)
  • TxMeta (14-16)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Cloudflare Pages: interwovenkit-testnet
🔇 Additional comments (7)
packages/interwovenkit-react/src/pages/bridge/data/tx.ts (3)

57-62: LGTM! Clean API design for fee parameterization.

The new BridgeTxOptions interface and updated useBridgeTx signature provide a clean, backward-compatible way to pass optional fee parameters. The destructuring with default empty object is a good pattern.


136-139: LGTM! Proper use of CosmJS calculateFee utility.

The refactoring to use calculateFee from @cosmjs/stargate is a best practice improvement over manual fee object construction. The variable naming (cosmosFee) clearly distinguishes this from the Initia fee path.


103-106: The fee requirement for Initia transactions is properly handled. The component routing in BridgePreviewFooter.tsx ensures that Initia transactions always provide a fee: if srcChainType === "initia", the code renders BridgePreviewFooterWithFee, which calculates and passes the fee to useBridgeTx. Non-Initia transactions route to BridgePreviewFooterSimple and calculate fees internally. All Initia transaction flows are covered.

packages/interwovenkit-react/src/pages/bridge/BridgePreviewFooter.tsx (4)

30-46: LGTM! Clean separation of concerns with conditional rendering.

The branching logic correctly identifies Initia cosmos transactions and routes them to the fee-aware component wrapped in AsyncBoundary. The simple footer handles non-Initia transactions, maintaining backward compatibility.


48-54: LGTM! Good loading state UX.

The placeholder component provides clear user feedback during gas estimation with an appropriate loading message.


100-104: LGTM! Correct spend amount calculation.

The logic correctly accounts for the spending amount only when the fee denomination matches the source denomination. This ensures accurate balance sufficiency checks when both the transfer and fee are paid in the same token.


118-144: LGTM! Well-structured fee UI with proper error handling.

The fee UI implementation is solid:

  • Clear error messaging for insufficient balance
  • Proper button state management (disabled when insufficient)
  • Appropriate loading states for both gas estimation and transaction signing
  • Good use of the Footer extra prop for additional fee information

- Add useTxFee hook for fee option calculation and management
- Add getFeeDetails function to calculate and validate fee sufficiency
- Rename TxMetaItem to TxMeta with container component pattern
- Add darker style variant to Dropdown component
- Integrate fee estimation and validation in EnableAutoSign page
- Add fee display and selection UI to send-nft and send pages
- Support internal transaction tracking with status watching
Copy link

@coderabbitai coderabbitai bot left a 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

Fix all issues with AI Agents 🤖
In @packages/interwovenkit-react/src/pages/autosign/data/actions.ts:
- Line 76: The code memoizes a single Date instance via "const now = useMemo(()
=> new Date(), [])" which becomes stale; remove that memoized "now" and instead
construct a fresh Date() inside the "buildGrantMessages" function at the moment
it's invoked (or pass a current timestamp into buildGrantMessages) so expiration
calculations use the actual current time; update any references to the
module-level "now" to use the locally created Date/timestamp within
buildGrantMessages and remove the useMemo declaration.
- Around line 60-71: The hook useBuildEnableMessages currently throws during
render when wallets or pending request are missing; change it to return a
nullable/disabled result instead of throwing: inside useBuildEnableMessages,
replace the throws that check useInitiaAddress(), useEmbeddedWalletAddress(),
and pendingAutoSignRequestAtom (the initiaAddress, embeddedWalletAddress and
pendingRequest variables) with an early return (e.g., null or an object with a
disabled flag) so consumers can guard or disable UI; update callers to handle
the null/disabled return value rather than relying on exceptions.
♻️ Duplicate comments (3)
packages/interwovenkit-react/src/pages/bridge/BridgePreviewFooter.tsx (1)

90-98: Previous review concern remains unaddressed: error handling for gas estimation and fee retrieval.

The gas estimation query still lacks error handling—if it fails, estimatedGas defaults to 0, producing incorrect fee calculations. Additionally, getFee() can return undefined, which will cause issues when passed to useBridgeTx.

🔎 Suggested fixes
 const { data: estimatedGas = 0, isLoading } = useQuery({
   queryKey: queryKeys.gas({ chainId: srcChainId, sender: initiaAddress, messages }).queryKey,
   queryFn: () => estimateGas({ messages, chainId: srcChainId }),
   enabled: messages.length > 0,
+  throwOnError: true,
 })

 const { feeOptions, feeDenom, setFeeDenom, getFee } = useTxFee({ chain, estimatedGas })
 const fee = getFee()
+if (!fee) throw new Error("Unable to calculate transaction fee")
 const { mutate, isPending } = useBridgeTx(tx, { fee })
packages/interwovenkit-react/src/pages/wallet/txs/send-nft/SendNftFields.tsx (1)

106-113: Previous review concern: fee calculated with estimatedGas = 0 before gas is ready.

When gasQuery is loading, estimatedGas defaults to 0, causing useTxFee to compute fees with zero gas. This results in momentarily displaying zero-amount fees until gas estimation completes. Consider conditionally rendering the fee section only after gas is available, or displaying a loading state for the fee.

packages/interwovenkit-react/src/data/fee.ts (1)

148-156: feeDenom can be undefined at runtime despite type declaration.

initialFeeDenom on line 153 returns feeOptions[0]?.amount[0]?.denom which is string | undefined. When gasPrices is empty (edge case), this will be undefined, but UseTxFeeResult declares feeDenom: string. This type mismatch can cause runtime issues in consumers expecting a string.

This was flagged in a past review but appears unaddressed.

🔎 Suggested fix
 interface UseTxFeeResult {
   gasPrices: Coin[]
   gas: number
   feeOptions: StdFee[]
-  feeDenom: string
+  feeDenom: string | undefined
   setFeeDenom: (denom: string) => void
   getFee: () => StdFee | undefined
 }

Then update consumers to handle the possibly undefined value.

🧹 Nitpick comments (7)
packages/interwovenkit-react/src/pages/tx/TxFee.tsx (1)

18-21: Consider a more robust check for small amounts.

The string comparison === "0.000000" is brittle—if formatAmount ever changes its default decimal places, this check could silently break. A numeric comparison against a threshold would be more robust.

🔎 Suggested improvement
 const getDp = (amount: string, decimals: number) => {
-  if (formatAmount(amount, { decimals }) === "0.000000") return 8
+  // Show more decimals for amounts that would otherwise display as zero
+  const formatted = parseFloat(formatAmount(amount, { decimals }))
+  if (formatted === 0 && parseFloat(amount) > 0) return 8
   return undefined
 }
packages/interwovenkit-react/src/pages/bridge/BridgePreviewFooter.tsx (1)

79-88: Consider defensive error handling for amino conversion.

If aminoConverters[msg_type_url] is undefined for an unexpected message type, accessing .aminoType will throw. Consider adding a guard or more descriptive error.

🔎 Suggested improvement
 const messages =
   "cosmos_tx" in tx && tx.cosmos_tx.msgs
     ? tx.cosmos_tx.msgs.map(({ msg_type_url, msg }) => {
         if (!(msg_type_url && msg)) throw new Error("Invalid transaction data")
+        const converter = aminoConverters[msg_type_url]
+        if (!converter) throw new Error(`Unsupported message type: ${msg_type_url}`)
         return aminoTypes.fromAmino({
-          type: aminoConverters[msg_type_url].aminoType,
+          type: converter.aminoType,
           value: JSON.parse(msg),
         })
       })
     : []
packages/interwovenkit-react/src/pages/wallet/txs/send-nft/SendNftFields.tsx (1)

94-104: Query key serialization may cause unnecessary re-fetches.

Using JSON.stringify(messages, ...) for the query key can lead to cache misses if object reference changes even when content is identical. Consider using a stable identifier derived from message content (e.g., a hash or specific fields).

packages/interwovenkit-react/src/pages/wallet/txs/send/SendFields.tsx (1)

90-100: Consider edge case when feeOptions is empty.

If feeOptions is empty (e.g., gas prices not yet loaded or chain config issue), the find on line 94 returns undefined, defaulting feeAmount to "0". This could cause maxAmount to equal the full balance even when the user can't actually pay fees, potentially misleading them.

🔎 Suggested defensive check
  const maxAmount = useMemo(() => {
    // If sending the same token as fee token
    if (denom === feeDenom) {
+     if (!feeOptions.length) return balance // No fee options available yet
      const feeAmount =
        feeOptions.find((fee) => fee.amount[0].denom === feeDenom)?.amount[0]?.amount ?? "0"
      const maxValue = BigNumber(balance).minus(feeAmount)
      return maxValue.gt(0) ? maxValue.toFixed() : "0"
    }
    // If sending different token
    return balance
  }, [denom, feeDenom, feeOptions, balance])
packages/interwovenkit-react/src/pages/autosign/EnableAutoSign.tsx (1)

64-69: Gas estimation query lacks error handling.

If estimateGas fails (e.g., network error), the query will error but estimatedGas will remain 0 due to the default. This flows through to useTxFee, producing zero-gas fee calculations. Consider adding error handling or showing an error state to the user.

🔎 Suggested improvement
  // Estimate gas
- const { data: estimatedGas = 0, isLoading: isEstimatingGas } = useQuery({
+ const { data: estimatedGas = 0, isLoading: isEstimatingGas, isError: isGasError } = useQuery({
    queryKey: accountQueryKeys.gas(estimationMessages, chainId).queryKey,
    queryFn: () => estimateGas({ messages: estimationMessages, chainId }),
    enabled: !!estimationMessages.length,
    staleTime: STALE_TIMES.INFINITY,
  })

Then include isGasError in the disabled condition and optionally show an error message.

packages/interwovenkit-react/src/data/fee.ts (1)

158-160: useEffect to sync feeDenom may cause unnecessary re-renders.

When initialFeeDenom changes (e.g., due to feeOptions recalculation), the effect sets state even if the new value equals the current feeDenom. This could cause extra renders.

🔎 Suggested optimization
  useEffect(() => {
+   if (feeDenom !== initialFeeDenom) {
      setFeeDenom(initialFeeDenom)
+   }
  }, [initialFeeDenom])

Note: This requires adding feeDenom to deps, which changes behavior slightly. Alternatively, keep current approach if re-renders are acceptable.

packages/interwovenkit-react/src/data/tx.ts (1)

26-33: Consider using a more explicit type for internal field.

The type boolean | string | -1 is unconventional. The -1 literal type mixed with boolean | string can be confusing for consumers. Consider using a union with named types or an object structure for clarity.

🔎 Suggested alternative
 export interface BaseTx {
   messages: EncodeObject[]
   memo?: string
   chainId?: string

   /** Internal use only */
-  internal?: boolean | string | -1 // -1 for disabling notification
+  internal?: 
+    | false           // External request (default)
+    | true            // Internal with notification
+    | string          // Internal with redirect path
+    | "silent"        // Internal without notification
 }

Using "silent" instead of -1 is more self-documenting.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to Reviews -> Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between 51efc8a and 72e0510.

📒 Files selected for processing (18)
  • packages/interwovenkit-react/src/components/Dropdown.module.css
  • packages/interwovenkit-react/src/components/Dropdown.tsx
  • packages/interwovenkit-react/src/components/Page.tsx
  • packages/interwovenkit-react/src/data/fee.ts
  • packages/interwovenkit-react/src/data/tx.ts
  • packages/interwovenkit-react/src/pages/autosign/EnableAutoSign.tsx
  • packages/interwovenkit-react/src/pages/autosign/data/actions.ts
  • packages/interwovenkit-react/src/pages/autosign/data/queries.ts
  • packages/interwovenkit-react/src/pages/autosign/data/validation.ts
  • packages/interwovenkit-react/src/pages/bridge/BridgePreviewFooter.tsx
  • packages/interwovenkit-react/src/pages/bridge/data/tx.ts
  • packages/interwovenkit-react/src/pages/tx/TxFee.tsx
  • packages/interwovenkit-react/src/pages/tx/TxMeta.module.css
  • packages/interwovenkit-react/src/pages/tx/TxMeta.tsx
  • packages/interwovenkit-react/src/pages/tx/TxRequest.tsx
  • packages/interwovenkit-react/src/pages/wallet/txs/send-nft/SendNftFields.tsx
  • packages/interwovenkit-react/src/pages/wallet/txs/send/SendFields.tsx
  • packages/interwovenkit-react/src/public/app/Drawer.tsx
🚧 Files skipped from review as they are similar to previous changes (4)
  • packages/interwovenkit-react/src/components/Page.tsx
  • packages/interwovenkit-react/src/pages/bridge/data/tx.ts
  • packages/interwovenkit-react/src/pages/tx/TxMeta.module.css
  • packages/interwovenkit-react/src/components/Dropdown.module.css
🧰 Additional context used
🧬 Code graph analysis (7)
packages/interwovenkit-react/src/pages/autosign/data/queries.ts (1)
packages/interwovenkit-react/src/pages/autosign/data/validation.ts (1)
  • autoSignQueryKeys (13-24)
packages/interwovenkit-react/src/pages/autosign/EnableAutoSign.tsx (8)
packages/interwovenkit-react/src/data/account.ts (2)
  • accountQueryKeys (14-19)
  • useBalances (43-47)
packages/interwovenkit-react/src/public/data/hooks.ts (1)
  • useInterwovenKit (51-93)
packages/interwovenkit-react/src/pages/autosign/data/actions.ts (2)
  • useEnableAutoSign (126-176)
  • useBuildEnableMessages (60-123)
packages/interwovenkit-react/src/pages/autosign/data/constants.ts (1)
  • DEFAULT_DURATION (12-12)
packages/interwovenkit-react/src/data/http.ts (1)
  • STALE_TIMES (4-8)
packages/interwovenkit-react/src/data/assets.ts (1)
  • useFindAsset (76-88)
packages/interwovenkit-react/src/data/fee.ts (1)
  • useTxFee (136-165)
packages/interwovenkit-react/src/pages/tx/TxMeta.tsx (1)
  • TxMeta (14-16)
packages/interwovenkit-react/src/pages/tx/TxFee.tsx (1)
packages/interwovenkit-react/src/data/chains.ts (1)
  • NormalizedChain (34-34)
packages/interwovenkit-react/src/pages/bridge/BridgePreviewFooter.tsx (8)
packages/interwovenkit-react/src/pages/bridge/data/tx.ts (1)
  • useBridgePreviewState (53-55)
packages/interwovenkit-react/src/pages/bridge/data/chains.ts (2)
  • useSkipChain (80-83)
  • useChainType (33-36)
packages/interwovenkit-react/src/public/data/hooks.ts (1)
  • useInterwovenKit (51-93)
packages/interwovenkit-react/src/data/account.ts (1)
  • useBalances (43-47)
packages/interwovenkit-react/src/data/assets.ts (1)
  • useFindAsset (76-88)
packages/interwovenkit-react/src/pages/bridge/data/assets.ts (1)
  • useFindSkipAsset (35-42)
packages/interwovenkit-react/src/data/fee.ts (2)
  • useTxFee (136-165)
  • getFeeDetails (36-59)
packages/interwovenkit-react/src/pages/tx/TxMeta.tsx (1)
  • TxMeta (14-16)
packages/interwovenkit-react/src/pages/autosign/data/actions.ts (4)
packages/interwovenkit-react/src/pages/autosign/data/wallet.ts (1)
  • useEmbeddedWalletAddress (20-23)
packages/interwovenkit-react/src/pages/autosign/data/store.ts (1)
  • pendingAutoSignRequestAtom (9-9)
packages/interwovenkit-react/src/pages/autosign/data/validation.ts (2)
  • useAutoSignMessageTypes (27-49)
  • autoSignQueryKeys (13-24)
packages/interwovenkit-react/src/data/tx.ts (1)
  • useTx (69-303)
packages/interwovenkit-react/src/data/fee.ts (2)
packages/interwovenkit-react/src/data/chains.ts (1)
  • NormalizedChain (34-34)
packages/interwovenkit-react/src/public/data/constants.ts (1)
  • DEFAULT_GAS_ADJUSTMENT (3-3)
packages/interwovenkit-react/src/pages/tx/TxRequest.tsx (2)
packages/interwovenkit-react/src/data/fee.ts (1)
  • getFeeDetails (36-59)
packages/interwovenkit-react/src/pages/tx/TxMeta.tsx (1)
  • TxMeta (14-16)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Cloudflare Pages: interwovenkit-testnet
🔇 Additional comments (21)
packages/interwovenkit-react/src/components/Dropdown.tsx (1)

22-22: LGTM! Clean implementation of the darker variant.

The optional darker prop is correctly integrated:

  • Added to the interface for type safety
  • Destructured in the function parameters
  • Conditionally applied to the trigger element using clsx

The implementation is backward-compatible and follows React best practices.

Also applies to: 29-29, 45-47

packages/interwovenkit-react/src/public/app/Drawer.tsx (1)

83-90: LGTM! Good documentation of the focus-trap rationale and historical context.

The switch from modal={false} to modal="trap-focus" improves UX by preventing accidental drawer closure when users Tab through interactive elements. The inline comments documenting the Base UI beta.4 bugs and testing guidance are valuable for future maintainers.

packages/interwovenkit-react/src/pages/tx/TxMeta.tsx (1)

14-20: LGTM! Clean composable component pattern.

The TxMeta wrapper with TxMeta.Item subcomponent provides a cleaner API for consumers, allowing usage like:

<TxMeta>
  <TxMeta.Item title="..." content="..." />
</TxMeta>

This is a well-established React composition pattern.

packages/interwovenkit-react/src/pages/tx/TxFee.tsx (1)

15-27: LGTM! Prop-driven chain context improves reusability.

Passing chain as a prop instead of deriving it internally makes the component more testable and decouples it from specific hook contexts.

packages/interwovenkit-react/src/pages/autosign/data/queries.ts (1)

18-18: LGTM! Query key aligned with renamed definition.

The update to autoSignQueryKeys.allGrants(...) correctly reflects the renamed key in validation.ts.

packages/interwovenkit-react/src/pages/autosign/data/validation.ts (1)

18-24: LGTM! Query keys are well-structured.

The rename from grants to allGrants improves clarity, and the new revokeMessages key with its grantee parameter enables proper query scoping for revocation-related operations.

packages/interwovenkit-react/src/pages/bridge/BridgePreviewFooter.tsx (1)

30-46: Clean separation of Initia vs non-Initia transaction flows.

The branching logic with AsyncBoundary and dedicated placeholder/simple/fee-aware components improves code organization and user experience during async operations.

packages/interwovenkit-react/src/pages/wallet/txs/send-nft/SendNftFields.tsx (2)

119-121: Good explicit fee validation before submission.

Unlike BridgePreviewFooter, this correctly validates that getFee() returns a valid fee before proceeding with the transaction.


165-181: Fee UI integration looks good.

The TxMeta and TxMeta.Item composition pattern is used correctly, and the insufficient balance error is properly surfaced to the user.

packages/interwovenkit-react/src/pages/wallet/txs/send/SendFields.tsx (2)

120-124: LGTM!

The fee validation and submission flow is correctly implemented. The error thrown when fee is not found will be properly caught by react-query's mutation error handling.


186-200: LGTM!

Good integration of TxMeta and TxFee components for fee display and selection. The insufficient balance warning is properly shown based on feeDetails.isSufficient.

packages/interwovenkit-react/src/pages/autosign/EnableAutoSign.tsx (2)

94-98: Defensive throw is now guarded by disabled state.

The handleEnable throw at line 96 is now properly guarded since !getFee() is part of isEnableDisabled (line 106) and isEstimatingGas is in the button's disabled condition (line 218). The throw serves as a defensive fallback that should never be reached in practice.


216-222: LGTM - Button states properly handle all loading and disabled conditions.

The disabled condition now correctly includes isEstimatingGas indirectly via !getFee() during loading (zero gas produces valid but incorrect fee), plus the explicit isInsufficient check. The loading state shows during both gas estimation and mutation pending.

packages/interwovenkit-react/src/data/fee.ts (1)

36-59: LGTM!

The getFeeDetails function correctly computes fee sufficiency by combining spend amount and fee amount, then comparing against balance. The logic handles the optional spendAmount parameter gracefully.

packages/interwovenkit-react/src/pages/tx/TxRequest.tsx (3)

46-58: LGTM!

Clean extraction of getSpendAmount and calcFeeDetails helper functions that properly delegate to the shared getFeeDetails utility. This improves consistency with other fee-handling flows.


60-73: getInitialFeeDenom correctly prioritizes fee selection.

The logic properly:

  1. Tries the last-used denomination if sufficient
  2. Falls back to the first sufficient option
  3. Defaults to the first available option if none are sufficient

This ensures users get a working default when possible.


103-111: LGTM!

Good migration to TxMeta.Item components with proper chain-aware TxFee integration.

packages/interwovenkit-react/src/pages/autosign/data/actions.ts (1)

150-159: LGTM!

The mutation correctly accepts { durationInMs, fee } and uses the centralized buildEnableMessages function. The fee is properly passed to submitTxBlock.

packages/interwovenkit-react/src/data/tx.ts (3)

173-182: LGTM!

The watchTxStatus helper properly centralizes the status watching logic. It correctly handles both success and error cases from waitForTxConfirmation, and maps tx.code === 0 to success status.


242-245: Error handling sets status but error is still thrown.

When internal is truthy and not a number, the error is recorded in txStatus, then re-thrown. This is correct for allowing callers to handle errors while also updating UI status, but ensure error boundaries or try-catch blocks exist in calling code.


231-247: No changes needed. The simultaneous status watching and navigation for string internal values is intentional design. The watchTxStatus function initiates an asynchronous operation that runs in the background, allowing the app to navigate to the specified route while continuing to track transaction confirmation. This pattern is correct and used consistently throughout the codebase (e.g., internal: "/", internal: "/nfts").

- Support fee denom selection for Initia transactions
- Add gas estimation with message serialization
- Validate sufficient balance for gas fees
- Replace requestTxSync with submitTxSync API
- Add fee details calculation and error handling
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
packages/interwovenkit-react/src/pages/bridge/BridgePreviewFooter.tsx (1)

68-70: Missing validation for undefined fee.

getFee() may return undefined if no matching fee denomination is found. This undefined value is passed directly to useBridgeTx, which can cause runtime errors when the transaction is submitted. The isInsufficient check won't catch this case since getFeeDetails defaults missing fees to "0".

This issue was previously flagged and remains unaddressed.

🧹 Nitpick comments (1)
packages/interwovenkit-react/src/pages/bridge/BridgePreviewFooter.tsx (1)

60-66: Redundant message parsing in query.

parseCosmosMessages(tx) is called at line 60 and again at line 64 in queryFn. Reuse the already-parsed messages variable to avoid redundant computation.

🔎 Proposed fix
   const messages = parseCosmosMessages(tx)

   const { data: estimatedGas = 0, isLoading } = useQuery({
     queryKey: queryKeys.gas({ chainId: srcChainId, sender: initiaAddress, tx }).queryKey,
-    queryFn: () => estimateGas({ messages: parseCosmosMessages(tx), chainId: srcChainId }),
+    queryFn: () => estimateGas({ messages, chainId: srcChainId }),
     enabled: messages.length > 0,
   })
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to Reviews -> Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between d7cbed0 and 7d5beab.

📒 Files selected for processing (2)
  • packages/interwovenkit-react/src/pages/bridge/BridgePreviewFooter.tsx
  • packages/interwovenkit-react/src/pages/bridge/data/tx.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/interwovenkit-react/src/pages/bridge/data/tx.ts
🧰 Additional context used
🧬 Code graph analysis (1)
packages/interwovenkit-react/src/pages/bridge/BridgePreviewFooter.tsx (9)
packages/interwovenkit-react/src/public/data/hooks.ts (1)
  • useInterwovenKit (51-93)
packages/interwovenkit-react/src/pages/bridge/data/tx.ts (2)
  • useBridgePreviewState (53-55)
  • parseCosmosMessages (57-68)
packages/interwovenkit-react/src/data/chains.ts (1)
  • useFindChain (65-95)
packages/interwovenkit-react/src/data/account.ts (1)
  • useBalances (43-47)
packages/interwovenkit-react/src/data/assets.ts (1)
  • useFindAsset (76-88)
packages/interwovenkit-react/src/pages/bridge/data/assets.ts (1)
  • useFindSkipAsset (35-42)
packages/interwovenkit-react/src/data/fee.ts (2)
  • useTxFee (136-165)
  • getFeeDetails (36-59)
packages/interwovenkit-react/src/pages/tx/TxMeta.tsx (1)
  • TxMeta (14-16)
packages/interwovenkit-react/src/pages/bridge/data/chains.ts (2)
  • useSkipChain (80-83)
  • useChainType (33-36)
⏰ 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: Cloudflare Pages: interwovenkit-testnet
🔇 Additional comments (4)
packages/interwovenkit-react/src/pages/bridge/BridgePreviewFooter.tsx (4)

29-35: LGTM!

Clean placeholder implementation for the suspense fallback.


37-47: LGTM!

Clean implementation for the simple (non-Initia) transaction flow.


90-116: LGTM!

The fee display UI with insufficient balance handling and loading states is well-structured. Good use of TxMeta with the composable Item subcomponent.


119-135: LGTM!

Clean routing logic with appropriate AsyncBoundary wrapping for the fee-aware path.

@simcheolhwan simcheolhwan marked this pull request as draft January 5, 2026 05:23
- Replace memoized date constant with direct new Date() call
- Remove unused useMemo import
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants