1

Is there documentation anywhere on how to create a gnosis safe programmatically with detailed information on the parameters?

This looks a lot like this question (How can I programmatically create new Gnosis Safe?) but since there wasn't really an exhaustive answer, I'm asking it again while trying to provide more contexte on the hoped answer.

From reading through the documentation, it seems that in order to deploy a new wallet, one either needs to deploy a new proxy, or use the ProxyFactory (https://github.com/safe-global/safe-smart-account/blob/0142ec8a4a05f03167daba9e7231b7e858aabd32/contracts/proxies/SafeProxyFactory.sol#L56) to generate a new proxy automatically, by providing it with the latest deployed singleton instance (https://contractscan.xyz/contract/0x41675c099f32341bf84bfc5382af534df5c7461a).

Am I understanding this correctly or am I missing something?

Additionally, in these examples (https://docs.safe.global/sdk/signers) there are a few wallet providers mentioned but it isn't clear to me how to define multiple providers as owners when creating the wallet.

To summarise, how could I create (deploy?) a gnosis safe wallet with multiple owners, using for instance one of the providers mentioned in the doc or even some EOA wallets that are created through private keys generation?

Edit:

Thanks Louis for the pointers. I'm editing my question to include what I ended up using in case someone later finds this thread and is looking for a complete answer. After reading through the SDK's protocol kit, the important missing piece for me was the following:

const safeDeployTransactionData = { to: safeProxyFactoryContract.getAddress(), value: '0', // we use the createProxyWithNonce method to create the Safe in a deterministic address, see: https://github.com/safe-global/safe-contracts/blob/main/contracts/proxies/SafeProxyFactory.sol#L52 data: safeProxyFactoryContract.encode('createProxyWithNonce', [ asHex(safeSingletonContract.getAddress()), asHex(initializer), // call to the setup method to set the threshold & owners of the new Safe BigInt(saltNonce) ]) } 

This highlights the usage of the Factory contract to deploy new safe wallets and the possibility to use whatever EOA wallets as signer for the new Safe wallet.

I'm still not sure however how to configure the safe such that I can have multiple wallets as owners. Do I need to create the safe and then submit multiple transactions to add new owners?

Is there doc or an example somewhere of the parameters that I can modify in order to configure the safe signing scheme?

2
  • can you provide a more detailed example? what it is the initializer in this case? Commented Nov 27, 2024 at 4:35
  • Actually, that’s the issue for me and the reason why there isn’t an accepted answer yet. I don’t know what to put in the initializer in order to setup multiple owners for me wallet on creation. So far I’ve only managed to get the wallet creation working by putting an empty initializer, which is far from ideal Commented Nov 28, 2024 at 8:58

2 Answers 2

1

The easiest way would be to use Safe{Core} SDK's protocol kit. You will have to first create a Safe object (also called 'counterfactual' or 'predicted' Safe), before deploying it.

Technically, you don't use providers as owners; providers lets you read data from the blockchain, and, if paired with a signer, write to it as a key holder. Signers return an address, and it is this address that you can add as a Safe owner. Note that you can use a given address with any signer that you like.

So in the protocol kit example I just linked, the signer would typically return an address (usually signer.address but this can differ from one signer to another), and this address is the one you would add as an owner in the predictSafe object; you would then use the signer to execute the transaction (with Safe.init(...) and protocolKit.createSafeDeploymentTransaction()).

Please let me know if anything is not perfectly clear. Best, Louis

3
  • Thank you for your answer. It will be a good starting point to investigate options. While the core protocol kit is limited to JS, knowing where and what to look for will allow me to reverse engineer the object creation and understand or even potentially create it using a different language. Unless there's some documentation around its construction that I could read through instead of diving in the code? Commented Oct 9, 2024 at 13:04
  • Sure, what language would you like to use? Let’s see if we can help Commented Oct 10, 2024 at 18:24
  • See updated question. Commented Oct 11, 2024 at 19:57
0
async function deploySafe( provider: ethers.JsonRpcProvider, wallet: ethers.Wallet, safeConfig: SafeConfig, chainConfig: ChainConfig): Promise<string> { const safeFactoryABI = [ "function createProxyWithNonce(address _singleton, bytes memory initializer, uint256 saltNonce) public returns (address proxy)", ]; const safeSingletonABI = [ "function setup(address[] calldata _owners, uint256 _threshold, address to, bytes calldata data, address fallbackHandler, address paymentToken, uint256 payment, address payable paymentReceiver) external", ]; const safeFactory = new ethers.Contract( chainConfig.safeFactoryAddress, safeFactoryABI, wallet, ); const safeSingleton = new ethers.Contract( chainConfig.safeSingletonAddress, safeSingletonABI, wallet, ); const ownerAddresses = safeConfig.owners.map((owner) => owner.address); var paymentReceiver = "0x5afe7A11E7000000000000000000000000000000"; var fallbackHandler = "0xfd0732Dc9E303f09fCEf3a7388Ad10A83459Ec99"; var data = "0x"; var addressTo = "0xBD89A1CE4DDe368FFAB0eC35506eEcE0b1fFdc54"; const initializer = safeSingleton.interface.encodeFunctionData("setup", [ ownerAddresses, safeConfig.threshold, addressTo, data, fallbackHandler, ethers.ZeroAddress, 0, paymentReceiver, ]); const saltNonce = Math.floor(Math.random() * 1000000); const tx = await safeFactory.createProxyWithNonce( chainConfig.safeSingletonAddress, initializer, saltNonce, ); const receipt = await tx.wait(); console.log( `Confirmed : ${safeConfig.name}, dirección: ${receipt.logs[0].address}`, ); return receipt.logs[0].address; 

}

3
  • Nice code sample, thanks for that. Any way you could add some documentation link that explains the initializer arguments? Specifically, I'm looking to find where the complete doc is so I can access other information when needed and in particular here I'm curious about the paymentReceiver, fallbackHandler and addressTo which seems a bit hard coded here and are probably specific to a given network. Commented Dec 3, 2024 at 20:11
  • You can find more info on these parameters directly in the source code: github.com/safe-global/safe-smart-account/blob/…. addressTo and data are optional and meant for enabling modules; payment and paymentReceiver are also optional. Lastly, fallbackHandler will depend on the network, and the functionality you want for your safe (e.g. 4337). You can see here for the official list of fallback handlers: docs.safe.global/advanced/smart-account-supported-networks Commented Dec 4, 2024 at 15:04
  • Please avoir code only answer and provide an explanation. Commented Dec 7, 2024 at 19:12

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.