Skip to content

Tisi.new bridge#2383

Draft
Xenofluxx wants to merge 6 commits intomainfrom
tisi.new-bridge
Draft

Tisi.new bridge#2383
Xenofluxx wants to merge 6 commits intomainfrom
tisi.new-bridge

Conversation

@Xenofluxx
Copy link
Contributor

@Xenofluxx Xenofluxx commented Mar 9, 2026

Summary by CodeRabbit

Release Notes

  • New Features

    • Added Bridge modal for seamless ETH bridging across Ethereum, Base, Arbitrum, and Optimism to Yominet with live balance updates and transaction tracking.
  • Improvements

    • Enhanced modal z-index customization for better layer control.
    • Improved wallet and account visibility logic during bridge workflows.
    • Updated security policy to support additional blockchain RPC endpoints.

@Xenofluxx Xenofluxx marked this pull request as ready for review March 9, 2026 11:43
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 9, 2026

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.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2c31d59d-637b-406b-b067-d90009d9a9bc

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

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

A new bridge modal feature enables ETH transfers from Ethereum-compatible chains to Yominet through a modal UI component. Supporting infrastructure includes bridge API utilities, type definitions, wallet integration hooks, and validator state management updates to coordinate the bridging workflow.

Changes

Cohort / File(s) Summary
Bridge Modal & Core Components
packages/client/src/app/components/modals/bridge/Bridge.tsx, constants.ts, types.ts, utils.ts, index.ts
New bridge modal component with wallet integration, balance management, network switching, and transaction polling. Supporting modules provide EVM chain configuration, bridge types (EVMChainOption, BridgeMsgsPayload, EVMWalletProvider), and Ethereum utilities for balance retrieval and RPC caching.
Bridge Network Infrastructure
packages/client/src/network/bridge.ts, packages/client/src/network/hooks/useBridgeOpener.ts
Router API integration module providing fetchBridgeRoute, fetchBridgeMsgs, buildBridgePayload, and getBridgeServiceStatus functions. New hook useBridgeOpener dispatches CustomEvent for bridge modal triggering with request/override options.
Hook Migration
packages/client/src/network/utils/hooks.ts
Removed useBridgeOpener from utils module; function relocated to network/hooks/useBridgeOpener.ts with new event-based implementation replacing previous openBridge logic.
Component Import Updates
packages/client/src/app/components/fixtures/menu/buttons/More.tsx, modals/FundOperator.tsx, validators/GasHarasser.tsx
Updated import paths for useBridgeOpener from network/utils/hooks to network/hooks/useBridgeOpener with no behavioral changes.
Validator Integration
packages/client/src/app/components/validators/AccountRegistrar/AccountRegistrar.tsx, AccountRegistrar/Registration.tsx, WalletConnector/WalletConnector.tsx
Added bridge modal visibility guards to prevent validator state changes during bridge flow. Registration introduces BRIDGE_THRESHOLD_ETH constant replacing previous GasConstants reference. WalletConnector segregates wagmi and app store hooks, tracks bridgeModalVisible and accountExists state.
Modal & Store Setup
packages/client/src/app/components/index.ts, library/modals/ModalWrapper.tsx, packages/client/src/app/stores/visibility.ts
Registered BridgeModal component with grid positioning. Enhanced ModalWrapper to accept optional wrapperZIndex prop for overlay z-index customization. Added bridgeERC20 modal to MODAL_ZONES center region.
Contract Types & ABI
packages/client/abi/NewbieVendorBuySystem.json, packages/client/types/ethers-contracts/NewbieVendorBuySystem.ts, types/ethers-contracts/index.ts
Added calcPrice() view function to NewbieVendorBuySystem contract ABI and corresponding ethers-contract type overloads. Updated factory exports in index.ts with reordering and additions of system/component factories.
Security & Networking
packages/client/index.html
Expanded Content-Security-Policy connect-src directive with RPC endpoints (ethereum-rpc.publicnode.com, mainnet.base.org, arb1.arbitrum.io, mainnet.optimism.io) for blockchain network access.

Sequence Diagram

