Multichain Safe Deployment
This guide will teach you how to replicate a Safe address across different chains using the Protocol Kit. This process includes initializing the Protocol Kit, configuring the Safes to deploy, predicting its address on different chains, and executing the deployment transactions.
For more detailed information, see the Protocol Kit Reference.
Prerequisites
Install dependencies
First, you need to install the Protocol Kit.
_10pnpm add @safe-global/protocol-kit viem
Steps
Imports
Here are all the necessary imports for this guide.
_10import Safe, {_10 PredictedSafeProps,_10 SafeAccountConfig,_10 SafeDeploymentConfig_10} from '@safe-global/protocol-kit'_10import { waitForTransactionReceipt } from 'viem/actions'_10import { gnosisChiado, sepolia } from 'viem/chains'
Create a signer
You need a signer to instantiate the Protocol Kit. This example uses a private key to obtain a signer, but other EIP-1193 (opens in a new tab) compatible signers are also supported. For detailed information about signers, please refer to the Protocol Kit reference.
_10const SIGNER_PRIVATE_KEY = // ...
Configure the Safe deployment
Define the predictedSafe
object with the configuration for all the Safe accounts you will deploy. Check the reference to learn about all the different configuration options.
_10const safeAccountConfig: SafeAccountConfig = {_10 owners: ['0x...', '0x...', '0x...'],_10 threshold: 2_10 // More optional properties_10}_10_10const predictedSafe: PredictedSafeProps = {_10 safeAccountConfig_10 // More optional properties_10}
Initialize the Protocol Kit
Initialize an instance of the Protocol Kit for each network where you want to deploy a new Safe smart account by calling the init
method. Pass the provider
with its corresponding value depending on the network, the signer
executing the deployment, and the predictedSafe
with the Safe account configuration.
_11const protocolKitSepolia = await Safe.init({_11 provider: sepolia.rpcUrls.default.http[0],_11 signer: SIGNER_PRIVATE_KEY,_11 predictedSafe_11})_11_11const protocolKitChiado = await Safe.init({_11 provider: gnosisChiado.rpcUrls.default.http[0],_11 signer: PRIVATE_KEY,_11 predictedSafe_11})
Predict the Safe addresses
You can predict the Safe addresses by calling the getAddress
method from each Protocol Kit instance and ensure that the result addresses are the same.
_10const sepoliaPredictedSafeAddress = await protocolKitSepolia.getAddress()_10const chiadoPredictedSafeAddress = await protocolKitChiado.getAddress()
Deployment on Sepolia
Create the deployment transaction to deploy a new Safe account in Sepolia by calling the createSafeDeploymentTransaction
method.
_10const sepoliaDeploymentTransaction =_10 await protocolKitSepolia.createSafeDeploymentTransaction()
Call the sendTransaction
method from your Sepolia client instance and wait for the transaction to be executed.
_14const sepoliaClient =_14 await protocolKitSepolia.getSafeProvider().getExternalSigner()_14_14const transactionHashSepolia = await sepoliaClient!.sendTransaction({_14 to: sepoliaDeploymentTransaction.to,_14 value: BigInt(sepoliaDeploymentTransaction.value),_14 data: sepoliaDeploymentTransaction.data as `0x${string}`,_14 chain: sepolia_14})_14_14await waitForTransactionReceipt(_14 sepoliaClient!,_14 { hash: transactionHashSepolia }_14)
Once the deployment transaction is executed, connect the new Safe address to the Protocol Kit instance by calling the connect
method.
_10const newProtocolKitSepolia = await protocolKitSepolia.connect({_10 safeAddress: sepoliaPredictedSafeAddress_10})_10_10const isSepoliaSafeDeployed = await newProtocolKitSepolia.isSafeDeployed() // True_10const sepoliaDeployedSafeAddress = await newProtocolKitSepolia.getAddress()
If everything went well, isSepoliaSafeDeployed
should be true
, and the sepoliaDeployedSafeAddress
should equal the sepoliaPredictedSafeAddress
.
Deployment on Chiado
Repeat the same steps to deploy a Safe with the same configuration on Chiado testnet.
_24const chiadoDeploymentTransaction =_24 await protocolKitChiado.createSafeDeploymentTransaction()_24_24const chiadoClient =_24 await protocolKitChiado.getSafeProvider().getExternalSigner()_24_24const transactionHashChiado = await chiadoClient!.sendTransaction({_24 to: chiadoDeploymentTransaction.to,_24 value: BigInt(chiadoDeploymentTransaction.value),_24 data: chiadoDeploymentTransaction.data as `0x${string}`,_24 chain: gnosisChiado_24})_24_24await waitForTransactionReceipt(_24 chiadoClient!,_24 { hash: transactionHashChiado }_24)_24_24const newProtocolKitChiado = await protocolKitChiado.connect({_24 safeAddress: chiadoPredictedSafeAddress_24})_24_24const isChiadoSafeDeployed = await newProtocolKitChiado.isSafeDeployed() // True_24const chiadoDeployedSafeAddress = await newProtocolKitChiado.getAddress()
If everything went well, isChiadoSafeDeployed
should be true
, and the chiadoDeployedSafeAddress
should equal the chiadoPredictedSafeAddress
.
In addition, chiadoDeployedSafeAddress
and sepoliaDeployedSafeAddress
should have the same value.
Recap and further reading
After following this guide, you are able to deploy multiple Safe accounts with the same address on different chains using the Protocol Kit.