Managing your Safe in Production
When co-managing a Safe with a larger team, ape-safe brings extra tools to the table to help you.
Queue Scripts
You can define “queue scripts” which are scripts under your project’s scripts/ folder that use
the propose_from_simulation decorator in your script.
Important
These scripts should follow a recommended naming convention by naming them either
scripts/nonce<N>.py or scripts/<ecosystem>_<network>_nonce<N>.py).
This will automatically registry with the queue management
subcommand that ships with ape-safe.
The value of the nonce N must correspond to a specific nonce above the current on-chain contract
value of nonce from the Safe, or it will not be collected by the queue management command.
An example:
# scripts/nonce<N>.py
from ape_safe.cli import propose_from_simulation
@propose_from_simulation()
def cli():
# This entire function is executed within a fork/isolated context
# Use normal Ape features
my_contract = Contract("<address>")
# Any transaction is performed as if `sender=safe.address` (using Ape's default sender support)
my_contract.mutableMethod(...)
# You can make assertions that will cause your simulation to fail if tripped
assert my_contract.viewMethod() == ...
# You can also add conditional calls
if my_contract.viewMethod() < some_number:
my_contract.mutableMethod()
Warning
The callback function MUST be named cli or it won’t work with Ape’s script runner.
Once the simulation is complete, the decorator will collect all receipts it finds from the safe’s
history during that session, and collect them into a single SafeTx to propose to a public network.
Note
If only one transaction is detected, it will “condense” that as a direct call,
instead of using MultiSend (which is used for >1 detected transactions).
The decorated function may have safe or submitter args in it,
in order to access their values within your simulation.
Note
If submitter is needed, safe must also be present in the args.
@propose_from_simulation()
# NOTE: Can also just use `def cli(safe):` if you don't need `submitter`
def cli(safe, submitter):
# You can use `safe` or `submitter` in your script to query values from the chain
bal_before = token.balanceOf(submitter)
amount = token.balanceOf(safe)
token.transfer(submitter, amount)
assert token.balanceOf(submitter) == bal_before + amount
You can run the script directly using the following command, and it will “dry-run” the SafeTx:
$ ape run nonce<N> your-safe --network ethereum:mainnet-fork --submitter TEST::0
Queue Management
We also provide a command ape safe pending ensure
that allows you to execute all matching queue scripts you have defined in scripts/.
This allows you to check side-effects from running all commands in the queue,
helping to reduce human error.
To execute a dry-run of all matching queue scripts for your Safe, execute the following:
$ ape safe pending ensure --safe your-safe --network ethereum:mainnet-fork --submitter TEST::0
Once you are ready, you can “push” your queue scripts to the Safe API via the following:
$ ape safe pending ensure --safe your-safe --network ethereum:mainnet --submitter local-wallet
Warning
The wallet you use for --submitter must either be one of the signers on the safe, or pre-approved
by the Safe API as a delegate for another signer, otherwise it will fail to propose the new script.
Note
It is recommended to use our ensure command in a secure context, such as Github Actions, where all
scripts can be reviewed by signers, and access is controlled behind Github’s access control system.