Package: safe-cli
Safe-CLICommand line utility forSafecontracts. Use it to manage yourSafeeasily from the command line.Safe-CLI does not rely onSafe{Core} API, so it can be used in networks where Safe services are not available. If they are available,
Safe-CLI can also interact with them intx-servicemode.Using with dockerIf you haveDockerinstalled on your system, you can just run:dockerrun-itsafeglobal/safe-clisafe-creatorfor creating Safesordockerrun-itsafeglobal/safe-clisafe-clito run the actualsafe-cliInstallingPython >= 3.7is required.Python 3.10is recommended.pip3install-Usafe-cliUsagesafe-cli[-h][--history][--get-safes-from-owner]addressnode_url

positionalarguments:addressTheaddressoftheSafe,oranowneraddressif--get-safes-from-ownerisspecified.node_urlEthereumnodeurl

options:-h,--helpShowthishelpmessageandexit--historyEnablehistory.Bydefaultit'sdisabledduetosecurityreasons--get-safes-from-ownerIndicatesthataddressisanowner(SafeTransactionServiceisrequiredforthisfeature)Start Safe-CLITo load a Safe, use the following command:safe-cli<checksummed_safe_address><ethereum_node_url>Then you should be on the prompt and see information about the Safe, like the owners, version, etc.
Next step would be loading some owners for the Safe. At leastthresholdowners need to be loaded to do operations
on the Safe and at least one of them should have funds for sending transactions.Load ownersFrom private keyLoading owners is not needed if you just want to doread-onlyoperations.To load owners:> load_cli_owners <account_private_key>
Loaded account 0xab...cd with balance=123 ether
Set account 0xab..cd as default sender of txsYou can also load owners fromenvironment variables. Before running thesafe-cli:exportMY_PRIVATE_KEY=YOUR_EOA_PRIVATE_KEYRun the Safe-CLI, then:> load_cli_owners MY_PRIVATE_KEY
Loaded account 0xab...cd with balance=123 ether
Set account 0xab..cd as default sender of txsTo check the loaded owners:> show_cli_ownersTo unload an owner:> unload_cli_owners <ethereum_checksummed_address>From hardware walletsNOTE: before signing anything ensure that the data showing on your hardware wallet device is the same as the safe-cli data.If you want to use bothledgerandtrezoryou need to runpipinstallsafe-cli[ledger,trezor]LedgerLedger module is an optional feature of safe-cli to sign transactions with the help ofledgerethlibrary based onledgerblue.To enable, safe-cli must be installed as follows:pip install safe-cli[ledger]When running on Linux, make sure the following rules have been added to/etc/udev/rules.d/:SUBSYSTEMS=="usb", ATTRS{idVendor}=="2c97", ATTRS{idProduct}=="0000", MODE="0660", TAG+="uaccess", TAG+="udev-acl" OWNER="<UNIX username>"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="2c97", ATTRS{idProduct}=="0001", MODE="0660", TAG+="uaccess", TAG+="udev-acl" OWNER="<UNIX username>"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="2c97", ATTRS{idProduct}=="0004", MODE="0660", TAG+="uaccess", TAG+="udev-acl" OWNER="<UNIX username>"Ledger commands:load_ledger_cli_owners [--legacy-accounts] [--derivation-path <str>]: show a list of the first 5 accounts (--legacy-accounts search using legacy derivation) or load an account from provided derivation path.TrezorTrezor module is an optional feature of safe-cli to sign transactions from Trezor hardware wallet using thetrezorlibrary.To enable, safe-cli must be installed as follows:pip install safe-cli[trezor]Trezor commands:load_trezor_cli_owners [--legacy-accounts] [--derivation-path <str>]: show a list of the first 5 accounts (--legacy-accounts search using legacy derivation) or load an account from provided derivation path.Creating a new SafeUsesafe-creator <node_url> <private_key> --owners <checksummed_address_1> <checksummed_address_2> --threshold <uint> --salt-nonce <uint256>.Example:safe-creator https://sepolia.infura.io/v3/$INFURA_TOKEN $PRIVATE_KEY --owners $OWNER_ADDRESS_1 [$OWNER_ADDRESS_2] --threshold 1OperatingModesThere are 2 operation modes:blockchain: The default mode. Useblockchaincommand to enable it. Transactions are sent to blockchain.tx-service: Usetx-servicecommand to enable it. Transactions are sent to the Safe Transaction Service (if available on the network), so you will be able to see them on the Safe web interface/mobile apps. At least one signer is needed to send transactions to the service. Txs arenot executed. It requiresSafe{Core} APIrunning on the network.Common operationsNote: Sender private key must be loaded first. When loading an owner it will be set automaticallysend_custom <address> <value-wei> <data-hex-str> [--delegate] [--safe-nonce <int>]:
Sends a custom transaction from the Safe to a contract. If--delegateis set adelegatecallwill be triggered.send_ether <address> <value-wei> [--safe-nonce <int>]:
Sends ether from the Safe to another accountsend_erc20 <address> <token_address> <value> [--safe-nonce <int>]:
Send ERC20 token from the Safe to another accountapprove_hash <keccak-hexstr-hash> <sender-address>: Approves asafe-tx-hashfor the provided sender address.add_owner <address>: Adds a new owneraddressto the Safe.remove_owner <address>: Removes an owneraddressfrom the Safe.change_threshold <integer>: Changes thethresholdof the Safe.enable_module <address>: Enable moduleaddressdisable_module <address>: Disable moduleaddresschange_fallback_handler <address>: Updates the fallback handler to beaddress. Supported by Safes withversion >= v1.1.0.WARNING: DON'T USE
THIS IF YOU DON'T KNOW WHAT YOU ARE DOING. ALL YOUR FUNDS COULD BE LOSTchange_guard <address>: Updates the guard to beaddress. Supported by Safes withversion >= v1.3.0.WARNING: DON'T USE
THIS IF YOU DON'T KNOW WHAT YOU ARE DOING. ALL YOUR FUNDS COULD BE LOSTchange_master_copy <address>: Updates the master copy to beaddress. It's used to update the Safe.WARNING: DON'T USE
THIS IF YOU DON'T KNOW WHAT YOU ARE DOING. ALL YOUR FUNDS COULD BE LOSTupdate: Updates the Safe to the latest version (if you are on a known network likeGoerliorMainnet).update_to_l2 <address>: Updates a v1.1.1/v1.3.0/v1.4.1 non L2 Safe to a L2 Safe supported by Safe Wallet UI.
The migration contract address needs to be provided.
It can be foundhere.
Nonce for the Safe must be 0 and supported versions are v1.1.1, v1.3.0 and v1.4.1.WARNING: DON'T USE THIS IF YOU DON'T KNOW WHAT YOU ARE DOING. ALL YOUR FUNDS COULD BE LOSTOperations only in tx-service modebalances: Returns a list of balances for ERC20 tokens and ether.history: History of multisig transactions (including pending).execute-tx <safe-tx-hash>: Execute a pending tx with enough signatures.sign-tx <safe-tx-hash>: Sign a tx with the loaded owners for the providedSafeTxHash.sign_message [--eip191_message <str>] [--eip712_path <file-path>]: sign the provided string message provided by standard input or theEIP712provided by file.batch-txs <safe-nonce> <safe-tx-hash> [ <safe-tx-hash> ... ]: Batch transactions into one Multisig
Transaction using the providedsafe-nonce.Any safe-tx can be used: transactions from other Safes, transactions
already executed, transactions pending for execution... Only limitation is thattransactions from other networks cannot be used. Batching order will follow the same order of thesafe-tx-hashesprovided.get_delegates: Returns a list ofdelegatesfor the Safe. Adelegatecan be used when youtrust an address to post transactions to the tx-service on your behalf. If a transaction is not trusted (posted to the service not signed by a delegate or an owner of the Safe) it will be stored in the service but not shown in the UI or mobile applications.add_delegate <address> <label> <owner-address>: Adds a new delegateaddressfor theownerof the Safe.remove_delegate <address> <owner-address>: Removes a delegateaddressfrom the Safe.drain <address>: Sends all Ether and ERC20 funds to the provided account.WARNING: DON'T USE THIS IF YOU DON'T KNOW WHAT YOU ARE DOING. ALL YOUR FUNDS COULD BE LOSTIf the information in the information bar is outdated or there's any problem you can force thesafe-clito update the information about the Safe using:>refreshDemosCreating a SafeFor this demo,PRIVATE_KEYenvironment variable was set to anEOAprivate key.Sending Ether using a Trezor hardware walletCreate a remove owner transaction in the Transaction ServiceUse custom contractsSafe-clicomes with the official deterministic Safe contract addresses deployed on multiple chains configured by default. If you want to use your own you can edit the filesafe_cli/safe_addresses.pyBe careful when modifying these addresses, the funds in a Safe can get stuck if an invalid address it's used when updating to an invalid Safe Master Copy.Recovery Safe Deployment GuideThis guide will walk you through the process of recreating a Safe with the same address on the desired network, in case you sent funds to your Safe address in an incorrect chain.Note: It's not always posible to recover a Safe,check this linkRecreate Safe 1.3.0 or 1.1.1To recreate a Safe (version 1.3.0 or 1.1.1), you'll need the following essential data::TheSingletonaddressTheProxyFactoryaddressTheFallbackHandleraddressTheOwnersaddresses with which Safe was createdTheSaltNoncevalueTheThresholdvalueRPC node provider for the target chain.The private-key of deployer addressThe necessary addresses can be collected fromsafe-deploymentsand the salt nonce from theSafe creation transactionin a block explorer.WARNING: Ensure that theSingleton,ProxyFactory, andFallbackHandlerare deployed in the target chain in the same addresses as the origin chain.To recreate the Safe is necessary executesafe-creatoras follows:safe-creator --owners <owners-addresses> --safe-contract <singleton-address>
--callback-handler <fallback-handler-address> --proxy-factory <proxy-factory-address>
--threshold <threshold-value> --salt-nonce <salt-nonce-value> <url-rpc-node>  <deployer-private-key>The Safe should have been successfully recreated with the same address on the target chain. If not, double-check the data collected from the transaction and ensure that all the necessary contracts are deployed in the chain.Migrate a Safe from Non L2 to L2If you've recreated a Safe from a L1 network (like mainnet) on a L2 network, our services will not be able to index them as for L1 we use trace based indexing and for L2 events indexing, and L1 Safe singleton does not emmit events.
To address this, you'll need to update it to the L2 singleton with commandupdate_to_l2or consider transferring the funds to a new Safe on L2 that you control withdraincommand.
For detailed instructions on running these commands, please refer to theCommon operationssection for more information.Safe{Core} API/ProtocolSafe InfrastructureSafe Transaction ServiceSafe Smart AccountSafe Smart Account deployment info and addresesSetting up for developingIf you miss something and want to send us a PR:gitclonehttps://github.com/safe-global/safe-cli.gitcdsafe-cli
statvenv2>/dev/null||python3-mvenvvenvsourcevenv/bin/activate&&pipinstall-rrequirements-dev.txt
pre-commitinstall-fContributorsPedro Arias RuizUxío FuentefríaMoisés Fernández