Skip to content

Conversation

@tractorss
Copy link
Contributor

@tractorss tractorss commented Jun 18, 2025

Summary by CodeRabbit

  • New Features

    • Simulate transaction lists via Tenderly from the UI (Simulate button + Inspect link) and a backend simulation endpoint with validation and rate limiting.
    • Contract metadata fetching with dynamic validation feedback in the transaction modal and a hook for contract info.
    • Reusable external link component for outbound links.
  • Chores

    • Updated environment example to include Tenderly vars, allowed origins, and Etherscan key.
    • Standardized list-related imports.

@coderabbitai
Copy link

coderabbitai bot commented Jun 18, 2025

Walkthrough

Adds Tenderly-based simulation support: new env vars, backend routes for contract info and simulation (with validation and rate limiting), Tenderly utilities, a hook for contract info, Lists context simulation integration, UI updates (simulate action, Tenderly link, dynamic ABI feedback), and a small dependency bump.

Changes

Cohort / File(s) Change Summary
Env / config
web/.env.example
Added Tenderly env vars, ALLOWED_ORIGINS, and ETHERSCAN_API_KEY.
API endpoints
web/src/app/api/simulate/route.ts, web/src/app/api/contract/route.ts
Added POST /api/simulate (validation, rate limiting, calls simulation utilities) and GET /api/contract (rate limiting, Tenderly primary + Etherscan fallback).
Simulation utils (Tenderly)
web/src/utils/tenderly/encodeState.ts, web/src/utils/tenderly/simulateWithTenderly.ts
New modules to build/encode state overrides and perform simulations + enable sharing via Tenderly API.
Request validation & throttling
web/src/utils/simulateRouteUtils.ts
New request body validation and simple per-IP rate limit utility used by API routes.
Lists context & integration
web/src/context/NewListsContext.tsx, web/src/app/(main)/governor/[governorAddress]/MyLists/Lists.tsx
Added simulateList, isSimulating, simulations map in provider; UI: "Simulate" button, loading state, and "Inspect on Tenderly" link; conditional modal rendering.
Contract info hook & API usage
web/src/hooks/useContractInfo.ts, web/src/app/(main)/governor/[governorAddress]/MyLists/AddTxnModal.tsx, web/src/app/(main)/governor/[governorAddress]/MyLists/JsonInput.tsx
New useContractInfo hook; AddTxnModal fetches contract metadata and shows dynamic validation feedback; JSONInput accepts optional abi prop and syncs on change.
Small UI/component additions & imports
web/src/components/ExternalLink.tsx, web/src/app/(main)/governor/[governorAddress]/MyLists/Header.tsx, .../SubmissionButton.tsx, .../index.tsx, web/src/utils/txnBuilder/constructSubmissionData.ts
Added ExternalLink component; updated imports to NewListsContext; updated type import paths where applicable.
Dependency
web/package.json
Bumped @kleros/ui-components-library from ^3.4.5 to ^3.6.0.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    actor User
    participant UI
    participant ListsContext
    participant API
    participant Tenderly
    Note over UI,ListsContext: User initiates simulation flow
    User->>UI: Click "Simulate" (listId)
    UI->>ListsContext: simulateList(listId)
    ListsContext->>API: POST /api/simulate { networkId, governorAddress, transactions }
    API->>Tenderly: POST /encode_state (build state override)
    Tenderly-->>API: encoded state
    API->>Tenderly: POST /simulate (with encoded state)
    Tenderly-->>API: { simulationId }
    API->>ListsContext: { status: true, simulationLink }
    ListsContext->>UI: simulation result
    UI->>User: show "Inspect on Tenderly" link / success
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • alcercu

Poem

🐇
I hopped through code to make a trail,
Sent txns to Tenderly down the vale.
Buttons that shimmer, links that gleam,
Simulate, inspect — a tester's dream.
Carrot cheers for shared state and steam! 🥕

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title "Feat/tenderly simulations" clearly and concisely summarizes the primary enhancement introduced by this pull request—adding Tenderly-based simulation capabilities—without including unnecessary details or noise.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/tenderly-simulations

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d9ad34b and 89466c6.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (1)
  • web/package.json (1 hunks)
🔇 Additional comments (1)
web/package.json (1)

18-18: @kleros/ui-components-library upgrade looks good

Version bump aligns with the new UI features introduced elsewhere in the PR. Nice catch keeping the dependency current.


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

