Crypto Training

Safe Smart Account Evolution: v1.0.0 to v1.5.0, Feature Growth vs Security Surface

A deep, code-first analysis of Safe version evolution from v1.0.0 to v1.5.0: what changed, why it changed, what risks were introduced, and how audits and fixes shifted the security posture over time.

Crypto Training2025-09-2323 min read

Safe is one of the most battle-tested smart account systems in production, but battle-tested does not mean static. It means repeated exposure, repeated stress, repeated fixes, and repeated redesign when assumptions change.

This post is a code-first analysis of Safe from v1.0.0 to v1.5.0, built from release tags, changelog entries, audit artifacts, and Safe docs. The goal is not a marketing timeline. The goal is to understand security posture drift: what each feature bought, what each feature cost, and which old classes of bugs were actually eliminated versus reshaped.

Primary references:

Evolution map: capabilities vs attack surface#

flowchart LR V100["v1.0.0"] --> V111["v1.1.1"] V111 --> V120["v1.2.0"] V120 --> V130["v1.3.0"] V130 --> V140["v1.4.0"] V140 --> V141["v1.4.1"] V141 --> V150["v1.5.0"] V100 --- A100["Formal verification cycle and core multisig invariants"] V111 --- A111["Fallback and deployment hardening under Istanbul constraints"] V120 --- A120["EIP-150 gas overhead correction and relayer griefing fix"] V130 --- A130["Chain-aware hashes, guards, fallback split, L2 event model"] V140 --- A140["Factory and signature validation hardening"] V141 --- A141["ERC-4337 setup compatibility and migration helpers"] V150 --- A150["Extensible fallback handler and module guard era"]

The key pattern across all releases:

  • security fixes reduced specific known bug classes
  • extensibility added new composition risks
  • migration and operational tooling improved UX while adding control-plane complexity

That is normal for a modular smart account architecture.

What stayed constant across all versions#

Across all versions analyzed, the foundational trust skeleton remained the same:

  • proxy delegates to singleton implementation
  • owners and threshold control transaction authorization
  • nonce-based replay protection anchors ordering
  • modules and fallback handlers provide extension points
  • execution path is still fundamentally verify signatures -> enforce policy -> execute external call

Core code anchors by version:

flowchart TB O["Owners"] --> X["Safe.execTransaction"] SIG["Signature checks"] --> X G["Transaction guard"] --> X MG["Module guard"] --> X F["Fallback handler"] --> X N["Nonce"] --> X T["Target contract"] --> X X --> OUT["ExecutionSuccess or ExecutionFailure"]

Because this shape stayed stable, most security evolution happened in the edges: signature encoding details, gas semantics, setup/deployment constraints, callback handling, migration helpers, and policy hooks.

Version by version: what changed, why, and what new risk arrived#

v1.0.0: strong baseline, but critical classes discovered during verification#

Code:

Audit artifact:

Key takeaways from the formal verification cycle:

  • critical issue classes were identified in execution and transaction safety logic
  • high-value classes included reentrancy around transaction execution and gas/refund abuse paths
  • the release quality improved by fixing those classes before finalizing that release line

Interpretation:

  • v1.0.0 is not “perfect from day one”; it is “formal methods found hard bugs, then the protocol hardened”
  • this is exactly how formal verification should improve production software

v1.1.0 and v1.1.1: deployment and lifecycle hardening#

Code:

Audit artifacts:

What was emphasized:

  • delegatecall-induced state integrity risk was explicitly documented as a design hazard
  • master copy must be non-operable by construction, to reduce upgrade/deployment trust accidents
  • Istanbul gas repricing made legacy assumptions around send/transfer behavior fragile
  • MultiSend hardening reduced accidental misuse and self-destruct style lifecycle risk

New risk introduced by architecture evolution:

  • fallback routing gives flexibility, but any fallback route becomes a permanent selector-level trust boundary
sequenceDiagram autonumber participant Caller participant Proxy participant Safe participant FallbackManager participant Handler Caller->>Proxy: unknown selector Proxy->>Safe: delegate to singleton Safe->>FallbackManager: fallback dispatch FallbackManager->>Handler: forward calldata plus context Handler-->>FallbackManager: return FallbackManager-->>Caller: return

If handler assumptions are weak, the path above becomes a policy bypass vector.

v1.2.0: EIP-150 gas overhead fix under relayer threat model#

Code:

Audit artifact:

Issue class fixed:

  • incorrect gas overhead calculation under EIP-150 could allow a relayer to force internal failure while consuming nonce progression

