Skip to navigation

Deploy a Safe on Your Rollup

Safe (formerly Gnosis Safe) is the standard multisig wallet used to control admin roles, treasuries, and contract ownership on Ethereum-style chains. Instead of one key controlling funds or permissions, a Safe requires a threshold of owners (e.g. “3 of 5”) to approve any action.

This guide applies to OP Stack rollups only. The Safe factory is one of the OP Stack’s preinstalled contracts, deployed automatically at the same address on every chain. Arbitrum Orbit chains do not get this preinstall, so contact Support if you need a Safe deployed on an Orbit chain.

Preinstalled contracts

Every OP Stack rollup launched on Conduit ships with these contracts already deployed, at the same addresses as Ethereum mainnet and other OP Stack chains:

ContractAddressPurpose
SafeSingletonFactory0x914d7Fec6aaC8cd542e72Bca78B30650d45643d7Deterministic factory used to deploy the Safe singleton and proxy contracts
Safe0x69f4D1788e39c87893C980c06EdF4b7f686e2938Safe singleton logic contract (no events, cheaper gas)
SafeL20xfb1bffC9d739B8D520DaF37dF666da4C687191EASafe singleton logic contract that emits events (recommended for L2s so activity is easier to index/monitor)
MultiSend / MultiSendCallOnly0x998739BFdAAdde7C933B942a68053933098f9EDa / 0xA1dabEF33b3B82c7814B6D82A79e50F4AC44102BBatch multiple calls into a single Safe transaction

For the full list and addresses, see the Optimism preinstalls reference.

The Safe protocol-kit handles building the deployment transaction for you: you just supply your RPC URL, owners, and threshold.

1

Install the SDK

npm install @safe-global/protocol-kit
2

Configure your Safe and predict its address

import Safe, { PredictedSafeProps, SafeAccountConfig } from '@safe-global/protocol-kit'
const safeAccountConfig: SafeAccountConfig = {
owners: ['0xOwner1...', '0xOwner2...', '0xOwner3...'],
threshold: 2,
}
const predictedSafe: PredictedSafeProps = { safeAccountConfig }
const protocolKit = await Safe.init({
provider: '[your_rpc_url]',
signer: '[deployer_private_key]',
predictedSafe,
})
const safeAddress = await protocolKit.getAddress()
3

Deploy the Safe

const deploymentTransaction = await protocolKit.createSafeDeploymentTransaction()
const client = await protocolKit.getSafeProvider().getExternalSigner()
const txHash = await client.sendTransaction({
to: deploymentTransaction.to,
value: BigInt(deploymentTransaction.value),
data: deploymentTransaction.data,
})

Once deployed, you can manage the Safe (propose/approve/execute transactions) using the Safe api-kit against your own transaction service, or manually via protocol-kit, since the hosted Safe Wallet app does not support arbitrary custom chains out of the box.

Option 2: Call the factory directly

If you’d rather not pull in the SDK, you can call the factory’s createProxyWithNonce function yourself:

function createProxyWithNonce(
address _singleton,
bytes memory initializer,
uint256 saltNonce
) external returns (address proxy);
  • _singleton: the SafeL2 address above (or Safe, if you don’t need events).
  • initializer: the encoded call to Safe.setup(owners, threshold, to, data, fallbackHandler, paymentToken, payment, paymentReceiver) (pass your owners array and threshold, and zero/empty values for the rest unless you need those features).
  • saltNonce: any value you choose; it (plus your initializer) determines the resulting Safe’s address, so you can predict it ahead of time.

This is lower-level and mainly useful if you’re scripting deployments with Foundry/cast or integrating Safe creation into your own tooling.