@tractorss tractorss changed the base branch from master to feat/functionality June 18, 2025 13:47
@netlify
Copy link

netlify bot commented Jun 18, 2025

Deploy Preview for kleros-governor-v2 ready!

Name Link
🔨 Latest commit 89466c6
🔍 Latest deploy log https://app.netlify.com/projects/kleros-governor-v2/deploys/68e3a373cbf9b30008b9a82d
😎 Deploy Preview https://deploy-preview-13--kleros-governor-v2.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

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: 9

🧹 Nitpick comments (7)
web/.env.example (1)

4-10: Tighten .env hygiene (ordering, quoting & trailing newline)

Minor nits picked up by dotenv-linter:

  1. Keys are currently out of alphabetical order – helps merge-conflict hygiene.
  2. Placeholder values don’t need to be wrapped in quotes; keeps the file free of superfluous chars.
  3. Missing terminal newline.
  4. ALLOWED_ORIGINS will be parsed as a single entry; if multiple origins are expected prefer a comma-separated list and document it in a comment.
-export NEXT_PUBLIC_COURT_SITE="https://dev--kleros-v2-testnet.netlify.app/#"

-# tenderly simulations
-export TENDERLY_ACCOUNT_NAME="test"
-export TENDERLY_PROJECT_NAME="governor-test"
-export TENDERLY_ACCESS_KEY="abcabcabcabcabcabc"
-export ALLOWED_ORIGINS="http://localhost:3000"
+# tenderly simulations
+export ALLOWED_ORIGINS=http://localhost:3000
+export TENDERLY_ACCOUNT_NAME=test
+export TENDERLY_PROJECT_NAME=governor-test
+export TENDERLY_ACCESS_KEY=abcabcabcabcabcabc
+

Not blocking, but adopting the linter suggestions keeps the file clean and predictable.

web/src/components/ExternalLink.tsx (1)

1-6: Slim down & make the link truly external

A couple of quick wins:

  1. React import is redundant in a Next 13/14 project using the new JSX runtime.
  2. next/link is optimised for internal routing; for external links an <a> avoids unnecessary prefetching.
    – If you keep Link, add prefetch={false} to suppress the prefetch.
  3. Consider an aria-label for better accessibility.
-import React, { HTMLAttributes } from "react";
-
-import clsx from "clsx";
-import Link from "next/link";
+import { HTMLAttributes } from "react";
+import clsx from "clsx";

@@
-    <Link
-      href={url}
-      className={clsx("block w-fit text-center hover:brightness-[1.2]", className)}
-      target="_blank"
-      rel="noreferrer noopener"
+    <a
+      href={url}
+      className={clsx("block w-fit text-center hover:brightness-[1.2]", className)}
+      target="_blank"
+      rel="noreferrer noopener"
+      aria-label={text}
     >
@@
-    </Link>
+    </a>

Entirely optional, but keeps bundle size and runtime behaviour tidy.

Also applies to: 14-24

web/src/utils/txnBuilder/constructSubmissionData.ts (1)

13-20: String concatenation & BigInt parsing could be simplified and hardened

  1. titles is manually concatenated inside the loop. Array.prototype.join() is clearer and O(n) instead of the current O(n²) ­string-copy pattern.

  2. BigInt(txn.txnValue) will throw if txnValue is not a strict base-10 integer string.
    • If you ever accept "0x…" or decimal‐float strings, wrap the cast with validation or pass a radix.
    • Consider returning a descriptive error instead of a hard crash.

Example refactor:

-const addresses: Address[] = [];
-const values: bigint[] = [];
-let data: Hex = "0x";
-const dataSizes: bigint[] = [];
-let titles = "";
-
-transactions.forEach((txn) => {
-  addresses.push(txn.to);
-  values.push(BigInt(txn.txnValue));
-  dataSizes.push(BigInt(txn.data.slice(2).length / 2));
-  titles += `${txn.name},`;
-  data += txn.data.slice(2);
-});
-
-titles = titles.replace(/,$/, ""); // trim trailing comma
+const addresses  = transactions.map((t) => t.to);
+const values     = transactions.map((t) => BigInt(t.txnValue));
+const dataSizes  = transactions.map((t) => BigInt(t.data.length / 2 - 1)); // skip 0x
+const titles     = transactions.map((t) => t.name).join(",");
+const data       = transactions.reduce((acc, t) => acc + t.data.slice(2), "0x");
web/src/context/NewListsContext.tsx (1)

60-63: Unused publicClient – remove or leverage