Practical impact:

  • this is not a cosmetic gas bug
  • nonce consumption plus relayer execution is an authorization and liveness problem

Relevant behavior notes:

flowchart TD S["Signed Safe tx"] --> R["Relayer submits"] R --> GCHK["Gas overhead check"] GCHK -->|ok| EXEC["Internal execution"] GCHK -->|underprovided| FAIL["Controlled failure path"] EXEC --> N["Nonce progression"] FAIL --> N

The high-value lesson: gas math errors in account abstraction flows are often security bugs, not just UX bugs.

v1.3.0: major security architecture pivot#

Code:

Audit artifacts:

Feature gains:

  • chain-aware transaction hashing (chainId in EIP-712 domain)
  • transaction guards (checkTransaction, checkAfterExecution)
  • fallback context improvements (msg.sender forwarding semantics)
  • EIP-1271 compatibility logic shifted to handler layer
  • L2 event-rich execution model (GnosisSafeL2)
  • better simulation support via StorageAccessible

Why it was done:

  • split core account logic from compatibility and observability concerns
  • make policy hooks explicit and auditable
  • support richer integrations without forcing core bloat

New risks introduced:

  • guards can lock accounts if guard logic is buggy or misconfigured
  • fallback handlers became even more central to message/signature workflows
  • simulation and compatibility paths introduced more “advanced user footguns”
sequenceDiagram autonumber participant User participant Safe participant Guard participant Target User->>Safe: execTransaction Safe->>Safe: verify signatures and nonce Safe->>Guard: checkTransaction Guard-->>Safe: allow or revert Safe->>Target: CALL or DELEGATECALL Target-->>Safe: success or failure Safe->>Guard: checkAfterExecution Safe-->>User: emit execution event

v1.3.0 is where Safe became a clearer policy platform, not just a multisig executor.

v1.4.0: factory hardening, signature integrity checks, and naming cleanup#

Code:

Audit artifact:

Important changes:

  • contract namespace changed from Gnosis* to Safe*
  • factory removed risky/legacy APIs like createProxy
  • setup path added stricter singleton existence checks
  • createChainSpecificProxyWithNonce added to avoid cross-chain address replay where unwanted
  • contract-signature validation tightened by enforcing dataHash consistency
  • guard assignment now checks ERC-165 support
  • event indexing improved for operational monitoring

Why it mattered:

  • this release focused on reducing operational mistakes and ambiguous deployment behavior

New risks and caveats:

  • callback-based deployment flows still had race assumptions
  • guard and module design still had acknowledged liveness and front-running trade-offs
flowchart LR U["User wants deterministic deployment"] --> F["SafeProxyFactory"] F --> C1["createProxyWithNonce"] F --> C2["createChainSpecificProxyWithNonce"] C1 --> A1["Cross-chain same address possible"] C2 --> A2["Chain-bound address only"]

v1.4.1 and 1.4.1-x: ERC-4337 compatibility fix plus migration libraries#

Code:

What changed:

  • setupModules removed gasleft() dependency and used full forwarding semantics for compatibility in ERC-4337 contexts
  • migration helper contracts introduced to move between singleton variants and L2 compatible setups

Why it mattered:

  • smart account infra had to coexist with ERC-4337 style constraints and cross-network replay/deployment workflows

New risks:

  • migration helpers are power tools; assumptions around nonce/version/singleton must stay explicit
  • indexers and backends can misinterpret migration event context if they assume more than what code guarantees
stateDiagram-v2 [*] --> LegacySafe LegacySafe --> MigrationCall: delegatecall migration helper MigrationCall --> Validate: check version and nonce assumptions Validate --> UpdateSingleton UpdateSingleton --> OptionalFallbackUpdate OptionalFallbackUpdate --> EmitEvents EmitEvents --> Migrated Migrated --> [*]

v1.5.0: extensible fallback, module guarding, cleanup of unsafe deployment assumptions#

Code:

Audit artifacts:

Major feature and policy changes:

  • extensible fallback handler subsystem introduced
  • module guard interface introduced and wired
  • overloaded checkNSignatures added with executor context support
  • internal revert reason propagation improved
  • legacy send and transfer usage removed in favor of call
  • createProxyWithCallback deprecated and removed
  • EraVM specific support path deprecated as EVM compatibility strategy evolved
  • SafeToL2Migration deprecated in favor of updated setup/migration posture

Why it mattered:

  • Safe expanded from a guarded smart account into a richer programmable policy substrate

