Skip to content

feat: multi-tx simulation and batch-specific approval UI#2238

Closed
janek26 wants to merge 1 commit into03-06-refactor_transaction_requests_arrayfrom
03-06-feat_multi_tx_simulation_and_batch_ui
Closed

feat: multi-tx simulation and batch-specific approval UI#2238
janek26 wants to merge 1 commit into03-06-refactor_transaction_requests_arrayfrom
03-06-feat_multi_tx_simulation_and_batch_ui

Conversation

@janek26
Copy link
Contributor

@janek26 janek26 commented Mar 16, 2026

Multi-tx simulation and batch-specific approval UI

The approval screen now simulates all calls in a batch together, merges the results, and displays per-call details. Single-transaction approvals are unaffected.

What changed

  • useSimulateTransactions – Replaces useSimulateTransaction. Accepts an array of transactions, simulates them via the backend's batch API, and merges results with mergeSimulations (worst scanning result wins, in/out/approvals concatenated, metas collected per-call).
  • CallDetails component – Extracted from TransactionDetails. Renders contract info, function name, source code status, and creation date for a single call. TransactionDetails now iterates metas[] and renders one CallDetails per call with a "Call N" label for batches.
  • BatchTransactionData – Data tab for batches: shows each call's target address and calldata separately with a combined copy button.
  • Details tab expansion – Chain badge and dapp badge are now shown in the details tab for all requests (not just overview), giving reviewers full context without switching tabs.
  • Simulation-driven scam detectioneffectiveDappStatus is derived from the simulation scanning result. If simulation flags the request as malicious, the approval screen shows the scam warning regardless of the dapp's metadata status.
  • Layout fixes in Simulation.tsxwrap={false} and flexShrink/minWidth fixes prevent asset amounts from wrapping awkwardly on narrow viewports.
  • Scrollable Alert description – Long error messages (common with batch failures) no longer overflow the alert dialog.

Design choices & review focus

  • metametas[] – The simulation response has a single meta per transaction. For batches we collect them into an array so the UI can render per-call details. The TransactionSimulation type is updated throughout.
  • Worst-case scanningmergeSimulations picks the most severe scanning result across all calls. If any call is flagged malicious, the whole batch is treated as malicious. This is conservative by design.
  • SimulationQueryResult type – A new exported type that bundles data, status, error, and isRefetching. This replaces passing four separate props through the component tree.

PR-Codex overview

This PR focuses on enhancing the Alert component's styling and improving the simulation transaction handling across multiple components, including better organization of transaction data and improved error handling.

Detailed summary

  • Updated Alert component to restrict max height and manage overflow.
  • Enhanced Simulation handling to merge multiple results and added error management.
  • Refactored SendTransaction to use multiple transactions and improved simulation state handling.
  • Modified SendTransactionInfo to include simulation results.
  • Improved rendering of batch transaction details.

✨ Ask PR-Codex anything about this PR by commenting with /codex {your question}

@janek26
Copy link
Contributor Author

janek26 commented Mar 16, 2026

Warning

This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
Learn more


How to use the Graphite Merge Queue

Add the label merge-queue to this PR to add it to the merge queue.

You must have a Graphite account in order to use the merge queue. Sign up using this link.

An organization admin has enabled the Graphite Merge Queue in this repository.

Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue.

This stack of pull requests is managed by Graphite. Learn more about stacking.