sequenceDiagram
    participant User as User/UI
    participant BridgeModal as BridgeModal Component
    participant BridgeService as Bridge API
    participant Wallet as EVM Wallet
    participant SourceRPC as Source Chain RPC
    participant YomiRPC as Yominet RPC

    User->>BridgeModal: Opens bridge modal
    BridgeModal->>BridgeService: getBridgeServiceStatus()
    BridgeService-->>BridgeModal: health status
    
    User->>BridgeModal: Selects chain & amount
    BridgeModal->>SourceRPC: getNativeBalance(address)
    SourceRPC-->>BridgeModal: balance
    
    BridgeModal->>YomiRPC: getYominetEthBalance(address)
    YomiRPC-->>BridgeModal: balance
    
    User->>BridgeModal: Confirm bridge transaction
    
    BridgeModal->>BridgeService: fetchBridgeRoute(request)
    BridgeService-->>BridgeModal: route with operations
    
    BridgeModal->>BridgeService: fetchBridgeMsgs(operations)
    BridgeService-->>BridgeModal: EVM transaction payload
    
    BridgeModal->>Wallet: switchNetwork(chainId)
    Wallet-->>BridgeModal: network switched
    
    BridgeModal->>Wallet: sendTransaction(txPayload)
    Wallet-->>BridgeModal: txHash
    
    loop Poll for completion (up to 40 attempts)
        BridgeModal->>YomiRPC: getYominetEthBalance(address)
        YomiRPC-->>BridgeModal: updated balance
        alt Balance increased
            BridgeModal-->>User: Bridge complete ✓
        end
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

  • fix bridge logic for local builds #2156: Modifies bridge-aware visibility logic in same validator components (AccountRegistrar, WalletConnector) to coordinate with bridge modal flow.
  • Bridge Widget #2132: Introduces original bridge hook and UI component integrations; this PR refactors and relocates useBridgeOpener with new event-based implementation and comprehensive bridge modal.
  • Change bridge defaults #2140: Edits original useBridgeOpener implementation in network/utils/hooks.ts; this PR removes and relocates the hook to network/hooks with new behavior.

Suggested reviewers

  • chlwys
  • JirAcheron

Poem

🐰 A bridge was built across the chain so grand,
With modals bright and wallets in command.
The rabbit hops from Ethereum to Yomi's door,
With balance checks and polling evermore!
One click, one swap, one journey complete—
Bridging dreams now taste so sweet! 🌉

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Tisi.new bridge' refers to the main feature added: a bridge modal component that enables bridging ETH from source chains to Yominet, along with supporting infrastructure.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch tisi.new-bridge
📝 Coding Plan
  • Generate coding plan for human review comments

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.

❤️ Share

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

Tip

CodeRabbit can use Trivy to scan for security misconfigurations and secrets in Infrastructure as Code files.

Add a .trivyignore file to your project to customize which findings Trivy reports.

@Xenofluxx Xenofluxx marked this pull request as draft March 9, 2026 11:44
@vercel
Copy link

vercel bot commented Mar 9, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
kamigotchi-client Ready Ready Preview, Comment Mar 13, 2026 1:43am

Request Review

Copy link
Contributor

@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: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
packages/client/src/app/components/fixtures/menu/buttons/More.tsx (2)

36-36: ⚠️ Potential issue | 🟡 Minor

Remove duplicate semicolon.

There's a double semicolon ;; at the end of handleResetState. While JavaScript tolerates this, it's a code smell.

Proposed fix
-  };;
+  };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/client/src/app/components/fixtures/menu/buttons/More.tsx` at line
36, Remove the stray duplicate semicolon after the handleResetState function in
More.tsx: locate the closing of the handleResetState implementation (symbol
handleResetState) and delete the extra semicolon so the function end is
terminated by a single semicolon (or none if consistent with surrounding style).

61-61: ⚠️ Potential issue | 🟡 Minor

Remove duplicate semicolon.

Same issue here at the end of clearCache.

Proposed fix
-  };;
+  };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/client/src/app/components/fixtures/menu/buttons/More.tsx` at line
61, The function clearCache contains an extraneous duplicate semicolon (";;") at
its end; remove the extra semicolon so only a single semicolon terminates the
statement. Locate the clearCache function in the More component (symbol:
clearCache) and delete the redundant semicolon to clean up the syntax.
packages/client/types/ethers-contracts/NewbieVendorBuySystem.ts (1)

207-251: ⚠️ Potential issue | 🔴 Critical

The entire factories directory is missing, preventing the generated types from working.

The type file (NewbieVendorBuySystem.ts) has been regenerated with calcPrice, and the ABI JSON includes it. However, packages/client/types/ethers-contracts/factories/ does not exist. The index.ts file exports NewbieVendorBuySystem__factory from a non-existent directory, causing import failures. Regenerate the ethers contract factories.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/client/types/ethers-contracts/NewbieVendorBuySystem.ts` around lines
207 - 251, The generated types reference a missing factories directory and
export NewbieVendorBuySystem__factory from a non-existent path; regenerate the
ethers contract factories so the factory files (including
NewbieVendorBuySystem__factory) are created and match the ABI (which contains
calcPrice). Run the TypeChain/ethers factory generation command used by this
repo (e.g., the project's codegen or typechain script) to recreate
packages/client/types/ethers-contracts/factories/, then verify index.ts exports
point to the newly generated NewbieVendorBuySystem__factory and update any
mismatched export paths if needed.
🧹 Nitpick comments (10)
packages/client/src/app/components/modals/bridge/utils.ts (2)

7-16: Provider cache never evicts stale connections.

If an RPC endpoint becomes unresponsive, the cached JsonRpcProvider will continue to be reused. For a long-running app, consider adding a simple TTL or connection health check.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/client/src/app/components/modals/bridge/utils.ts` around lines 7 -
16, The providerCache in getRpcProvider stores JsonRpcProvider instances
indefinitely, so stale/unresponsive RPC connections get reused; update
getRpcProvider/providerCache to evict or refresh entries by adding a TTL and
health check: store a timestamp with each cached provider, on retrieval validate
age and optionally perform a lightweight call (e.g., provider.getBlockNumber or
a small health ping) to confirm responsiveness, and if the provider is too old
or fails the health check create a new JsonRpcProvider, replace the cache entry,
and return it; ensure you reference getRpcProvider, providerCache, and
JsonRpcProvider when locating code to modify.