What new risk came with that power:

  • higher policy graph complexity means more misconfiguration states
  • fallback method maps and domain verifiers create additional trust configuration vectors
  • module guard logic can protect execution or accidentally deadlock it
flowchart TB C["Caller"] --> S["Safe core"] S --> TG["Transaction guard"] S --> MG["Module guard"] S --> FH["Extensible fallback handler"] FH --> SM["Safe methods registry"] FH --> DV["Domain verifiers"] S --> EXT["External target"]

The security trajectory is clear: stronger controls, but more moving parts to reason about.

Function-level evolution that changed the threat model#

Below is the practical diff map that matters most when auditing systems pinned to specific Safe versions.

Version jumpFunction or componentWhy it changedSecurity consequence
v1.0.0 -> v1.1.1fallback and master-copy lifecycle controlsIstanbul and deployment trust concernsreduced unsafe lifecycle assumptions, but introduced persistent fallback trust boundaries
v1.1.1 -> v1.2.0gas overhead math in execution checksEIP-150 correctness under relayersclosed nonce-burning relayer griefing vector
v1.2.0 -> v1.3.0checkSignatures and checkNSignatures visibility and usemake signature logic reusable for modules/handlersimproved composability, but increased misuse blast radius if callers pass wrong context
v1.2.0 -> v1.3.0guard hooks (checkTransaction, checkAfterExecution)policy extensibilitypolicy enforcement became explicit, but guard DoS became a real liveness class
v1.2.0 -> v1.3.0EIP-1271 moved to fallback compatibility layerdecouple evolving standard from coreimproved upgrade agility, but handler correctness became critical
v1.3.0 -> v1.4.0setGuard ERC-165 checksreject malformed guard contractsreduced accidental bricking by non-conforming guard targets
v1.3.0 -> v1.4.0setupModules stricter target checksprevent uninitialized or invalid setup flowsreduced setup-time misconfiguration risk
v1.4.0 -> v1.4.1setupModules no gasleft() dependencyERC-4337 compatibilitybetter bundler semantics, fewer gas-opcode edge assumptions
v1.4.x -> v1.5.0createProxyWithCallback removedcallback bypass/front-run classremoved an API with known race assumptions
v1.4.x -> v1.5.0module guard interface pathpolicy controls over module executionbetter defense-in-depth, but more liveness complexity
v1.4.x -> v1.5.0overloaded checkNSignatures with executorsafe use of pre-approved signatures from modulesless logic duplication and fewer context bugs
v1.4.x -> v1.5.0send and transfer removed in favor of callfuture gas schedule resilienceavoids stipend fragility, but reentrancy analysis burden increases

Representative code links for this diff map:

Why some features were removed, not fixed#

A recurring pattern in mature security engineering is feature retirement. Safe did this in multiple places:

  • removing createProxyWithCallback rather than iterating forever on nonce/callback race subtleties
  • deprecating paths that made migration or cross-network assumptions brittle
  • removing dependence on gas stipend behavior (transfer and send) when protocol-level gas schedules can change

This is the right trade-off when:

  • the feature is low usage
  • the exploit or misuse class is persistent
  • equivalent safer paths already exist
flowchart TD F0["Feature introduced"] --> R1["Usage in production"] R1 --> A1["Audit discovers structural risk"] A1 --> M1{"Can constraints fully eliminate risk?"} M1 -->|yes| F1["Keep feature with hardening"] M1 -->|no| F2["Deprecate or remove feature"] F1 --> R2["Re-audit and monitor"] F2 --> R2

Safe’s v1.5.0 release clearly uses this strategy.

Release rationale in one sentence per major change#

This subsection is intentionally direct so engineers can map design intent to threat model updates.

  • Chain-aware transaction hash domain was added so signatures are not accidentally replayable across chain domains.
  • Transaction guards were added so teams can enforce policy before and after account execution, rather than relying purely on signer behavior.
  • Compatibility fallback handler split was introduced so evolving standards logic can move faster than core execution logic.
  • L2 event-heavy variant was added because indexing and observability requirements differ by gas market and execution environment.
  • Factory API cleanup was done because deterministic deployment mistakes are a major source of operator error.
  • ERC-165 guard checks were added because malformed guard contracts can brick systems even without malicious intent.
  • Migration helpers were introduced because the ecosystem needed explicit operational paths between singleton and network compatibility variants.
  • Module guard support was added because module execution had become large enough to require dedicated policy controls.
  • Extensible fallback handler was added because teams needed richer programmable flows without forcing permanent core-contract growth.