@janek26 janek26 marked this pull request as ready for review March 16, 2026 12:38
@janek26 janek26 requested a review from DanielSinclair March 16, 2026 12:39
@janek26 janek26 force-pushed the 03-06-refactor_transaction_requests_array branch from 8c2e0cb to a3fd029 Compare March 16, 2026 12:42
@janek26 janek26 force-pushed the 03-06-feat_multi_tx_simulation_and_batch_ui branch from f0bd569 to 8e02de6 Compare March 16, 2026 12:42
@janek26 janek26 force-pushed the 03-06-refactor_transaction_requests_array branch from a3fd029 to 6a6b56c Compare March 17, 2026 10:42
@janek26 janek26 force-pushed the 03-06-feat_multi_tx_simulation_and_batch_ui branch 2 times, most recently from 0d7df2b to f0b92c6 Compare March 17, 2026 13:21
@janek26 janek26 force-pushed the 03-06-refactor_transaction_requests_array branch from 6a6b56c to 965fcd9 Compare March 17, 2026 13:21
@janek26 janek26 force-pushed the 03-06-feat_multi_tx_simulation_and_batch_ui branch from f0b92c6 to 732e6f9 Compare March 18, 2026 09:02
@janek26 janek26 force-pushed the 03-06-refactor_transaction_requests_array branch from 965fcd9 to 876ac3f Compare March 18, 2026 09:02
@janek26 janek26 force-pushed the 03-06-feat_multi_tx_simulation_and_batch_ui branch from 732e6f9 to bc92150 Compare March 18, 2026 12:42
@janek26 janek26 force-pushed the 03-06-refactor_transaction_requests_array branch 2 times, most recently from 8b637e8 to eebd2fe Compare March 18, 2026 13:00
@janek26 janek26 force-pushed the 03-06-feat_multi_tx_simulation_and_batch_ui branch from bc92150 to 59e9783 Compare March 18, 2026 13:00
@janek26 janek26 force-pushed the 03-06-feat_multi_tx_simulation_and_batch_ui branch from 59e9783 to 2d4b17c Compare March 19, 2026 12:38
@janek26 janek26 force-pushed the 03-06-refactor_transaction_requests_array branch 2 times, most recently from 36350be to ded1927 Compare March 19, 2026 12:59
@janek26 janek26 force-pushed the 03-06-feat_multi_tx_simulation_and_batch_ui branch from 2d4b17c to 74d097a Compare March 19, 2026 12:59
@janek26 janek26 force-pushed the 03-06-refactor_transaction_requests_array branch from ded1927 to d642d91 Compare March 19, 2026 13:30
@janek26 janek26 force-pushed the 03-06-feat_multi_tx_simulation_and_batch_ui branch 2 times, most recently from 7a22023 to e52798a Compare March 19, 2026 13:57
@janek26 janek26 force-pushed the 03-06-refactor_transaction_requests_array branch from 367d8f9 to f4c61ee Compare March 19, 2026 14:27
@janek26 janek26 force-pushed the 03-06-refactor_transaction_requests_array branch from 11e308f to 94da91a Compare March 19, 2026 15:00
@janek26 janek26 force-pushed the 03-06-feat_multi_tx_simulation_and_batch_ui branch from 40ad160 to c50cdc9 Compare March 19, 2026 15:01
@janek26 janek26 force-pushed the 03-06-refactor_transaction_requests_array branch from 94da91a to be04e5e Compare March 19, 2026 15:44
@janek26 janek26 force-pushed the 03-06-feat_multi_tx_simulation_and_batch_ui branch from c50cdc9 to 6de788e Compare March 19, 2026 15:44
@janek26 janek26 force-pushed the 03-06-refactor_transaction_requests_array branch from be04e5e to 1e8abe4 Compare March 19, 2026 15:48
@janek26 janek26 force-pushed the 03-06-feat_multi_tx_simulation_and_batch_ui branch 2 times, most recently from c24b094 to fd93362 Compare March 19, 2026 15:54
@janek26 janek26 force-pushed the 03-06-refactor_transaction_requests_array branch 2 times, most recently from 4fd1d89 to a8c4375 Compare March 20, 2026 07:12
@janek26 janek26 force-pushed the 03-06-feat_multi_tx_simulation_and_batch_ui branch from fd93362 to ba3adba Compare March 20, 2026 07:12
@DanielSinclair DanielSinclair force-pushed the 03-06-refactor_transaction_requests_array branch from a8c4375 to d95fc80 Compare March 20, 2026 07:15
@DanielSinclair DanielSinclair force-pushed the 03-06-feat_multi_tx_simulation_and_batch_ui branch 2 times, most recently from 7d0004d to e6c04f9 Compare March 20, 2026 07:26
@DanielSinclair DanielSinclair force-pushed the 03-06-refactor_transaction_requests_array branch from d95fc80 to b7dd5d6 Compare March 20, 2026 07:26
@DanielSinclair DanielSinclair force-pushed the 03-06-feat_multi_tx_simulation_and_batch_ui branch from e6c04f9 to e945d1e Compare March 20, 2026 07:31
@DanielSinclair DanielSinclair force-pushed the 03-06-refactor_transaction_requests_array branch 2 times, most recently from 81f7e84 to 44353f6 Compare March 20, 2026 07:43
@DanielSinclair DanielSinclair force-pushed the 03-06-feat_multi_tx_simulation_and_batch_ui branch from e945d1e to 806e379 Compare March 20, 2026 07:43
Copy link
Collaborator

@DanielSinclair DanielSinclair left a comment

Choose a reason for hiding this comment

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

Mentioned a few issues that I think we'll need to fix or further investigate before merging. I assume you'd be able to do that today. We'd want QA to run through those edge case scenarios as well.

maxHeight: 200,
overflow: 'auto',
wordBreak: 'break-word',
}}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Drifting away from the design system primitives here. When mocking this or fixing a layout issue, would point Codex at the design-system/ library and ask it to critique and itterate based on the design system constraints

queryFn: async () => {
const normalized = transactions.map((t) => ({
...t,
to: t.to || '',
Copy link
Collaborator

Choose a reason for hiding this comment

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

Spooked by fallback here too

to: tx.to?.toString() ?? '',
value: tx.value?.toString() ?? '0',
data: tx.data?.toString() ?? '0x',
})) ?? [],
Copy link
Collaborator

Choose a reason for hiding this comment

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

Same issue from the prior PR

});

if (!results[0]) throw 'UNSUPPORTED';
if (results.length === 0) throw 'UNSUPPORTED';
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do we filter out these potential null values elsewhere?

onRejectRequest={onRejectRequest}
loading={loading}
dappStatus={dappMetadata?.status}
dappStatus={effectiveDappStatus}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do we have a loading state here, or do we always flash scam state?

});
const effectiveDappStatus =
simulationResult.data?.scanning.result !== 'OK'
? DAppStatus.Scam
Copy link
Collaborator

Choose a reason for hiding this comment

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

We're losing the separate Warning state handling

Copy link
Collaborator

Choose a reason for hiding this comment

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

This also maps WARNING scans to DAppStatus.Scam, even though the metadata API distinguishes warning- level results from malicious ones. For transactions that are risky but not malicious, the approval UI still switches to the red scam affordance and “send anyway” behavior, which overstates the scan result and changes the normal approval flow.


const [expanded, setExpanded] = useState(false);

const isScamDapp = dappMetadata?.status === DAppStatus.Scam;
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think these states are going out of sync.

The parent component (index.tsx) computes effectiveDappStatus which incorporates simulation scanning results, and passes it to SendTransactionActions. But SendTransactionInfo never receives
effectiveDappStatus — it computes isScamDapp independently from raw dappMetadata?.status only.

This means if simulation flags a transaction as malicious but the backend dapp metadata says the dapp
is OK:

  • The approve button correctly shows "Send Transaction Anyway" (via effectiveDappStatus)
  • But the header still says "Transaction request" instead of "Dangerous request" (via isScamDapp)
  • The DappHostName component also uses the raw status, not the effective one

The scam UI is split across two components with two different data sources.

transactionSpeedClicked:
event.dappPromptSendTransactionSpeedClicked,
}}
chainId={getChainIdForRequest(request, activeSession?.chainId)}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Are we handling edge cases where session chainId differs from the batch request? Assume that might get caught at the provider, but code flow review caught this (havent investigated myself):

When a dapp session is connected on one chain and submits a batch for another, the sheet can approve a batch that has no gas on the target network or block one that actually does, because fee estimation and balance validation are now reading different chains.

@janek26 janek26 force-pushed the 03-06-feat_multi_tx_simulation_and_batch_ui branch from 806e379 to 7a0d8c3 Compare March 20, 2026 10:42
@janek26 janek26 force-pushed the 03-06-refactor_transaction_requests_array branch from 44353f6 to 06aeae1 Compare March 20, 2026 10:42
@DanielSinclair DanielSinclair force-pushed the 03-06-refactor_transaction_requests_array branch from 06aeae1 to 60fa6f8 Compare March 20, 2026 20:18
@DanielSinclair DanielSinclair force-pushed the 03-06-feat_multi_tx_simulation_and_batch_ui branch from 7a0d8c3 to 2f7a66d Compare March 20, 2026 20:18
@DanielSinclair DanielSinclair marked this pull request as draft March 20, 2026 20:28
@DanielSinclair DanielSinclair force-pushed the 03-06-refactor_transaction_requests_array branch from 60fa6f8 to 5bb953f Compare March 20, 2026 22:36
@DanielSinclair DanielSinclair force-pushed the 03-06-feat_multi_tx_simulation_and_batch_ui branch from 2f7a66d to ae2abd0 Compare March 20, 2026 22:37
DanielSinclair pushed a commit that referenced this pull request Mar 20, 2026
…ound execution (#2254)

This PR aims to replace these 3 from the stack  
#2236  
#2237  
#2238  
  
All of them had one common issue pointed out by Daniel in the review, I tried to generalise the sendTransaction interface into working with transaction request arrays, and transform normal sendTransaction requests into a transaction request array of length one.

This resulted in multiple issues:

1. transaction requests through sendTransaction and sendCalls with calls length of one would simulate the same
2. If not delegated yet, the delegation would not be simulated, and results would not be verified
3. Serial gas estimation does not represent actual gas costs 
    1. `est(call1)+est(call2)+est(call3) !== est(call1+call2+call3)`

This PR aims to use what's in place instead of forcing everything into a new array format. Instead we're reusing the existing format of having a single transaction request. That"s possible by wrapping calls with `prepareBatchedTransaction` from the delegation sdk.

<!-- start pr-codex -->

---

## PR-Codex overview
This PR introduces support for the `wallet_sendCalls` method, enhancing batch transaction capabilities. It includes new schemas, hooks, and UI components for better handling of batch requests and transactions, improving error handling and user feedback.

### Detailed summary
- Added support for `wallet_sendCalls` in `ApproveAppRequest.tsx`.
- Introduced `executeSendCallsBatch` handler and contract.
- Created schemas for batch call parameters and execution.
- Updated gas estimation logic to accommodate batch transactions.
- Enhanced UI components to display batch transaction details and simulation results.
- Improved error handling and user notifications for batch requests.

> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`

<!-- end pr-codex -->
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