usePublicClient is fetched but never used except for the undefined-check shortcut. If Tenderly does not rely on it, drop the hook to reduce re-renders; otherwise actually forward the live chainId instead of the constant default (see next comment).

web/src/utils/tenderly/simulateWithTenderly.ts (1)

24-28: Declare explicit return type for simulateWithTenderly

The function currently relies on inference and effectively returns any.
Explicitly declaring Promise<SimulationResult> makes the compiler catch mismatched shapes coming back from Tenderly.

-export const simulateWithTenderly = async (
+export const simulateWithTenderly = async (
   networkId: number,
   governorAddress: Address,
   transactions: ListTransaction[]
-) => {
+): Promise<SimulationResult> => {
web/src/utils/tenderly/encodeState.ts (1)

62-69: Normalise governor address casing once

To avoid key-matching issues across the codebase, convert addresses to lowercase (or checksum) at the point of building the object:

-      [governorAddress]: {
+      [governorAddress.toLowerCase()]: {

Do the same for the map lookup when parsing the response.

web/src/utils/simulateRouteUtils.ts (1)

68-90: In-memory rate-limit map leaks over time

ipRequestMap grows unbounded with new IPs and never evicts old entries.
Add a clean-up step after each request:

 // Reset count if the window has passed
 if (now > rateData.resetTime) {
   rateData.count = 0;
   rateData.resetTime = now + RATE_LIMIT_WINDOW;
 }
+
+// GC old entries once per call
+for (const [key, { resetTime }] of ipRequestMap) {
+  if (now > resetTime) ipRequestMap.delete(key);
+}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e4a3c56 and 424c1ab.

⛔ Files ignored due to path filters (2)
  • web/src/assets/svgs/icons/link-arrow.svg is excluded by !**/*.svg
  • web/src/assets/svgs/logos/tenderly.svg is excluded by !**/*.svg
📒 Files selected for processing (13)
  • web/.env.example (1 hunks)
  • web/src/app/(main)/governor/[governorAddress]/MyLists/AddTxnModal.tsx (1 hunks)
  • web/src/app/(main)/governor/[governorAddress]/MyLists/Header.tsx (1 hunks)
  • web/src/app/(main)/governor/[governorAddress]/MyLists/Lists.tsx (4 hunks)
  • web/src/app/(main)/governor/[governorAddress]/MyLists/SubmissionButton.tsx (1 hunks)
  • web/src/app/(main)/governor/[governorAddress]/MyLists/index.tsx (1 hunks)
  • web/src/app/api/simulate/route.ts (1 hunks)
  • web/src/components/ExternalLink.tsx (1 hunks)
  • web/src/context/NewListsContext.tsx (4 hunks)
  • web/src/utils/simulateRouteUtils.ts (1 hunks)
  • web/src/utils/tenderly/encodeState.ts (1 hunks)
  • web/src/utils/tenderly/simulateWithTenderly.ts (1 hunks)
  • web/src/utils/txnBuilder/constructSubmissionData.ts (1 hunks)
🧰 Additional context used
🪛 dotenv-linter (3.3.0)
web/.env.example

[warning] 4-4: [UnorderedKey] The NEXT_PUBLIC_COURT_SITE key should go before the NEXT_PUBLIC_REOWN_PROJECT_ID key


[warning] 7-7: [QuoteCharacter] The value has quote characters (', ")


[warning] 8-8: [QuoteCharacter] The value has quote characters (', ")


[warning] 9-9: [QuoteCharacter] The value has quote characters (', ")


[warning] 9-9: [UnorderedKey] The TENDERLY_ACCESS_KEY key should go before the TENDERLY_ACCOUNT_NAME key


[warning] 10-10: [EndingBlankLine] No blank line at the end of the file


[warning] 10-10: [QuoteCharacter] The value has quote characters (', ")


[warning] 10-10: [UnorderedKey] The ALLOWED_ORIGINS key should go before the TENDERLY_ACCESS_KEY key

🔇 Additional comments (6)
web/src/app/(main)/governor/[governorAddress]/MyLists/AddTxnModal.tsx (1)

8-10: Correct context import – looks good

The switch to NewListsContext aligns this component with the updated provider and simulation features. No further issues spotted in this hunk.

web/src/app/(main)/governor/[governorAddress]/MyLists/index.tsx (1)

5-5: Import path update is accurate

Importing ListsProvider from the new context avoids the typo in the previous path and enables simulation state – all good.

web/src/app/(main)/governor/[governorAddress]/MyLists/SubmissionButton.tsx (1)

9-10: Context path fix acknowledged

The updated import resolves the casing typo and connects the component to the new lists API – no concerns.

web/src/app/(main)/governor/[governorAddress]/MyLists/Header.tsx (1)

4-4: Import path update looks correct

The switch to NewListsContext aligns this component with the new simulation-aware provider. No further issues spotted.

web/src/app/api/simulate/route.ts (1)

50-57: Hard dependency on simulationResult.simulation.id without null-checking

simulateWithTenderly can fail silently and return {error: …}. Guard against this to avoid Cannot read property 'id' of undefined'.

- if (!isUndefined(simulationResult.simulation.id)) {
+ if (simulationResult?.simulation?.id) {
web/src/context/NewListsContext.tsx (1)

170-171: Array.prototype.toReversed() is still Stage 3

Node 18 / many browsers don’t implement it yet. Use slice().reverse() for wider compatibility or ensure the target runtime supports ES2023 array methods.

- lists: [...lists.values()].toReversed(),
+ lists: [...lists.values()].slice().reverse(),

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 (3)
web/src/context/NewListsContext.tsx (3)

61-61: Global isSimulating state couples all lists

The global simulation state still prevents concurrent simulations of different lists, which can degrade user experience when managing multiple lists.

Also applies to: 183-183


76-83: Content-Type header missing in POST request

The POST request is still missing the required Content-Type header, which can cause issues with gateways, proxies, or CORS pre-flight checks.


79-79: Chain mismatch – hard-coding DEFAULT_CHAIN.id ignores current network

The code still hard-codes DEFAULT_CHAIN.id instead of using the dynamic chainId value, which can produce incorrect simulations when users are connected to a different chain.

🧹 Nitpick comments (2)
web/src/context/NewListsContext.tsx (2)

105-105: Remove publicClient from dependency array to prevent unnecessary re-renders

Including publicClient in the dependency array causes the simulateList function to be recreated whenever the client changes, which can happen frequently and cause performance issues.

-[lists, governorAddress, publicClient]
+[lists, governorAddress]

95-95: Improve error logging for debugging

The current error logging doesn't provide enough context for debugging simulation failures in production environments.

-console.log(error instanceof Error ? error.message : `Simulation Failed`);
+console.error('Simulation failed for list:', listId, error instanceof Error ? error.message : 'Unknown error', error);
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 424c1ab and ff9ddb2.

📒 Files selected for processing (2)
  • web/src/app/(main)/governor/[governorAddress]/MyLists/Lists.tsx (4 hunks)
  • web/src/context/NewListsContext.tsx (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • web/src/app/(main)/governor/[governorAddress]/MyLists/Lists.tsx
🔇 Additional comments (1)
web/src/context/NewListsContext.tsx (1)

67-67: simulations Map recreated on every render

The useMemo with an empty dependency array still creates a new Map instance on every render, defeating its purpose and causing unnecessary re-renders of consuming components.

-const simulations = useMemo(() => new Map<string, string>(), []);
+const [simulations] = useState(() => new Map<string, string>());

Likely an incorrect or invalid review comment.

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 (4)
web/.env.example (2)

6-11: Add documentation comments for new environment variables.

Consider adding comments above each new environment variable section to explain their purpose and usage, especially for sensitive keys like API keys.

+# Tenderly integration for transaction simulations
 export TENDERLY_ACCOUNT_NAME="test"
 export TENDERLY_PROJECT_NAME="governor-test"
 export TENDERLY_ACCESS_KEY="abcabcabcabcabcabc"
+
+# CORS configuration for API routes
 export ALLOWED_ORIGINS="http://localhost:3000"
+
+# Etherscan API for contract verification fallback
 export ETHERSCAN_API_KEY="abcabcabcabcabcabc"
+

11-11: Add blank line at end of file.

The file should end with a blank line for consistent formatting.

 export ETHERSCAN_API_KEY="abcabcabcabcabcabc"
+
web/src/hooks/useContractInfo.ts (1)

24-25: Consider finite staleTime for better data freshness.

While contract ABIs rarely change, using staleTime: Infinity could lead to stale data issues if a contract is upgraded or re-verified. Consider using a longer but finite staleTime.

-    staleTime: Infinity,
+    staleTime: 60 * 60 * 1000, // 1 hour
     gcTime: 24 * 60 * 60 * 1000,
web/src/app/api/contract/route.ts (1)

96-96: Fix variable naming inconsistency.

The variable is named arbiscanData but contains data from Etherscan API.

-    const arbiscanData = await response.json();
+    const etherscanData = await response.json();

-    if (arbiscanData.status !== "1" || !arbiscanData.result) {
+    if (etherscanData.status !== "1" || !etherscanData.result) {
       return NextResponse.json({ error: "Contract not verified on Etherscan" }, { status: 404 });
     }

-    const abi = JSON.parse(arbiscanData.result);
+    const abi = JSON.parse(etherscanData.result);
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ff9ddb2 and d267938.

📒 Files selected for processing (9)
  • web/.env.example (1 hunks)
  • web/src/app/(main)/governor/[governorAddress]/MyLists/AddTxnModal.tsx (4 hunks)
  • web/src/app/(main)/governor/[governorAddress]/MyLists/JsonInput.tsx (2 hunks)
  • web/src/app/api/contract/route.ts (1 hunks)
  • web/src/app/api/simulate/route.ts (1 hunks)
  • web/src/context/NewListsContext.tsx (4 hunks)
  • web/src/hooks/useContractInfo.ts (1 hunks)
  • web/src/utils/simulateRouteUtils.ts (1 hunks)
  • web/src/utils/tenderly/encodeState.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • web/src/app/api/simulate/route.ts
  • web/src/utils/simulateRouteUtils.ts
  • web/src/utils/tenderly/encodeState.ts
  • web/src/context/NewListsContext.tsx
🧰 Additional context used
🪛 dotenv-linter (3.3.0)
web/.env.example

[warning] 4-4: [UnorderedKey] The NEXT_PUBLIC_COURT_SITE key should go before the NEXT_PUBLIC_REOWN_PROJECT_ID key


[warning] 7-7: [QuoteCharacter] The value has quote characters (', ")


[warning] 8-8: [QuoteCharacter] The value has quote characters (', ")


[warning] 9-9: [QuoteCharacter] The value has quote characters (', ")


[warning] 9-9: [UnorderedKey] The TENDERLY_ACCESS_KEY key should go before the TENDERLY_ACCOUNT_NAME key


[warning] 10-10: [QuoteCharacter] The value has quote characters (', ")


[warning] 10-10: [UnorderedKey] The ALLOWED_ORIGINS key should go before the TENDERLY_ACCESS_KEY key


[warning] 11-11: [EndingBlankLine] No blank line at the end of the file


[warning] 11-11: [QuoteCharacter] The value has quote characters (', ")


[warning] 11-11: [UnorderedKey] The ETHERSCAN_API_KEY key should go before the TENDERLY_ACCESS_KEY key

🔇 Additional comments (5)
web/src/app/(main)/governor/[governorAddress]/MyLists/JsonInput.tsx (1)

95-97: LGTM! Clean implementation of dynamic ABI prop handling.

The useEffect implementation correctly handles the optional ABI prop and integrates well with the existing handleAbiChange logic. The dependency array and undefined check are appropriate.

web/src/app/(main)/governor/[governorAddress]/MyLists/AddTxnModal.tsx (2)

36-45: LGTM! Well-structured dynamic UI feedback implementation.

The useMemo implementation provides clean, efficient UI state management based on contract info loading states. The logic correctly handles loading, success, and default states for user feedback.


133-135: Good integration of dynamic contract validation feedback.

The TextField enhancements provide real-time feedback to users about contract verification status, improving the user experience during transaction creation.

web/src/hooks/useContractInfo.ts (1)

30-42: LGTM! Robust error handling and API integration.

The fetch implementation includes proper error checking, response validation, and detailed error logging. The error propagation allows React Query to handle retry logic appropriately.

web/src/app/api/contract/route.ts (1)

30-74: LGTM! Well-structured API route with proper error handling.

The main GET handler correctly implements rate limiting, environment variable validation, and graceful fallback logic. The error handling and logging are comprehensive.

coderabbitai[bot]
coderabbitai bot previously approved these changes Jun 19, 2025
Base automatically changed from feat/functionality to master October 3, 2025 14:45
@tractorss tractorss dismissed coderabbitai[bot]’s stale review October 3, 2025 14:45

The base branch was changed.

@tractorss tractorss merged commit 3caee11 into master Nov 18, 2025
5 checks passed
@tractorss tractorss deleted the feat/tenderly-simulations branch November 18, 2025 09:19
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