Risk ledger by component, not by release#

A release-centric view is useful for chronology. A component-centric view is better for active audits.

ComponentTypical historical bug classBest current controlResidual risk
Signature decoding and checksmalformed encoding assumptions, context mismatchesexplicit signature type handling, executor-aware checkNSignaturesintegration-layer misuse of packed signatures and domain context
Nonce and gas semanticsrelayer griefing and nonce burn edge casescorrected EIP-150 overhead handling and documented safeTxGas semanticsoperational errors in off-chain gas estimation and relay strategy
Guard systemincorrect guard implementations causing liveness failureERC-165 interface checks and clearer guard hooksaccepted trade-off: broken guard can still DoS execution
Module execution pathprivilege overreach and module front-run assumptionsmodule guard support and stricter governance processhigh blast radius if module trust model is weak
Fallback dispatchselector confusion and handler trust boundary flawsexplicit fallback handler contracts and context helperspolicy complexity in extensible handler maps
Factory and setupuninitialized proxies, callback race assumptionsstricter setup checks and callback path deprecationdeployment script quality remains critical
Migration helpersversion/nonce/event assumption driftexplicit migration contracts and guarded preconditionsmigration scripts and backend interpretation drift

Gas semantics and safeTxGas are still often misunderstood#

Safe’s gas model changed for good reasons, and misunderstanding it still causes integration bugs.

Reference:

The core point:

  • with refunds (gasPrice > 0), safeTxGas behaves as a strict cap-like execution budget for internal execution economics
  • without refunds (gasPrice == 0), safeTxGas can behave more like a minimum-availability guard and failure semantics differ based on safeTxGas and gasPrice

This is one of the most practical examples of how account abstraction UX and protocol security intertwine.

flowchart LR TX["Safe tx"] --> GP{"gasPrice > 0 ?"} GP -->|yes| REF["Refund path active"] GP -->|no| NREF["No refund path"] REF --> B1["Relayer economics and nonce burn risk if misconfigured"] NREF --> B2["safeTxGas=0 allows revert bubbling and retry semantics"]

A note on docs and implementation drift#

One useful lesson from multiple audits is that documentation mismatches can produce security misunderstandings even when bytecode is correct.

Examples from the v1.5.0 cycle include:

  • signature description mismatches fixed in docs
  • event indexing and interpretation clarifications
  • behavior notes added where a direct code change was not the right move

This is not paperwork. For high-value account systems, docs are part of the security surface, because most failures happen at integration boundaries.

Version delta matrix for operational teams#

If your team operates Safes across networks and vintages, this matrix is practical for migration planning.

AreaPre-1.3.0 bias1.3.x and 1.4.x bias1.5.0 bias
Signature strategycore-heavy logiccore plus compatibility splitricher compatibility and extensibility with clearer context APIs
Deployment strategyeasier accidental divergencestronger deterministic patternsdeterministic plus deprecated callback race paths
Policy controlsowners and modulesowners/modules plus tx guardowners/modules plus tx guard plus module guard
Fallback behaviorbasic callbacks and compatibilityricher context and compatibilityextensible routing and domain verifier model
Migration posturead hoc and painfulexplicit migration helpersmigration helpers plus deprecations for old paths

Practical exploit-path thought model for modern Safe stacks#

When you audit protocols integrating Safe as an admin or treasury account, this sequence catches real bugs:

sequenceDiagram autonumber participant Attacker participant Integration participant Safe participant Module participant Guard Attacker->>Integration: trigger privileged flow assumptions Integration->>Safe: forward action via module or direct tx Safe->>Guard: policy checks Safe->>Module: optional module path Module-->>Safe: execution request Safe-->>Integration: success or failure Note over Integration,Safe: Most incidents happen where integration assumptions differ from Safe semantics

If you threat-model only Safe core and not the integration code around it, you miss the bug class that currently causes the most real losses.

Chronological closure notes from audit cycles#

