This is a draft for a post that will be shared on a more visible channel (e.g. the Safe mirror) in the future. It would be nice to receive feedback and comments how to improve this article.
In 2017, Gnosis performed their token launch to start building a decentralized ecosystem for prediction markets. In this token launch, they collected their funds into the Gnosis MultiSig. This was a smart contract where transactions could be proposed on-chain, then a predefined number of the owners of the multisig had to confirm the transaction before it could be executed.
As part of their vision, Gnosis realized that it is not enough to provide a prediction market smart contract, but that more tools are required to drive the ecosystem forward. That is how the early Gnosis vision of “Create, Trade, Hold” came into existence. Create was referring to the prediction markets that would create tokens that represent the outcomes of the different predictions. Trade was required to bring value to these tokens and therefore Gnosis was putting quite some effort into driving different market mechanisms (this became the CoW protocol). And Hold represented the possibility to securely and easily manage all these tokens and trades in the ecosystem.
When thinking about how to bring secure and easy asset management to the ecosystem, Gnosis thought about building out the MultiSig contract that was used in their token launch. However it had a couple drawbacks that would need to be tackled before it could be used for a larger audience:
- High gas usage
- Too little flexibility
- No upgrade-ability
To improve these, the Safe contracts were developed. The goal was to provide a secure default with minimal gas costs and maximum flexibility.
As the secure default, the Safe contracts kept the multi signature logic where a threshold of owners have to confirm a transaction before it can be executed. To minimize the gas costs, the data of a transaction that should be executed, and the confirmations, are not stored on-chain. Additionally a proxy pattern was used that would reduce the setup costs and would allow to upgrade the logic of the contract to introduce features and fixes in the future. To ensure that even more advanced use cases can be covered, and additional flexibility is provided, the Safe supports “modules”, which are contracts which can use alternative access patterns (instead of multi signature) to execute transactions via the Safe. The contracts also added support for “delegate call”, which can be used to introduce complex execution logic by “loading” it from other contracts and execute them in the Safe context.
This initial version of the Safe also contained another feature which is essential to approach Account Abstraction: the confirmation/ verification logic is independent of
msg.sender . This allowed the Safe to support alternative signature schemes (EIP-1271 and EIP-712) and relaying from the very start.
Even though the first version was already very flexible and could be extended with alternative access logic and execution flows, it became clear that newer token standards had not been considered.
When EIP-1155 enforced callbacks to the receiving contract (with EIP-721 these were still optional), it was clear that there needed to be a way for a Safe can receive such tokens. As the chances were high that more standards would be introduced in the future that require contract callbacks it was important to keep maximum flexibility in mind. Therefore, a new feature was added to the Safe called “FallbackHandler”. With this feature, it was possible to register a contract that would be called when the Safe contract was not implementing the method that was invoked. This way it was possible to “add support” for new callbacks to an existing Safe without the need to upgrade it.
In addition to this, the team received feedback that the support for module interaction should be improved. The initial version of the contracts only provided minimal support by allowing to invoke transactions via the Safe, but it was not possible to get a response.
As these changes provided a big benefit to the users, this was the first version which the team decided to request the users to upgrade their Safes to.
With 1.1.1, more users started to create and use Safes and an effort was started to also bring these benefits to app developers by providing an SDK that allows the easy creation and usage of the Safe and all of its features (such as transaction batching). When developing this SDK, it became clear that the current version of the Safe contracts were missing some features to allow easy integration and full flexibility. Therefore, version 1.2.0 was created with some improvements when it comes to the integration of the Safe contract. As this was primarily a version that aimed towards developers and SDKs, it provided only minimal value to the existing users that used the Safe clients, therefore for this version, it was decided not to request the users to upgrade their Safes.
Over time the importance of side-chains and roll-ups increased, which was simultaneously increasing the risk of “cross-chain” attacks (i.e. replayability of transactions). Also, the UX and research in the ecosystem was rapidly moving forwards. Topics such as “miner extractable value”, “node client diversity” and “contract composability” became more relevant. This also triggered the need for better support of new and existing EIPs, such as EIP-1334 (chainid opcode) and EIP-165 (interface detection), in the Safe contracts.
To address the above topics, a new version of the Safe contracts was created. The most important change was to improve cross-chain security by using the chain ID for Safe transactions. This way it was not possible to replay transactions for Safes on the same address with the same configs on different chains.
In addition, the general security of the Safe was increased by introducing “Guards”. A guard can be registered to a Safe, to check transactions and state before and after the execution. This enabled additional checks which could for example be used to prevent MEV .
Besides this there were many changes that improved the UX when using the Safe (especially around gas estimation), further improving integrating with the Safe (especially make it easier to write modules) and making it easier to index Safes on low-cost networks (by emitting verbose events in the Safe contracts).
As this version brought major improvements to security and cross-chain usability, the team decided to request users to update their Safes.
With the popularity of EIP-4337, it became apparent again that for some integrations the current Safe contracts are still too “special”. In this case a restricted opcode is used during the integration. To prevent this from blocking any early integration with EIP-4337 or similar structured tools (that prevent specific opcodes), it makes sense to perform minimal changes to improve the “integration experience” (similar to version 1.2.0).
The users should not be requested to update their Safe, as this version would not bring any larger benefits to existing users (unless they want to use EIP-4337).
The vision for the Safe contracts is to become the core of all Smart Contract based wallets and make it easy to provide Account Abstraction to users and developers. For this, it is important to iterate on the original goal to provide a secure default with minimal gas costs and maximum flexibility. This is something that we are currently in the process of researching and defining how to push this forward.
The initial version of the Safe used the multi signature logic at the core to provide a secure default that could be used without trusting any additional contract (e.g. a module, guard or fallback handler). But it also provided some more specialized functionality to pay gas fees (in Ether and token), estimate gas or sign messages as a contract. In the meantime, new standards are evolving (i.e. EIP-1271 was finalized, EIP-4337 was kicked off) which have not been considered in the default. It is necessary to evaluate again what functionality and standards should be part of the next Safe version and what security impact it has to add support for these. In addition it is important to iterate on the current support for unused or outdated features and standards to limit the impact on security.
When version 1.0.0 of the Safe was deployed, L2 and side-chains were still not that common, therefore L1 was the way to go. But with the increasing Ether price and limited throughput, the gas prices started to become very high. This was the reason why the v1 contracts put a strong focus on optimizing all gas that can be saved. With the current state of the layered Ethereum ecosystem this is not as critical anymore. While gas usage should still be optimized, it is important to not “over-optimize”.
Modules provide an extreme flexibility when it comes to core contracts. In addition to this, delegate call can be used to execute transactions in a very complex manner. But how does this play into the secure default? Currently delegate call is most commonly used for transaction batching. There are more secure ways to provide this as part of the Safe core contract. Should the flexibility trade-off with security be more explicit?
As the answers to these questions might require bigger changes, it will take some time to evaluate, implement and verify all of this. Only once this has been sufficiently done, it can be evaluated how to best bring this to the users.
Join us on the discussion around the next versions of the Safe contracts in the forum or participate in our community calls: