Skip to content

Releases

v0.13.0

Choose a tag to compare

@github-actions github-actions released this 17 Oct 17:52
v0.13.0
c02d03a

Smaller installs

The library and the CLI are still two crates, and each one now compiles only what it uses:

cargo install safe-rs-cli --locked                       # the `safe` CLI, with Ledger support
cargo install safe-rs-cli --locked --no-default-features # without Ledger (no libusb)
[dependencies]
safe-rs = "0.13"                                             # with fork simulation
safe-rs = { version = "0.13", default-features = false }      # without revm
safe-rs = { version = "0.13", features = ["hd-wallet"] }      # + keystores and RotatingSafe
Feature Default Adds
simulation yes Fork simulation with revm: simulate() on the builders, ForkSession, simulate_exec*
hd-wallet no Encrypted mnemonic keystores, HD keys and RotatingSafe (implies simulation)
ledger no Owner keys on a Ledger (implies hd-wallet)

Neither crate enables alloy's full or tokio's full any more, which dropped the WebSocket and IPC transports, pubsub and the anvil/txpool/debug provider APIs. A library build without default features compiles 242 crates instead of 331, without revm, c-kzg, blst or secp256k1-sys. Without simulation, the builders still execute(), with gas from eth_estimateGas, so a reverting call still fails before anything is broadcast.

Multi-owner Safes

  • SignedSafeTx holds several owners' signatures, sorted. New functions pack, unpack and recover them, and build approveHash calldata.
  • safe send, safe send --rotate and safe rotate read the Safe's owners, threshold and on-chain approvedHashes. They then pick one of:
    • collecting signatures offchain in a bundle file (--bundle, --sign-only, --merge-bundle)
    • approving the hash on chain (--approve-hash)
    • executing
  • New library functions: read_approved_owners, simulate_exec_with_threshold, simulate_approve_hash and OuterTxParams::approve_hash.

One-time owner keys

Every transaction can swap the Safe's owner for a fresh key, so the key that signed is no longer an owner once its signature is public.

  • CLI:
    • safe send --rotate --hd-wallet <name> appends the swap and then moves the old key's ETH to the new one.
    • safe rotate does the swap on its own, finds the Safe when you don't name one, and moves an existing Safe onto the wallet.
    • safe create --hd-wallet deploys from a funding key, so the owner key has never signed anything.
    • safe wallet new|import|list|address|status|sync|sweep manage the encrypted phrase and the record of used keys.
  • Library: the hd-wallet feature adds RotatingSafe, HdWallet, encrypted keystores, and rebuilding the rotation state from on-chain owner events.

Ledger

  • CLI: --ledger takes the owner keys from a Ledger, wherever --hd-wallet is accepted. It uses the same derivation path, so a Ledger seeded with a phrase shares that phrase's state. It's a default feature of the CLI.
  • Library: the ledger feature adds LedgerKeys. Safe transactions are now signed as EIP-712 typed data, which the device can display. For a local key the signatures are unchanged.

Safe v1.5.0

  • safe create deploys v1.5.0 Safes by default. Pass --safe-version 1.4.1 for the old version.
  • Owner rotation works on v1.5.0.
  • Batches go through the MultiSend contracts of the Safe's own version (ChainAddresses::for_version, detect_addresses).

Breaking changes

  • safe-rs no longer turns on alloy's full feature. If your code relied on it for WebSocket, IPC or the extra provider APIs, enable those on your own alloy dependency.
  • CallBuilder::simulate, simulation_result and simulation_success exist only with the simulation feature, which is on by default.
  • safe info takes the Safe address as a positional argument, defaults to the signer's own Safe, and no longer reads SAFE_ADDRESS.
  • SignedSafeTx holds a map of signatures instead of a single signature.

Fixes

  • execute() estimates gas itself, so a send that fails estimation no longer leaves the provider a nonce ahead with the next transaction stuck.
  • A batch whose MultiSend target has no code is refused before signing. That applies to SafeBuilder and to RotatingSafe. Before, it looked successful while none of the calls ran.
  • The fork backend runs on the caller's runtime, so simulate() followed by another call on the same provider can no longer hang.
  • WalletBuilder::connect_with_config keeps the configured contract addresses.
  • Every sweep still owed from an earlier key stays on record across later rotations.
  • Deployment checks no longer fail on a stale read from another RPC node.

Full changelog: v0.12.0...v0.13.0

v0.12.0

Choose a tag to compare

@tynes tynes released this 24 Sep 01:52
v0.12.0
06d73c7

The CLI is on crates.io

The safe CLI is now published as its own crate, safe-rs-cli:

cargo install safe-rs-cli --locked

It installs the same safe binary as before. The CLI used to live in an unpublished package, and the README's cargo install safe-rs failed because safe-rs is library-only.

Changes

  • The cli/ package is renamed from safe to safe-rs-cli, which already has crates.io metadata. The binary is still safe.
  • The CLI's version now matches the library's: both crates are at 0.12.0.
  • The README install instructions are fixed, for both the CLI and the library.