To avoid vague statements like “audited and secure,” this timeline maps concrete findings to concrete closure behavior:

  • v1.0.0 formal verification cycle surfaced critical classes including execution reentrancy and gas/refund abuse concerns. These were treated as release-blocking classes and fixed before finalization.
  • v1.1.0 and v1.1.1 reviews found no immediately exploitable direct vulnerability in reviewed deltas, but emphasized that delegatecall-capable account systems require strict provenance and deployment discipline.
  • v1.2.0 gas-validation review identified the EIP-150 overhead miscalculation that could let malicious relayers force internal failure and burn nonce progress. The report states the issue was fixed.
  • v1.3.0 April/May 2021 reviews reported no serious issues, with mostly optimization and ergonomic notes.
  • v1.4.0 report highlighted medium and warning classes such as broken guard DoS and operational caveats around module/front-run behavior. Some issues were fixed, some acknowledged as design trade-offs.
  • v1.5.0 Ackee report captured a medium-severity callback bypass front-running path and multiple lower-severity correctness issues; most were fixed and some were acknowledged/documented.
  • v1.5.0 Certora report contributed medium/low findings around ERC-777 behavior assumptions, dirty-bit handling, fallback semantics, and documentation consistency, with most fixes merged and a small set explicitly accepted.

This closure pattern is what mature security engineering looks like in high-value account software:

  1. detect real classes through multiple methods (manual review, formal properties, integration-driven audits)
  2. fix what is code-fixable
  3. document what is architectural trade-off
  4. remove APIs that are structurally fragile
flowchart LR D["Detection"] --> T["Triage"] T --> F["Code fix"] T --> C["Constraint or deprecation"] T --> Doc["Doc and operational guidance"] F --> V["Verification and regression checks"] C --> V Doc --> V

Teams integrating Safe should copy this mindset: do not just consume release notes. Build internal upgrade playbooks that map each release delta to your exact module, guard, and fallback configuration.

Audit-fix ledger by version#

This table consolidates what was added and what had to be fixed or acknowledged.

VersionFeature intentSecurity issue class observedTypical outcome
v1.0.0reliable multisig baselinereentrancy and gas/refund abuse classes surfaced in verificationcritical classes fixed during formal verification cycle
v1.1.xlifecycle and deployment hardeningdelegatecall state integrity caveats, master copy deployment trust, Istanbul gas behaviormitigations, stronger deployment guidance, constructor hardening
v1.2.0gas correctness under relayersEIP-150 overhead miscalc enabled nonce-burning griefingmedium severity fix applied
v1.3.0policy and compatibility architectureguard liveness and fallback complexity became first-class risk domainsno serious findings in reviewed deltas, but stronger integration obligations
v1.4.0deployment API cleanup and validation hardeningcallback race assumptions, guard DoS risk, module front-run realitiesmixed fixed and acknowledged trade-offs
v1.4.1ERC-4337 setup and migration supportmigration assumptions and helper misuse riskbugfix + migration utilities, then iterative hardening
v1.5.0extensibility and modernized interfacescallback bypass class, dirty bits and fallback/method-map correctness, handler semanticsmost findings fixed; some documented or explicitly accepted

Audit references:

Deep technical slices that matter in real audits#

1) Signature engine evolution and why checkNSignatures overload matters#

Relevant code:

The overloaded method that accepts executor context solves a real integration pain point: pre-approved hash flows from modules and guards.

Without executor context, modules either duplicate logic or make brittle assumptions around msg.sender-coupled approval semantics.

flowchart LR H["safeTxHash"] --> B["Signature bytes"] B --> T{"Signature type"} T --> EOA["ECDSA"] T --> ETHSIGN["eth_sign adjusted v"] T --> C1271["EIP-1271 contract signature"] T --> PRE["Pre-approved hash"] PRE --> EXE["Executor aware checkNSignatures overload"]

Risk that remains:

  • integrators can still misuse signature packing, ordering, or context assumptions and accidentally widen authority.

2) Guard and module guard are security controls and liveness risks#

Relevant code:

Guards are not “just checks.” They are externally programmable policy engines invoked in critical execution paths.

sequenceDiagram autonumber participant Module participant Safe participant ModuleGuard participant Target Module->>Safe: execTransactionFromModule Safe->>ModuleGuard: checkModuleTransaction ModuleGuard-->>Safe: allow or revert Safe->>Target: execute Target-->>Safe: status Safe->>ModuleGuard: checkAfterModuleExecution Safe-->>Module: success or failure

If check* logic reverts unexpectedly, governance and operations can halt.

3) Fallback handler evolution increased power and review burden#

Relevant code:

Fallback logic moved from basic token callback compatibility to a richer extensible method and domain verification system.

flowchart TD C["External caller"] --> FB["Safe fallback"] FB --> FM["FallbackManager"] FM --> EH["ExtensibleFallbackHandler"] EH --> M["Method registry"] EH --> D["Domain verifier"] EH --> R["Result or revert"]

Security implication:

  • policy mistakes in method registration or domain binding can produce subtle auth bugs
  • this is now a must-review surface in any Safe-based protocol audit

