Documentation Index Fetch the complete documentation index at: https://docs.cosmos.network/llms.txt
Use this file to discover all available pages before exploring further.
The Safe Singleton Factory is a minimal proxy factory that enables deterministic deployment of Safe (formerly Gnosis Safe) multisig wallets and related contracts. It uses CREATE2 to ensure the same wallet addresses across all EVM chains.
Contract Address : 0x914d7Fec6aaC8cd542e72Bca78B30650d45643d7
Deployment Status : Default preinstall
Repository : github.com/safe-global/safe-singleton-factory
Known Issue - Bytecode Verification Required The Safe Singleton Factory bytecode in the current DefaultPreinstalls (x/vm/types/preinstall.go:30-32 ) is currently identical to the Create2 factory bytecode , which is incorrect. Before deploying this contract in production:
Verify the correct Safe Singleton Factory bytecode from the official repository
Update your genesis configuration with the correct bytecode
Test the deployment on a testnet first
This issue is tracked and will be addressed in a future release. For now, you should manually verify and update the bytecode if you need Safe Factory functionality.
Key Features
Deterministic Deployment : Same Safe addresses across all chains
Minimal Implementation : Simple factory pattern using CREATE2
Version Agnostic : Deploy any version of Safe contracts
Gas Efficient : Optimized proxy deployment pattern
Ecosystem Standard : Used by Safe infrastructure globally
How It Works
The factory uses a two-step deployment process:
Deploy Singleton : Deploy the Safe master copy (implementation)
Deploy Proxy : Deploy minimal proxies pointing to the singleton
This pattern enables gas-efficient deployment of multiple Safe wallets sharing the same implementation.
Core Method
function deploy ( bytes memory data , bytes32 salt )
returns ( address deploymentAddress )
The factory has a single method that deploys contracts using CREATE2.
Usage Examples
Deploy a Safe Wallet
import { ethers } from "ethers" ;
import { SafeFactory } from "@safe-global/safe-core-sdk" ;
const FACTORY_ADDRESS = "0x914d7Fec6aaC8cd542e72Bca78B30650d45643d7" ;
// Using Safe SDK
async function deploySafeWallet ( owners , threshold , signer ) {
const safeFactory = await SafeFactory. create ({
ethAdapter: new EthersAdapter ({ ethers, signer }),
safeVersion: '1.4.1'
});
const safeAccountConfig = {
owners: owners,
threshold: threshold,
// Optional parameters
fallbackHandler: "0x..." , // Fallback handler address
paymentToken: ethers.ZeroAddress,
payment: 0 ,
paymentReceiver: ethers.ZeroAddress
};
// Predict address before deployment
const predictedAddress = await safeFactory. predictSafeAddress (safeAccountConfig);
console. log ( "Safe will be deployed to:" , predictedAddress);
// Deploy the Safe
const safeSdk = await safeFactory. deploySafe ({ safeAccountConfig });
const safeAddress = await safeSdk. getAddress ();
console. log ( "Safe deployed to:" , safeAddress);
return safeSdk;
}
// Manual deployment without SDK
async function deployManually ( signer ) {
const factory = new ethers. Contract (
FACTORY_ADDRESS ,
[ "function deploy(bytes,bytes32) returns (address)" ],
signer
);
// Prepare Safe proxy bytecode with initialization
const proxyBytecode = "0x..." ; // Safe proxy bytecode
const salt = ethers. id ( "my-safe-v1" );
// Deploy
const tx = await factory. deploy (proxyBytecode, salt);
const receipt = await tx. wait ();
// Get deployed address from events
const deployedAddress = receipt.logs[ 0 ].address;
return deployedAddress;
}
See all 54 lines
Predict Safe Address
Calculate the deployment address before deploying:
function predictSafeAddress ( owners , threshold , saltNonce ) {
const initializer = encodeSafeSetup (owners, threshold);
const salt = ethers. solidityPackedKeccak256 (
[ "bytes32" , "uint256" ],
[ethers. keccak256 (initializer), saltNonce]
);
const initCode = ethers. concat ([
PROXY_CREATION_CODE ,
ethers.AbiCoder. defaultAbiCoder (). encode ([ "address" ], [ SAFE_SINGLETON ])
]);
const deploymentAddress = ethers. getCreate2Address (
FACTORY_ADDRESS ,
salt,
ethers. keccak256 (initCode)
);
return deploymentAddress;
}
Deploy with Custom Configuration
async function deployCustomSafe ( config , signer ) {
const {
owners ,
threshold ,
modules = [],
guards = [],
fallbackHandler
} = config;
// Encode initialization data
const setupData = safeSingleton.interface. encodeFunctionData ( "setup" , [
owners,
threshold,
ethers.ZeroAddress, // to
"0x" , // data
fallbackHandler,
ethers.ZeroAddress, // paymentToken
0 , // payment
ethers.ZeroAddress // paymentReceiver
]);
// Deploy proxy pointing to singleton
const proxyFactory = new ethers. Contract (
PROXY_FACTORY_ADDRESS ,
proxyFactoryAbi,
signer
);
const tx = await proxyFactory. createProxyWithNonce (
SAFE_SINGLETON_ADDRESS ,
setupData,
Date. now () // saltNonce
);
const receipt = await tx. wait ();
const safeAddress = getSafeAddressFromReceipt (receipt);
// Enable modules if specified
for ( const module of modules) {
await enableModule (safeAddress, module , signer);
}
return safeAddress;
}
See all 44 lines
Deployment Patterns
Organization Wallets
Deploy consistent treasury addresses across chains:
async function deployOrgTreasury ( orgId , chains ) {
const salt = ethers. id ( `org-treasury-${ orgId }` );
const results = {};
for ( const chain of chains) {
const provider = new ethers. JsonRpcProvider (chain.rpc);
const signer = new ethers. Wallet (deployerKey, provider);
// Same salt = same address on all chains
const address = await deploySafeWithSalt (salt, signer);
results[chain.name] = address;
}
return results;
}
See all 15 lines
Counterfactual Wallets
Create wallets that can receive funds before deployment:
class CounterfactualSafe {
constructor ( owners , threshold ) {
this .owners = owners;
this .threshold = threshold;
this .address = this . predictAddress ();
}
predictAddress () {
// Calculate address without deploying
return predictSafeAddress (
this .owners,
this .threshold,
0 // saltNonce
);
}
async deploy ( signer ) {
// Only deploy when needed
const code = await signer.provider. getCode ( this .address);
if (code !== "0x" ) {
console. log ( "Already deployed" );
return this .address;
}
return deploySafeWallet (
this .owners,
this .threshold,
signer
);
}
}
See all 31 lines
Integration with Safe Ecosystem
Safe Modules
Deploy and enable Safe modules:
// Deploy module via factory
async function deployModule ( moduleCode , salt ) {
const tx = await factory. deploy (moduleCode, salt);
const receipt = await tx. wait ();
return receipt.contractAddress;
}
// Enable module on Safe
async function enableModule ( safeAddress , moduleAddress , signer ) {
const safe = new ethers. Contract (safeAddress, safeAbi, signer);
const tx = await safe. enableModule (moduleAddress);
await tx. wait ();
}
See all 13 lines
Safe Guards
Deploy transaction guards for additional security:
async function deployAndSetGuard ( safeAddress , guardCode , signer ) {
// Deploy guard
const salt = ethers. id ( `guard-${ safeAddress }` );
const guardAddress = await factory. deploy (guardCode, salt);
// Set as Safe guard
const safe = new ethers. Contract (safeAddress, safeAbi, signer);
const tx = await safe. setGuard (guardAddress);
await tx. wait ();
return guardAddress;
}
See all 12 lines
Best Practices
Salt Management
// Structured salt generation
function generateSalt ( context , nonce ) {
return ethers. solidityPackedKeccak256 (
[ "string" , "uint256" ],
[context, nonce]
);
}
// Examples
const userSalt = generateSalt ( `user-${ userId }` , 0 );
const orgSalt = generateSalt ( `org-${ orgId }` , iteration);
const appSalt = generateSalt ( `app-${ appId }-${ version }` , 0 );
See all 12 lines
Version Control
const SAFE_VERSIONS = {
"1.3.0" : "0xd9Db270c1B5E3Bd161E8c8503c55cEABeE709552" ,
"1.4.0" : "0x41675C099F32341bf84BFc5382aF534df5C7461a" ,
"1.4.1" : "0x29fcB43b46531BcA003ddC8FCB67FFE91900C762"
};
async function deploySafeVersion ( version , owners , threshold ) {
const singleton = SAFE_VERSIONS [version];
if ( ! singleton) throw new Error ( `Unknown version: ${ version }` );
return deployWithSingleton (singleton, owners, threshold);
}
See all 12 lines
Gas Optimization
Deploy singleton once per chain
Reuse proxy bytecode
Batch deployments when possible
Use minimal initializers
Security Considerations
Initialization : Ensure Safes are properly initialized after deployment
Salt Uniqueness : Use unique salts to prevent address collisions
Singleton Verification : Verify singleton contract before deployment
Access Control : The factory itself has no access control - anyone can deploy
Troubleshooting
Issue Solution Address mismatch Verify salt and bytecode are identical Deployment fails Check sufficient gas and valid bytecode Safe not working Ensure proper initialization after deployment Cross-chain inconsistency Verify same singleton and salt used
Safe Infrastructure
const SAFE_CONTRACTS = {
factory: "0x914d7Fec6aaC8cd542e72Bca78B30650d45643d7" ,
singleton_1_4_1: "0x29fcB43b46531BcA003ddC8FCB67FFE91900C762" ,
proxyFactory: "0x4e1DCf7AD4e460CfD30791CCC4F9c8a4f820ec67" ,
multiSend: "0x38869bf66a61cF6bDB996A6aE40D5853Fd43B526" ,
fallbackHandler: "0xfd0732Dc9E303f09fCEf3a7388Ad10A83459Ec99"
};
Further Reading