30-39: Consider adding error handling for RPC failures.

Both getNativeBalance and getYominetEthBalance propagate RPC errors directly. Since these are polled every 5 seconds, transient network failures could disrupt the UI.

♻️ Proposed error handling pattern
 export async function getNativeBalance(rpcUrl: string, address: string): Promise<bigint> {
+  try {
     const provider = getRpcProvider(rpcUrl);
     return provider.getBalance(getAddress(address));
+  } catch (error) {
+    console.warn(`Failed to fetch native balance from ${rpcUrl}:`, error);
+    throw error; // Let caller handle or return 0n as fallback
+  }
 }

 export async function getYominetEthBalance(address: string): Promise<bigint> {
+  try {
     const provider = getRpcProvider(YOMINET_RPC_URL);
     const contract = new Contract(YOMINET_ETH_TOKEN_ADDRESS, yominetEthAbi, provider);
     const balance = await contract.balanceOf(getAddress(address));
     return toBigInt(balance);
+  } catch (error) {
+    console.warn('Failed to fetch Yominet ETH balance:', error);
+    throw error;
+  }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/client/src/app/components/modals/bridge/utils.ts` around lines 30 -
39, Wrap the RPC calls in getNativeBalance and getYominetEthBalance with
try/catch to handle transient RPC failures: catch errors from
getRpcProvider()/provider.getBalance() and contract.balanceOf(), log the error
(use existing logger or console.error) and return a safe fallback (e.g., 0n)
instead of letting the error propagate and break the poller; keep existing
helpers (getRpcProvider, getAddress, toBigInt, YOMINET_RPC_URL,
YOMINET_ETH_TOKEN_ADDRESS, yominetEthAbi, Contract) when implementing the
try/catch and fallback logic.
packages/client/src/app/components/validators/AccountRegistrar/Registration.tsx (1)

17-18: Threshold inconsistency across bridge prompts.

BRIDGE_THRESHOLD_ETH = 0.001 differs from GasConstants.Empty (≈0.00001 ETH) used in GasHarasser.tsx (line 97) and FundOperator.tsx (line 87). Users with balances between 0.00001 and 0.001 ETH will be prompted to bridge during registration but not in other contexts.

Consider either:

  1. Centralizing the bridge threshold constant in constants/gas.ts for consistency, or
  2. Documenting why registration intentionally uses a higher threshold (e.g., ensuring enough gas for the full registration flow).

Also applies to: 55-57

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/client/src/app/components/validators/AccountRegistrar/Registration.tsx`
around lines 17 - 18, BRIDGE_THRESHOLD_ETH in Registration.tsx (and the
duplicated values at lines 55-57) is inconsistent with GasConstants.Empty used
in GasHarasser.tsx and FundOperator.tsx; either centralize the threshold (create
or use a single exported constant in constants/gas.ts, e.g., export const
BRIDGE_THRESHOLD_ETH and replace the local const in Registration.tsx and other
callers) or, if registration must intentionally use a higher value, replace the
literal with a clearly named constant in constants/gas.ts and add a short inline
comment documenting why registration requires the higher threshold so all
modules reference the same symbol and behavior is consistent.
packages/client/src/app/components/modals/bridge/types.ts (1)

8-18: Consider adding runtime validation for BridgeMsgsPayload.

Per packages/client/src/network/bridge.ts, fetchBridgeMsgs returns Record<string, unknown>. In Bridge.tsx:247-253, the response is cast directly to BridgeMsgsPayload without validation. If the API returns an unexpected shape (missing txs array or differently structured evm_tx), accessing evmTx.to, evmTx.value, etc. will fail silently or throw at runtime.

Consider adding a type guard or validation function:

function isBridgeMsgsPayload(data: unknown): data is BridgeMsgsPayload {
  if (!data || typeof data !== 'object') return false;
  const obj = data as Record<string, unknown>;
  if (!Array.isArray(obj.txs)) return true; // txs is optional
  return obj.txs.every((tx) => 
    typeof tx === 'object' && tx !== null
  );
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/client/src/app/components/modals/bridge/types.ts` around lines 8 -
18, Add runtime validation for BridgeMsgsPayload by implementing a type guard
(e.g., isBridgeMsgsPayload) and using it where fetchBridgeMsgs' result is cast
to BridgeMsgsPayload (code around the Bridge component handling the fetch). The
guard should check that the top-level value is an object, that txs—if present—is
an array, and that each tx is an object whose optional evm_tx (if present)
contains the expected string fields (chain_id, to, value, data, optional
signer_address). Replace the unchecked cast in the Bridge component (where the
fetch result is cast to BridgeMsgsPayload) with a runtime check that only
proceeds to read evm_tx.* when isBridgeMsgsPayload returns true, otherwise
handle the error/invalid shape path.
packages/client/src/app/components/modals/bridge/Bridge.tsx (2)

247-256: API response cast lacks validation.

The fetchBridgeMsgs response is cast to BridgeMsgsPayload without runtime validation. If the API returns an unexpected structure, accessing evmTx.to, evmTx.value, or evmTx.data on line 272-274 will fail at runtime.

Consider validating the response structure before use, or adding more defensive checks around evmTx property access.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/client/src/app/components/modals/bridge/Bridge.tsx` around lines 247
- 256, The code blindly casts fetchBridgeMsgs(...) to BridgeMsgsPayload and then
assumes evmTx fields exist; add runtime validation after receiving msgs from
fetchBridgeMsgs to ensure msgs is an object, msgs.txs is an array, find a tx
with tx.evm_tx, and that the resulting evmTx has the required properties (to,
value, data) before using them; if validation fails, throw a clear error (or
handle fallback) instead of accessing undefined. Implement this with a small
type guard or schema check around the existing fetchBridgeMsgs call and before
the evmTx usage so functions and consumers like BridgeMsgsPayload,
fetchBridgeMsgs, and evm_tx are protected from malformed API responses.

267-277: Validate transaction hash type.

The eth_sendTransaction result is cast to string without validation. While this typically returns a hex string, wallet implementations vary. A malformed response could cause issues in subsequent transaction tracking.

Proposed defensive check
 const hash = (await wallet.request({
   method: 'eth_sendTransaction',
   params: [/* ... */],
 })) as string;
+
+ if (typeof hash !== 'string' || !hash.startsWith('0x')) {
+   throw new Error('Wallet returned invalid transaction hash');
+ }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/client/src/app/components/modals/bridge/Bridge.tsx` around lines 267
- 277, The code casts the result of wallet.request(..., method:
'eth_sendTransaction') to string (variable hash) without validation; update the
handler around the wallet.request call in Bridge.tsx to validate that the
returned value is a string and a properly formatted hex transaction hash (e.g.,
typeof hash === 'string' and matches /^0x[0-9a-fA-F]+$/), and if not, log or
throw a clear error (including the raw response) so subsequent tracking logic
that relies on hash (the hash variable and any follow-up functions that track
the transaction from evmTx) doesn’t operate on malformed data; also consider
normalizing or rejecting non-prefixed values before using them.
packages/client/src/network/bridge.ts (4)

1-3: Consider making API URLs configurable via environment variables.

Hardcoded URLs make it difficult to switch between environments (development, staging, production). Extracting these to environment variables improves deployment flexibility.

♻️ Suggested approach
-export const ROUTER_API_BASE_URL = 'https://router-api.initia.xyz/v2/fungible';
-export const BRIDGE_STATUS_URL =
-  'https://opinit-api-yominet-1.anvil.asia-southeast.initia.xyz/status';
+export const ROUTER_API_BASE_URL =
+  process.env.NEXT_PUBLIC_ROUTER_API_BASE_URL ?? 'https://router-api.initia.xyz/v2/fungible';
+export const BRIDGE_STATUS_URL =
+  process.env.NEXT_PUBLIC_BRIDGE_STATUS_URL ??
+  'https://opinit-api-yominet-1.anvil.asia-southeast.initia.xyz/status';
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/client/src/network/bridge.ts` around lines 1 - 3, Replace the
hardcoded constants ROUTER_API_BASE_URL and BRIDGE_STATUS_URL with values read
from environment variables (e.g., process.env.ROUTER_API_BASE_URL and
process.env.BRIDGE_STATUS_URL) and provide sensible defaults so existing
behavior remains unchanged; update the export lines for ROUTER_API_BASE_URL and
BRIDGE_STATUS_URL to use the env fallback pattern and add brief comments
indicating the env var names so deployments can override them for
dev/staging/prod.

124-143: Add timeout to status check fetch.

Same concern as postRouterApi - the fetch has no timeout and could hang indefinitely if the status endpoint is unresponsive.

♻️ Suggested fix
   try {
     const response = await fetch(BRIDGE_STATUS_URL, {
       method: 'GET',
       headers: { 'Content-Type': 'application/json' },
+      signal: AbortSignal.timeout(10_000),
     });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/client/src/network/bridge.ts` around lines 124 - 143, The
getBridgeServiceStatus function lacks a fetch timeout and can hang; modify
getBridgeServiceStatus to use an AbortController and pass its signal to
fetch(BRIDGE_STATUS_URL), start a timer (e.g., 5s or configurable) that calls
controller.abort() on timeout, clear the timer when fetch completes, and ensure
the catch block handles the abort case (return healthy: false with an
appropriate detail message) while still delegating JSON parsing to
interpretBridgeHealth for successful responses.

54-72: Add request timeout to prevent indefinite hangs.

The fetch call has no timeout configured. If the bridge API becomes unresponsive, requests will hang indefinitely, potentially freezing the UI. Consider using AbortSignal.timeout() for automatic cancellation.

♻️ Suggested fix with timeout
 async function postRouterApi<TResponse>(
   path: 'route' | 'msgs',
   body: Record<string, unknown>
 ): Promise<TResponse> {
   const response = await fetch(`${ROUTER_API_BASE_URL}/${path}`, {
     method: 'POST',
     headers: {
       'Content-Type': 'application/json',
     },
     body: JSON.stringify(body),
+    signal: AbortSignal.timeout(30_000),
   });

   if (!response.ok) {
     const error = await response.text().catch(() => '');
     throw new Error(`Bridge ${path} request failed (${response.status}): ${error}`);
   }

   return (await response.json()) as TResponse;
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/client/src/network/bridge.ts` around lines 54 - 72, The
postRouterApi function currently uses fetch without a timeout which can hang;
update it to create an AbortSignal with a finite timeout (e.g., via
AbortSignal.timeout(ms)) and pass that signal to fetch so the request is
automatically aborted on timeout, then handle the abort case by catching the
error and throwing a clear Error including that it timed out; modify
postRouterApi to accept or create the timeout value, use
AbortSignal.timeout(...) as the signal option in fetch, and ensure the existing
error path still returns the response body text or JSON appropriately.

99-122: Consider logging unknown response shapes for observability.

Defaulting to healthy: true for unknown shapes avoids false-negative failures, but could silently mask API changes or issues. Adding a warning log when falling through to the default would aid debugging without changing the fail-safe behavior.

💡 Optional enhancement
   // Unknown shape: avoid false negative hard-fail.
+  console.warn('[bridge] Unknown health response shape, assuming healthy:', payload);
   return { healthy: true };
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/client/src/network/bridge.ts` around lines 99 - 122,
interpretBridgeHealth currently returns { healthy: true } for unknown payload
shapes without any logging; add a non-breaking warning log right before that
return so observability captures unexpected responses. In the
interpretBridgeHealth function, when falling through to the default return, call
the existing logger (or introduce a module logger if none exists) to warn with
context including the raw payload and that BridgeServiceStatus parsing failed,
but do not change the returned value or flow; reference the function name
interpretBridgeHealth and the BridgeServiceStatus return to locate where to
insert the log.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/client/src/app/components/modals/bridge/Bridge.tsx`:
- Line 302: In Bridge.tsx update the call to setStatusText inside the Bridge
component so the success message includes proper punctuation and spacing (e.g.,
change the string passed to setStatusText('Bridge Complete Congratulations') to
a properly punctuated sentence like "Bridge complete. Congratulations."). Locate
the setStatusText invocation in the Bridge component and replace the message
string accordingly.
- Around line 283-306: The polling loop in Bridge.tsx (POLL_MAX_ATTEMPTS)
continues after the modal closes, causing API calls and state updates on an
unmounted component; fix it by wiring an abort/check into the loop: create an
AbortController stored in a ref (or reuse an existing ref), pass its signal to
any async helpers (getBridgeServiceStatus/getYominetEthBalance) or check the
ref.signal.aborted, and on modal close (isOpen -> false) call
controller.abort(); additionally, inside the loop check isOpen (or
controller.signal.aborted) before awaiting or calling setters
(setIsServiceDegraded/setYomiBalance/setStatusText/setIsBridging) and break
early to avoid updates after unmount.
- Around line 241-242: The code is using routeAmountOut and amountOut without
guaranteeing either exists before calling fetchBridgeMsgs; update the Bridge.tsx
logic around routeAmountOut/amountOut to explicitly validate and handle missing
values: locate the lines that compute routeAmountOut and amountOut and either
(a) set a safe fallback (e.g. convert to a validated numeric/zero string) or (b)
return/throw/early-exit and surface a user-facing error if both amount_out and
amount_in are absent, and ensure you only call fetchBridgeMsgs when amountOut is
a defined, acceptable value; adjust any related UI/error state so
fetchBridgeMsgs never receives undefined.

---

Outside diff comments:
In `@packages/client/src/app/components/fixtures/menu/buttons/More.tsx`:
- Line 36: Remove the stray duplicate semicolon after the handleResetState
function in More.tsx: locate the closing of the handleResetState implementation
(symbol handleResetState) and delete the extra semicolon so the function end is
terminated by a single semicolon (or none if consistent with surrounding style).
- Line 61: The function clearCache contains an extraneous duplicate semicolon
(";;") at its end; remove the extra semicolon so only a single semicolon
terminates the statement. Locate the clearCache function in the More component
(symbol: clearCache) and delete the redundant semicolon to clean up the syntax.

In `@packages/client/types/ethers-contracts/NewbieVendorBuySystem.ts`:
- Around line 207-251: The generated types reference a missing factories
directory and export NewbieVendorBuySystem__factory from a non-existent path;
regenerate the ethers contract factories so the factory files (including
NewbieVendorBuySystem__factory) are created and match the ABI (which contains
calcPrice). Run the TypeChain/ethers factory generation command used by this
repo (e.g., the project's codegen or typechain script) to recreate
packages/client/types/ethers-contracts/factories/, then verify index.ts exports
point to the newly generated NewbieVendorBuySystem__factory and update any
mismatched export paths if needed.

---

Nitpick comments:
In `@packages/client/src/app/components/modals/bridge/Bridge.tsx`:
- Around line 247-256: The code blindly casts fetchBridgeMsgs(...) to
BridgeMsgsPayload and then assumes evmTx fields exist; add runtime validation
after receiving msgs from fetchBridgeMsgs to ensure msgs is an object, msgs.txs
is an array, find a tx with tx.evm_tx, and that the resulting evmTx has the
required properties (to, value, data) before using them; if validation fails,
throw a clear error (or handle fallback) instead of accessing undefined.
Implement this with a small type guard or schema check around the existing
fetchBridgeMsgs call and before the evmTx usage so functions and consumers like
BridgeMsgsPayload, fetchBridgeMsgs, and evm_tx are protected from malformed API
responses.
- Around line 267-277: The code casts the result of wallet.request(..., method:
'eth_sendTransaction') to string (variable hash) without validation; update the
handler around the wallet.request call in Bridge.tsx to validate that the
returned value is a string and a properly formatted hex transaction hash (e.g.,
typeof hash === 'string' and matches /^0x[0-9a-fA-F]+$/), and if not, log or
throw a clear error (including the raw response) so subsequent tracking logic
that relies on hash (the hash variable and any follow-up functions that track
the transaction from evmTx) doesn’t operate on malformed data; also consider
normalizing or rejecting non-prefixed values before using them.

In `@packages/client/src/app/components/modals/bridge/types.ts`:
- Around line 8-18: Add runtime validation for BridgeMsgsPayload by implementing
a type guard (e.g., isBridgeMsgsPayload) and using it where fetchBridgeMsgs'
result is cast to BridgeMsgsPayload (code around the Bridge component handling
the fetch). The guard should check that the top-level value is an object, that
txs—if present—is an array, and that each tx is an object whose optional evm_tx
(if present) contains the expected string fields (chain_id, to, value, data,
optional signer_address). Replace the unchecked cast in the Bridge component
(where the fetch result is cast to BridgeMsgsPayload) with a runtime check that
only proceeds to read evm_tx.* when isBridgeMsgsPayload returns true, otherwise
handle the error/invalid shape path.

In `@packages/client/src/app/components/modals/bridge/utils.ts`:
- Around line 7-16: The providerCache in getRpcProvider stores JsonRpcProvider
instances indefinitely, so stale/unresponsive RPC connections get reused; update
getRpcProvider/providerCache to evict or refresh entries by adding a TTL and
health check: store a timestamp with each cached provider, on retrieval validate
age and optionally perform a lightweight call (e.g., provider.getBlockNumber or
a small health ping) to confirm responsiveness, and if the provider is too old
or fails the health check create a new JsonRpcProvider, replace the cache entry,
and return it; ensure you reference getRpcProvider, providerCache, and
JsonRpcProvider when locating code to modify.
- Around line 30-39: Wrap the RPC calls in getNativeBalance and
getYominetEthBalance with try/catch to handle transient RPC failures: catch
errors from getRpcProvider()/provider.getBalance() and contract.balanceOf(), log
the error (use existing logger or console.error) and return a safe fallback
(e.g., 0n) instead of letting the error propagate and break the poller; keep
existing helpers (getRpcProvider, getAddress, toBigInt, YOMINET_RPC_URL,
YOMINET_ETH_TOKEN_ADDRESS, yominetEthAbi, Contract) when implementing the
try/catch and fallback logic.

In
`@packages/client/src/app/components/validators/AccountRegistrar/Registration.tsx`:
- Around line 17-18: BRIDGE_THRESHOLD_ETH in Registration.tsx (and the
duplicated values at lines 55-57) is inconsistent with GasConstants.Empty used
in GasHarasser.tsx and FundOperator.tsx; either centralize the threshold (create
or use a single exported constant in constants/gas.ts, e.g., export const
BRIDGE_THRESHOLD_ETH and replace the local const in Registration.tsx and other
callers) or, if registration must intentionally use a higher value, replace the
literal with a clearly named constant in constants/gas.ts and add a short inline
comment documenting why registration requires the higher threshold so all
modules reference the same symbol and behavior is consistent.

In `@packages/client/src/network/bridge.ts`:
- Around line 1-3: Replace the hardcoded constants ROUTER_API_BASE_URL and
BRIDGE_STATUS_URL with values read from environment variables (e.g.,
process.env.ROUTER_API_BASE_URL and process.env.BRIDGE_STATUS_URL) and provide
sensible defaults so existing behavior remains unchanged; update the export
lines for ROUTER_API_BASE_URL and BRIDGE_STATUS_URL to use the env fallback
pattern and add brief comments indicating the env var names so deployments can
override them for dev/staging/prod.
- Around line 124-143: The getBridgeServiceStatus function lacks a fetch timeout
and can hang; modify getBridgeServiceStatus to use an AbortController and pass
its signal to fetch(BRIDGE_STATUS_URL), start a timer (e.g., 5s or configurable)
that calls controller.abort() on timeout, clear the timer when fetch completes,
and ensure the catch block handles the abort case (return healthy: false with an
appropriate detail message) while still delegating JSON parsing to
interpretBridgeHealth for successful responses.
- Around line 54-72: The postRouterApi function currently uses fetch without a
timeout which can hang; update it to create an AbortSignal with a finite timeout
(e.g., via AbortSignal.timeout(ms)) and pass that signal to fetch so the request
is automatically aborted on timeout, then handle the abort case by catching the
error and throwing a clear Error including that it timed out; modify
postRouterApi to accept or create the timeout value, use
AbortSignal.timeout(...) as the signal option in fetch, and ensure the existing
error path still returns the response body text or JSON appropriately.
- Around line 99-122: interpretBridgeHealth currently returns { healthy: true }
for unknown payload shapes without any logging; add a non-breaking warning log
right before that return so observability captures unexpected responses. In the
interpretBridgeHealth function, when falling through to the default return, call
the existing logger (or introduce a module logger if none exists) to warn with
context including the raw payload and that BridgeServiceStatus parsing failed,
but do not change the returned value or flow; reference the function name
interpretBridgeHealth and the BridgeServiceStatus return to locate where to
insert the log.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2f43cde9-ffc9-49f0-9a5d-4fe39e8b1fd4

📥 Commits

Reviewing files that changed from the base of the PR and between 9b09469 and 544dc36.

📒 Files selected for processing (21)
  • packages/client/abi/NewbieVendorBuySystem.json
  • packages/client/index.html
  • packages/client/src/app/components/fixtures/menu/buttons/More.tsx
  • packages/client/src/app/components/index.ts
  • packages/client/src/app/components/library/modals/ModalWrapper.tsx
  • packages/client/src/app/components/modals/FundOperator.tsx
  • packages/client/src/app/components/modals/bridge/Bridge.tsx
  • packages/client/src/app/components/modals/bridge/constants.ts
  • packages/client/src/app/components/modals/bridge/index.ts
  • packages/client/src/app/components/modals/bridge/types.ts
  • packages/client/src/app/components/modals/bridge/utils.ts
  • packages/client/src/app/components/validators/AccountRegistrar/AccountRegistrar.tsx
  • packages/client/src/app/components/validators/AccountRegistrar/Registration.tsx
  • packages/client/src/app/components/validators/GasHarasser.tsx
  • packages/client/src/app/components/validators/WalletConnector/WalletConnector.tsx
  • packages/client/src/app/stores/visibility.ts
  • packages/client/src/network/bridge.ts
  • packages/client/src/network/hooks/useBridgeOpener.ts
  • packages/client/src/network/utils/hooks.ts
  • packages/client/types/ethers-contracts/NewbieVendorBuySystem.ts
  • packages/client/types/ethers-contracts/index.ts
💤 Files with no reviewable changes (1)
  • packages/client/src/network/utils/hooks.ts

Comment on lines +241 to +242
const routeAmountOut = (route as { amount_out?: unknown }).amount_out;
const amountOut = typeof routeAmountOut === 'string' ? routeAmountOut : route.amount_in;
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Check if BridgeRouteResponse guarantees amount_out or amount_in fields

# Search for the response type definition
rg -n -A10 'BridgeRouteResponse' packages/client/src/network/bridge.ts

Repository: Asphodel-OS/kamigotchi

Length of output: 1101


🏁 Script executed:

# Read the Bridge.tsx file around lines 241-242 with context
sed -n '230,260p' packages/client/src/app/components/modals/bridge/Bridge.tsx

Repository: Asphodel-OS/kamigotchi

Length of output: 1536


Validate amountOut before passing to fetchBridgeMsgs.

Since BridgeRouteResponse only guarantees operations and uses Record<string, unknown> for other fields, neither amount_out nor amount_in are required. If both are missing, amountOut will be undefined and passed to fetchBridgeMsgs at line 248, which likely doesn't accept undefined values.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/client/src/app/components/modals/bridge/Bridge.tsx` around lines 241
- 242, The code is using routeAmountOut and amountOut without guaranteeing
either exists before calling fetchBridgeMsgs; update the Bridge.tsx logic around
routeAmountOut/amountOut to explicitly validate and handle missing values:
locate the lines that compute routeAmountOut and amountOut and either (a) set a
safe fallback (e.g. convert to a validated numeric/zero string) or (b)
return/throw/early-exit and surface a user-facing error if both amount_out and
amount_in are absent, and ensure you only call fetchBridgeMsgs when amountOut is
a defined, acceptable value; adjust any related UI/error state so
fetchBridgeMsgs never receives undefined.

Comment on lines +283 to +306
for (let attempt = 0; attempt < POLL_MAX_ATTEMPTS; attempt++) {
let pollIntervalMs = POLL_INTERVAL_MS;
if (attempt % STATUS_RECHECK_EVERY_ATTEMPTS === 0) {
const pollServiceStatus = await getBridgeServiceStatus();
const degraded = !pollServiceStatus.healthy;
setIsServiceDegraded(degraded);

if (degraded) {
pollIntervalMs = DEGRADED_POLL_INTERVAL_MS;
setStatusText(
'Bridge submitted. Service is degraded right now, so this may take longer.'
);
}
}

await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
const nextBalance = await getYominetEthBalance(selectedAddress);
setYomiBalance(nextBalance);
if (nextBalance > baseline) {
setStatusText('Bridge Complete Congratulations');
setIsBridging(false);
return;
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Polling continues after modal is closed.

If the user closes the modal during the polling loop (lines 283-306), the loop continues executing API calls and state updates on an unmounted component. This wastes resources and can cause React warnings about state updates on unmounted components.

Consider adding an abort signal or checking isOpen within the loop:

Proposed fix using AbortController pattern
 const startBridge = async () => {
+  const abortController = new AbortController();
+  const signal = abortController.signal;
+
   // ... existing code ...

   const baseline = latestBalances.yomi;
   for (let attempt = 0; attempt < POLL_MAX_ATTEMPTS; attempt++) {
+    if (signal.aborted) return;
+
     let pollIntervalMs = POLL_INTERVAL_MS;
     // ... rest of loop ...
   }
 };

You'll also need to store the controller in a ref and abort it when isOpen becomes false.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/client/src/app/components/modals/bridge/Bridge.tsx` around lines 283
- 306, The polling loop in Bridge.tsx (POLL_MAX_ATTEMPTS) continues after the
modal closes, causing API calls and state updates on an unmounted component; fix
it by wiring an abort/check into the loop: create an AbortController stored in a
ref (or reuse an existing ref), pass its signal to any async helpers
(getBridgeServiceStatus/getYominetEthBalance) or check the ref.signal.aborted,
and on modal close (isOpen -> false) call controller.abort(); additionally,
inside the loop check isOpen (or controller.signal.aborted) before awaiting or
calling setters
(setIsServiceDegraded/setYomiBalance/setStatusText/setIsBridging) and break
early to avoid updates after unmount.

const nextBalance = await getYominetEthBalance(selectedAddress);
setYomiBalance(nextBalance);
if (nextBalance > baseline) {
setStatusText('Bridge Complete Congratulations');
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix completion message formatting.

The success message is missing punctuation between sentences.

Proposed fix
- setStatusText('Bridge Complete Congratulations');
+ setStatusText('Bridge complete. Congratulations!');
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
setStatusText('Bridge Complete Congratulations');
setStatusText('Bridge complete. Congratulations!');
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/client/src/app/components/modals/bridge/Bridge.tsx` at line 302, In
Bridge.tsx update the call to setStatusText inside the Bridge component so the
success message includes proper punctuation and spacing (e.g., change the string
passed to setStatusText('Bridge Complete Congratulations') to a properly
punctuated sentence like "Bridge complete. Congratulations."). Locate the
setStatusText invocation in the Bridge component and replace the message string
accordingly.

- Add BridgeFundStep: after bridge success, prompt user to fund operator directly from bridge modal (reserves 0.0002 ETH for gas)
- Rewrite GasHarasser & FundOperator inputs: text-based decimal input with regex validation, smart tier defaults, StepButton +/-, clear-on-focus/blur-to-restore
- Add reusable StepButton component (press-and-hold increment/decrement)
- Restyle BridgeForm inputs to match: ETH icons, StepButtons, 0.001 minimum
- Fix loading flashes: accountChecked gate, isLive/burnerAddress guards in AccountRegistrar, operatorHasGas default true
- Fix OperatorUpdater false trigger: dead-address guard, skip button
- Fix ValidatorWrapper ExitButton using wrong store key (id vs divName)
- Fix wallet switch stale state: clear account store and query caches on logout
- Fix Settings modal showing dead addresses before account creation
- Fix bridge check regression: restore useTokens (ERC20 ETH) instead of useBalance (native) for needsToBridge
- Enforce 0.001 ETH minimum on bridge amount (input, blur, nudge, and prefill)
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.

2 participants