The safe-rs library API is the same as in v0.11.0.

Full changelog: v0.11.0...v0.12.0

v0.11.0

Choose a tag to compare

@tynes tynes released this 24 Sep 01:40
v0.11.0
48e3670

Breaking changes

  • SafeBuilder::execute() now runs exactly the Safe transaction that simulate() ran. Before, the simulation used safeTxGas = 0, but execute() signed a transaction with safeTxGas = gas_used * 1.1. That meant a failing inner call could be mined as ExecutionFailure and still consume the nonce. safeTxGas is now the value you set with with_safe_tx_gas() / with_gas_limit(), or 0. execute() fails with NonceMismatch if the Safe nonce changed after simulate(), and with InvalidConfig if calls were added after it. (77dd65b)
  • execute() requires the ExecutionSuccess event. call_only() rejects DelegateCall for single calls. with_nonce() rejects a stale nonce.
  • Upgraded to alloy 2.5, revm 41, foundry-fork-db 0.27 and revm-inspectors 0.41.
  • is_safe read errors are now IncompleteRead instead of Fetch.

Features

  • Frozen Safe transactions (envelope): PreparedSafeTx / SignedSafeTx fix the nonce and use a fail-closed policy (safeTxGas = 0). SafeBuilder::prepare() returns a frozen transaction, so the same transaction can be reviewed, simulated, signed and broadcast.
  • Outer transactions (outer): EIP-1559 outer transactions are signed locally, so the raw bytes and hash are available before broadcast.
  • Submission (submit): raw broadcast that reports ambiguous outcomes. Receipt waits that time out still return the hash. ExecutionSuccess / ExecutionFailure are decoded by Safe address and Safe transaction hash.
  • Safe state inspection (inspect): reads Safe state pinned to a block: owners, threshold, nonce, modules (paginated, with a completeness flag), guard, fallback handler and singleton.
  • Fork sessions (simulation::session): ForkSession is a stateful revm session pinned by block hash. It takes an explicit next-block environment, EVM spec and gas cap.
  • TxChecks has a balance flag and a MEASURE preset, which measure gas at the transaction gas cap without requiring the sender to afford the cap.
  • Halted frames (for example OutOfGas) now appear by name in revert reasons. SafeExecutionOutcome::label gives stable machine-readable names.
  • Helpers for the prepare → sign → simulate → broadcast flow: OuterTxParams::exec_transaction, SimTx::from_outer, ForkSession::fork_next_block, ParentHeader::block_id, WalletBuilder::deploy_detailed, create2::fetch_proxy_creation_code, and WalletConfig::owners() / validate(). safe_outcome_for_receipt, receipt_logs and is_gs013_revert are exported.

CLI

  • send freezes the Safe transaction and simulates the exact outer execTransaction it will sign against the next block with strict checks. It then signs locally, broadcasts, and waits for ExecutionSuccess. New flags: --nonce, --gas-limit, --max-fee-per-gas, --max-priority-fee-per-gas, --raw-out, --no-submit, --timeout, --trace, --block, --spec, --multi-send, --multi-send-call-only.
  • info reads Safe state pinned to a block (version, singleton, modules, guard, fallback handler) with --block. call runs in a fork session with --block, --spec and --trace. create accepts --singleton and --factory overrides.

Fixes

  • EOA batches are simulated statefully, so call N sees the effects of the calls before it. For example, approve followed by transferFrom now simulates correctly.
  • A reverted simulation in the CLI used to print Success: true. It now prints the inner revert reason and exits non-zero.
  • Bundle entries with operation: 1 are sent as DELEGATECALLs. They used to be sent as plain CALLs.
  • Call traces record logs, so traced simulations show emitted events.
  • send --skip-simulation explains a GS013 revert when gas estimation fails.

Full changelog: v0.10.1...v0.11.0

v0.9.0

Choose a tag to compare

@tynes tynes released this 02 Feb 13:04
a1d2904

Release v0.9.0

v0.6.1

Choose a tag to compare

@tynes tynes released this 27 Jan 05:37
bf43e8a

Patch release with corrected Cargo.lock

v0.5.0

Choose a tag to compare

@tynes tynes released this 25 Jan 23:11
7d6ba2f

What's Changed

  • Add comprehensive EOA multicall builder tests

Full Changelog: v0.4.0...v0.5.0

v0.3.0

Choose a tag to compare

@tynes tynes released this 24 Jan 05:44
d1bd32f

What's Changed

Breaking Changes

  • Simplified builder API by removing type-state pattern. MulticallBuilder no longer uses NotSimulated/Simulated type states. The API is now a simple fluent builder with optional simulation.

New Features

  • Added state_diff field to SimulationResult showing pre/post state for all touched accounts during simulation. Uses alloy's DiffMode type from geth pre_state tracing.

API Changes

  • MulticallBuilder::execute_without_simulation() removed - use execute() directly
  • Simulation result accessed via builder.simulation_result() instead of methods on the builder
  • Re-exported DiffMode and AccountState types for working with state diffs

Full Changelog

v0.2.0...v0.3.0