4) Factory and setup remain critical trust boundaries#

Relevant code:

The createProxyWithCallback deprecation in v1.5.0 is a textbook case of retiring a fragile API surface once a front-running class is understood.

sequenceDiagram autonumber participant Alice participant Bob participant Factory Alice->>Factory: createProxyWithCallback(singleton, init, salt, cb) Bob->>Factory: front-run with createProxyWithNonce using derived salt Factory-->>Bob: proxy deployed Factory-->>Alice: collision or changed assumptions Note over Alice,Factory: callback dependent post-deploy logic can be skipped

This was directly captured and then closed in the v1.5.0 audit/fix cycle.

5) Migration tooling improves ops while increasing assumption load#

Relevant code:

Migration helpers make upgrades and cross-network normalization feasible, but migrations are state surgery. They are never low-risk by default.

flowchart LR L["Legacy singleton"] --> M["Delegatecall migration library"] M --> C["Version and nonce constraints"] C --> U["Singleton pointer update"] U --> H["Fallback handler update"] H --> E["Migration events"]

You should treat migration code as privileged maintenance logic with same scrutiny as upgrade admin contracts.

6) Storage layout discipline and hashed slots reduced collision risk#

Relevant guidance:

Safe’s hashed slot guidance is a practical defense against storage collisions in evolving proxy layouts.

flowchart TB ID["Identifier string"] --> HASH["keccak256(identifier)"] HASH --> SLOT["Storage slot constant"] SLOT --> READ["sload in assembly"] SLOT --> WRITE["sstore in assembly"]

This is not a silver bullet, but it is a strong pattern when extensions and migrations keep expanding.

Battle-tested vs fixed all old problems#

The short answer:

  • Safe is battle-tested
  • Safe has not fixed all old and future problems forever

The longer answer:

What battle-tested means in Safe’s case#

  • many years of production use with high-value wallets
  • repeated external audits across major and patch releases
  • formal verification integration for critical properties
  • demonstrated willingness to deprecate or remove fragile APIs

What it does not mean#

  • every extension composition is automatically safe
  • every guard/module/fallback combo is liveness-safe
  • migration pathways are impossible to misuse
  • operational assumptions cannot drift
stateDiagram-v2 [*] --> StableCore StableCore --> NewFeature NewFeature --> NewEdgeCase NewEdgeCase --> AuditAndFormal AuditAndFormal --> Hardening Hardening --> StableCore Hardening --> NewFeature

This cycle is healthy engineering maturity. It is not a terminal state.

Practical checklist for auditing modern Safe integrations#

If you audit a protocol that depends on Safe v1.4.x or v1.5.x, these checks produce real findings:

  1. Guard liveness and recovery paths
  • Can a broken guard permanently freeze execution?
  • Is there a safe fallback recovery process tested in scripts, not only documented?
  1. Module authority boundaries
  • Are module permissions scoped, monitored, and revocable?
  • Could a module front-run disable/removal workflows?
  1. Fallback handler method/domain correctness
  • Which selectors are routed where?
  • Are domain verifiers aligned with actual message domain intent?
  1. Signature context usage
  • Are integrations using the executor-aware signature checks where needed?
  • Are pre-approved hash semantics constrained to expected callers?
  1. Deployment flow assumptions
  • Does system logic rely on callback behavior from deprecated factory paths?
  • Is singleton existence and initialization success validated in deployment scripts?
  1. Migration assumptions
  • Are version and nonce preconditions enforced on-chain and re-validated off-chain?
  • Are events used as hints or as authority?
  1. Token and callback edge behavior
  • Are token callback handlers intentionally enabled?
  • Can token callback semantics lock assets or create implicit trust edges?
  1. Event-based indexer dependence
  • Are downstream systems robust to ordering and semantic nuance?
  • Do alerts watch state, not just event text?

Final verdict on Safe evolution#

Safe’s evolution from v1.0.0 to v1.5.0 is a strong case study in real security engineering:

  • core transaction integrity got much stronger
  • deployment and compatibility hazards were steadily reduced
  • extensibility increased practical utility and ecosystem reach
  • each extensibility step introduced fresh policy and composition risk

The right way to think about modern Safe is:

  • core is mature
  • extensions are powerful
  • integration quality is the deciding factor

In other words, Safe is no longer just a multisig contract. It is an account platform. Treat every enabled extension as part of your protocol’s trusted computing base.

Source map by version and docs#

Code tags:

Docs and design notes:

Audit PDFs: