Gnosis Safe is a brilliant product, and the unanimous choice for protocol team multisigs throughout Ethereum. But without care from owners, there are a few lurking threats when it comes to MEV protection:
Protocols like Yearn specifically are large targets for MEV given the massive volume of swaps that happen on a daily basis (some days upwards of $1,000,000). Up until now, it has been a struggle to implement the right human-level processes to guard against these threats and stay safe from giving up MEV opportunities.
This post presents "Stealth Safe Guard", a newly developed guard which aims to solve all 3 of these threats which can make interactions with your safe vulnerable.
Gnosis Safe transactions can be executed by anyone that has the required signatures, which are publicly available though the official gnosis API & UI. This opens up an attack vector where a malicious actor can add custom transactions before and after the multisig tx execution and grab it's MEV.
Gnosis Safe >=1.3.0 solves this by letting the safe assign a guard contract that can be set to only allow user-defined trusted addresses to execute the safe transactions. More details on why here.
Executing safe transactions through the public mempool opens up the same threat as stated above, anyone can see the tx and quickly generate a flashbots bundle that will grab the MEV from it.
StealthSafeGuard requires executors to go though the StealthRelayer contract, which protects against the txs getting into the public mempool by adding a bonded reward to anyone that sees the tx and reports it's secret hash, the tx will then not execute and take the penalty away from the executor. Thanks to the Tenderly team we have a set of automatic scripts looking for these type of txs that will immediately report them and pay the miner the full executor penalty.
Even if all of the above is done correctly there is still a way for our tx to end up in the wrong hands. The most common way for this to happen is when our stealth tx ends up getting included ONLY into an uncled block. This allows an attacker to grab our tx from the uncled block and include it into the following block.
There is an easy way to prevent this from happening which is requiring our tx to only succeed on a specific block number. StealthRelayer can be set to require the executor to send a target block number in the execution arguments. Doing this with flashbots also allows us to target the block from the bundle configuration and the smart contract to avoid reverted txs. Block protection can be disabled for networks not supported by Flashbots (such as FTM).
StealthSafeGuard is a Gnosis safe guard contract that allows multisig transactions to be safely signed on the UI without the risk of a 3rd party executing it, it also makes sure we execute using a private mempool + block protection to avoid getting sandwiched and|or uncle-bandit attacked by using bonded-stealth-txs.
This is achieved by requiring that the
msg.sender on StealthSafeGuard is our StealthRelayer and our
StealthRelayer.caller() to exist on
StealthSafeGuard can be easily deployed by with the following script: /guard/00-stealth-safe-guard-deploy.ts
Remember to correctly set your
StealthRelayer address on the utils/contracts.ts file, and to correctly input your safe address which will be assigned as the
Manager role, since the
msg.sender will get the
The account(s) you'll use to execute the safe transactions will need to perform a few transactions, including bonding some ETH into the StealthVault, to be able to properly execute through the StealthRelayer. Besides this, the Governor or Manager of the StealthSafeGuard would need to add these account(s) as executors. You can check on contract interactions how to perform this. The Executor's private key will also be used in the scripts below.
execute(GnosisSafeAddress, data, stealthHash, blockNumber)using a flashbots bundle
executeWithoutBlockProtection(GnosisSafeAddress, data, stealthHash)on non flashbots chains
setOverrideGuardChecks(true)to disable all guard checks in case of brick
Adding a guard into a safe is an extremely delicate action, since it can break/brick the safe completely. (you'll get locked out of your safe and lose all the assets it holds). We encourage new users to try this out on a brand new safe to get the hang of it.
StealthSafeGuard has a few protections in place which make sure you'll never get locked out of your safe.
StealthSafeGuard.setPendingManager(safe)as owner (second safe)
StealthSafeGuard.acceptManager()as main safe
StealthSafeGuard.setOverrideGuardChecks(true)as owner (second safe)
StealthSafeGuard.setOverrideGuardChecks(false)as owner (second safe)
Execution can be automated by running a script that every X minutes:
You can see an example on: scripts/guard/01-stealth-relayer-guard-get-signatures-and-execute.ts
The following command will prompt you to enter the Safe address and safeTxHash, then output the signed message.
npx hardhat run scripts/guard/02-stealth-relayer-guard-sign.ts
The signed message can be added into
offchainSignatures on: scripts/guard/01-stealth-relayer-guard-get-signatures-and-execute.ts
Then you can run the script to grab the first queued safe Tx, and it will grab signatures from the gnosis API and add the
offchainSignatures to the transaction, and submit it to flashbots (if on mainnet) though the stealth relayer.
npx hardhat run scripts/guard/01-stealth-relayer-guard-get-signatures-and-execute.ts --network rinkeby
StealthSafeGuard exists thanks to the efforts of the Flashbots, Gnosis and Yearn teams.
*git gud. *jk, you can contact any yearn moderators via our discord or telegram and ask to speak with our own stealth expert skeletor_spaceman or any of our other yMechanic team members on telegram, we would be happy to guide you.