Skip to content

feat: simulation when broadcast not specified#30

Merged
aviggiano merged 10 commits into
Recon-Fuzz:mainfrom
Telcoin-Association:feat/simulation-support
May 15, 2026
Merged

feat: simulation when broadcast not specified#30
aviggiano merged 10 commits into
Recon-Fuzz:mainfrom
Telcoin-Association:feat/simulation-support

Conversation

@chasebrownn
Copy link
Copy Markdown
Contributor

@chasebrownn chasebrownn commented May 12, 2026

Simulation support + SafeScriptBase

Motivation

At Telcoin Association we do a lot of on-chain executions via governance scripts and find the safe-utils very helpful. One thing we noticed is the lack of support around the traditional workflow of simulation when --broadcast is not specified. This PR adds simulation support so you can catch reverts, verify deployment addresses, and confirm state changes without touching a hardware wallet or proposing to the multisig.

What's added

Safe.sol — simulation functions

  • isSimulationMode() / isBroadcastMode() — detects whether the script is running with --broadcast. Falls back to the SAFE_BROADCAST env var for environments where vm.isContext is unavailable (e.g. test suites).
  • simulateTransactionNoSign(target, data, signer) — simulates a single Call transaction. Writes directly to the Safe's approvedHashes storage slot via vm.store so no real signature is needed.
  • simulateTransactionsNoSign(targets, datas, signer) — batch variant via MultiSend.
  • simulateTransactionMultiSigNoSign(target, data, signers[]) — multi-sig variant; sorts signers ascending as required by Safe's checkNSignatures.
  • simulateTransactionsMultiSigNoSign(targets, datas, signers[]) — batch multi-sig variant.
  • All simulate functions return bool (never throw) so callers decide how to handle failures.
  • proposeTransactionWithSignature(..., nonce) and proposeTransactionsWithSignature(..., nonce) — explicit-nonce overloads for scripts that propose multiple transactions in one run (the Safe's on-chain nonce only advances on execution, so sequential proposes need a manually incremented nonce).

SafeScriptBase.sol — new file

Abstract Foundry script base that auto-routes to simulate or propose based on mode detection. Eliminates boilerplate for the common pattern of "dry-run first, broadcast second":

  • _initializeSafe() / _initializeSafeMultiSig() — reads addresses and derivation path from env.
  • _proposeTransaction() / _proposeTransactions() — simulate or propose, increment currentNonce.
  • _proposeTransactionWithVerification() — adds a post-simulation code-length check for deployment transactions.

test/SafeSimulation.t.sol — new test file

11 tests against a real Base mainnet fork using a Safe whose owners are the default Foundry accounts (no private keys or storage hacks needed):

  • Mode detection with env override
  • Single-signer simulate success and revert cases
  • Nonce advancement after simulation
  • Batch simulation success and revert
  • Multi-sig simulate with sufficient and insufficient signers
  • Explicit-nonce propose overloads

@chasebrownn chasebrownn changed the title Simulation when broadcast not specified feat: simulation when broadcast not specified May 12, 2026
@chasebrownn chasebrownn marked this pull request as ready for review May 13, 2026 15:24
Copy link
Copy Markdown
Collaborator

Hi there,

Thanks for your submission. I will review this until the end of the week and we'll get this merge soon

Copy link
Copy Markdown
Collaborator

@aviggiano aviggiano left a comment

Choose a reason for hiding this comment

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

A few comments

Comment thread src/Safe.sol Outdated
Comment thread src/Safe.sol Outdated
Comment thread src/Safe.sol Outdated
@aviggiano
Copy link
Copy Markdown
Collaborator

@codex review

@aviggiano
Copy link
Copy Markdown
Collaborator

@chasebrownn I've added a few comments and asked codex to review
also, CI is not passing

can you double check? otherwise, looks good

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: df69f210ce

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/Safe.sol Outdated
Comment thread src/Safe.sol Outdated
Comment thread src/Safe.sol
Comment thread src/SafeScriptBase.sol
Comment thread src/Safe.sol Outdated
chasebrownn and others added 4 commits May 14, 2026 09:02
Adds simulation support to safe-utils, allowing Safe transactions to be
tested against a local fork without hardware wallet signing or Safe API
interaction.

Safe.sol additions:
- isBroadcastMode() / isSimulationMode(): detect --broadcast flag via
  vm.isContext, with SAFE_BROADCAST env var override
- simulateTransaction(): executes a pre-built tx via vm.prank + execTransaction
- simulateTransactionNoSign() / simulateTransactionsNoSign(): no-HW-wallet
  simulation using vm.store to mark approvedHashes[sender][txHash] = 1
- simulateTransactionMultiSigNoSign() / simulateTransactionsMultiSigNoSign():
  multi-sig simulation; signers are sorted ascending as Safe requires
- proposeTransactionWithSignature(..., uint256 nonce): additive overload for
  sequential proposals in one script run (on-chain nonce not yet advanced)
- proposeTransactionsWithSignature(..., uint256 nonce): same for batch

SafeScriptBase.sol (new file):
- Abstract base for forge scripts; auto-detects mode and routes to simulate
  or propose. Tracks currentNonce across sequential transactions.
- _proposeTransaction / _proposeTransactions / _proposeTransactionWithVerification
- Single-sig and multi-sig init via SIGNER_ADDRESS / SIGNER_ADDRESS_0..N
Move SAFE_BROADCAST env-var check to execute before vm.isContext so it
can override detection in all contexts (not just catch paths).

Add test/SafeSimulation.t.sol with 11 tests covering: mode detection,
single-signer simulation, batch simulation, multi-sig simulation, and
explicit-nonce propose overloads.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
SimulationFailed was declared but never used — simulate functions return
bool by design so callers decide how to handle failures.

Add README docs for simulateTransactionNoSign, multi-sig variants,
SafeScriptBase, mode detection helpers, and the SAFE_BROADCAST env var.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@chasebrownn chasebrownn force-pushed the feat/simulation-support branch from df69f21 to 98fc29c Compare May 14, 2026 16:16
@chasebrownn
Copy link
Copy Markdown
Contributor Author

@aviggiano All great findings. Everything should be addressed now. Please let me know if you need anything else.

@chasebrownn chasebrownn requested a review from aviggiano May 14, 2026 17:41
@aviggiano aviggiano merged commit 273945a into Recon-Fuzz:main May 15, 2026
2 checks passed
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