def deploy_create2_safe_tx(self, safe_address: str) -> SafeCreation2: """ Deploys safe if SafeCreation2 exists. :param safe_address: :return: tx_hash """ safe_creation2: SafeCreation2 = SafeCreation2.objects.get( safe=safe_address) if safe_creation2.tx_hash: logger.info('Safe=%s has already been deployed with tx-hash=%s', safe_address, safe_creation2.tx_hash) return safe_creation2 if safe_creation2.payment_token and safe_creation2.payment_token != NULL_ADDRESS: safe_balance = self.ethereum_client.erc20.get_balance( safe_address, safe_creation2.payment_token) else: safe_balance = self.ethereum_client.get_balance(safe_address) if safe_balance < safe_creation2.payment: message = 'Balance=%d for safe=%s with payment-token=%s. Not found ' \ 'required=%d' % (safe_balance, safe_address, safe_creation2.payment_token, safe_creation2.payment) logger.info(message) raise NotEnoughFundingForCreation(message) logger.info( 'Found %d balance for safe=%s with payment-token=%s. Required=%d', safe_balance, safe_address, safe_creation2.payment_token, safe_creation2.payment) setup_data = HexBytes(safe_creation2.setup_data.tobytes()) with EthereumNonceLock(self.redis, self.ethereum_client, self.funder_account.address, timeout=60 * 2) as tx_nonce: proxy_factory = ProxyFactory(safe_creation2.proxy_factory, self.ethereum_client) ethereum_tx_sent = proxy_factory.deploy_proxy_contract_with_nonce( self.funder_account, safe_creation2.master_copy, setup_data, safe_creation2.salt_nonce, gas=safe_creation2.gas_estimated, gas_price=safe_creation2.gas_price_estimated, nonce=tx_nonce) EthereumTx.objects.create_from_tx(ethereum_tx_sent.tx, ethereum_tx_sent.tx_hash) safe_creation2.tx_hash = ethereum_tx_sent.tx_hash safe_creation2.save() logger.info('Deployed safe=%s with tx-hash=%s', safe_address, ethereum_tx_sent.tx_hash.hex()) return safe_creation2
def deploy_again_create2_safe_tx(self, safe_address: str) -> SafeCreation2: """ Try to deploy Safe again with a higher gas price :param safe_address: :return: tx_hash """ safe_creation2: SafeCreation2 = SafeCreation2.objects.get( safe=safe_address) if not safe_creation2.tx_hash: message = f"Safe={safe_address} deploy transaction does not exist" logger.info(message) raise DeployTransactionDoesNotExist(message) if safe_creation2.block_number is not None: message = ( f"Safe={safe_address} has already been deployed with tx-hash={safe_creation2.tx_hash} " f"on block-number={safe_creation2.block_number}") logger.info(message) raise SafeAlreadyExistsException(message) ethereum_tx: EthereumTx = EthereumTx.objects.get( tx_hash=safe_creation2.tx_hash) assert ethereum_tx, "Ethereum tx cannot be missing" self._check_safe_balance(safe_creation2) setup_data = HexBytes(safe_creation2.setup_data.tobytes()) proxy_factory = ProxyFactory(safe_creation2.proxy_factory, self.ethereum_client) # Increase gas price a little gas_price = math.ceil( max(self.gas_station.get_gas_prices().fast, ethereum_tx.gas_price) * 1.1) ethereum_tx_sent = proxy_factory.deploy_proxy_contract_with_nonce( self.funder_account, safe_creation2.master_copy, setup_data, safe_creation2.salt_nonce, gas=safe_creation2.gas_estimated + 50000, # Just in case gas_price=gas_price, nonce=ethereum_tx.nonce, ) # Replace old transaction EthereumTx.objects.create_from_tx_dict(ethereum_tx_sent.tx, ethereum_tx_sent.tx_hash) safe_creation2.tx_hash = ethereum_tx_sent.tx_hash.hex() safe_creation2.save(update_fields=["tx_hash"]) logger.info( "Send again transaction to deploy Safe=%s with tx-hash=%s", safe_address, safe_creation2.tx_hash, ) return safe_creation2
def deploy_create2_safe_tx(self, safe_address: str) -> SafeCreation2: """ Deploys safe if SafeCreation2 exists. :param safe_address: :return: tx_hash """ safe_creation2: SafeCreation2 = SafeCreation2.objects.get( safe=safe_address) if safe_creation2.tx_hash: logger.info( "Safe=%s has already been deployed with tx-hash=%s", safe_address, safe_creation2.tx_hash, ) return safe_creation2 self._check_safe_balance(safe_creation2) setup_data = HexBytes(safe_creation2.setup_data.tobytes()) proxy_factory = ProxyFactory(safe_creation2.proxy_factory, self.ethereum_client) with EthereumNonceLock( self.redis, self.ethereum_client, self.funder_account.address, lock_timeout=60 * 2, ) as tx_nonce: ethereum_tx_sent = proxy_factory.deploy_proxy_contract_with_nonce( self.funder_account, safe_creation2.master_copy, setup_data, safe_creation2.salt_nonce, gas=safe_creation2.gas_estimated + 50000, # Just in case gas_price=safe_creation2.gas_price_estimated, nonce=tx_nonce, ) EthereumTx.objects.create_from_tx_dict(ethereum_tx_sent.tx, ethereum_tx_sent.tx_hash) safe_creation2.tx_hash = ethereum_tx_sent.tx_hash safe_creation2.save(update_fields=["tx_hash"]) logger.info( "Send transaction to deploy Safe=%s with tx-hash=%s", safe_address, ethereum_tx_sent.tx_hash.hex(), ) return safe_creation2
f'Sender {account.address} - Balance: {ether_account_balance}Ξ') if not ethereum_client.w3.eth.getCode(safe_contract_address) \ or not ethereum_client.w3.eth.getCode(proxy_factory_address): print_formatted_text('Network not supported') sys.exit(1) salt_nonce = secrets.SystemRandom().randint(0, 2**256 - 1) # TODO Add support for CPK print_formatted_text( f'Creating new Safe with owners={owners} threshold={threshold} and sat-nonce={salt_nonce}' ) gas_price = 0 safe_creation_tx = Safe.build_safe_create2_tx( ethereum_client, safe_contract_address, proxy_factory_address, salt_nonce, owners, threshold, gas_price, fallback_handler=callback_handler_address, payment_token=None) proxy_factory = ProxyFactory(proxy_factory_address, ethereum_client) ethereum_tx_sent = proxy_factory.deploy_proxy_contract_with_nonce( account, safe_contract_address, safe_creation_tx.safe_setup_data, safe_creation_tx.salt_nonce) print_formatted_text( f'Tx with tx-hash={ethereum_tx_sent.tx_hash.hex()} ' f'will create safe={ethereum_tx_sent.contract_address}')
def main(*args, **kwargs): parser = setup_argument_parser() print_formatted_text( pyfiglet.figlet_format("Gnosis Safe Creator")) # Print fancy text args = parser.parse_args() node_url: URI = args.node_url account: LocalAccount = Account.from_key(args.private_key) owners: List[str] = args.owners if args.owners else [account.address] threshold: int = args.threshold salt_nonce: int = args.salt_nonce to = NULL_ADDRESS data = b"" payment_token = NULL_ADDRESS payment = 0 payment_receiver = NULL_ADDRESS if len(owners) < threshold: print_formatted_text( "Threshold cannot be bigger than the number of unique owners") sys.exit(1) safe_contract_address = args.safe_contract or ( LAST_SAFE_L2_CONTRACT if args.l2 else LAST_SAFE_CONTRACT) proxy_factory_address = args.proxy_factory fallback_handler = args.callback_handler ethereum_client = EthereumClient(node_url) ethereum_network = ethereum_client.get_network() if not ethereum_client.is_contract(safe_contract_address): print_formatted_text( f"Safe contract address {safe_contract_address} " f"does not exist on network {ethereum_network.name}") sys.exit(1) elif not ethereum_client.is_contract(proxy_factory_address): print_formatted_text( f"Proxy contract address {proxy_factory_address} " f"does not exist on network {ethereum_network.name}") sys.exit(1) elif fallback_handler != NULL_ADDRESS and not ethereum_client.is_contract( fallback_handler): print_formatted_text( f"Fallback handler address {fallback_handler} " f"does not exist on network {ethereum_network.name}") sys.exit(1) account_balance: int = ethereum_client.get_balance(account.address) if not account_balance: print_formatted_text( "Client does not have any funds. Let's try anyway in case it's a network without gas costs" ) else: ether_account_balance = round( ethereum_client.w3.fromWei(account_balance, "ether"), 6) print_formatted_text( f"Network {ethereum_client.get_network().name} - Sender {account.address} - " f"Balance: {ether_account_balance}Ξ") if not ethereum_client.w3.eth.getCode( safe_contract_address) or not ethereum_client.w3.eth.getCode( proxy_factory_address): print_formatted_text("Network not supported") sys.exit(1) print_formatted_text( f"Creating new Safe with owners={owners} threshold={threshold} salt-nonce={salt_nonce}" ) print_formatted_text( f"Proxy factory={proxy_factory_address} safe-master-copy={safe_contract_address} and " f"fallback-handler={fallback_handler}") if yes_or_no_question("Do you want to continue?"): safe_contract = get_safe_V1_3_0_contract(ethereum_client.w3, safe_contract_address) safe_creation_tx_data = HexBytes( safe_contract.functions.setup( owners, threshold, to, data, fallback_handler, payment_token, payment, payment_receiver, ).buildTransaction({ "gas": 1, "gasPrice": 1 })["data"]) proxy_factory = ProxyFactory(proxy_factory_address, ethereum_client) ethereum_tx_sent = proxy_factory.deploy_proxy_contract_with_nonce( account, safe_contract_address, safe_creation_tx_data, salt_nonce) print_formatted_text( f"Tx with tx-hash={ethereum_tx_sent.tx_hash.hex()} " f"will create safe={ethereum_tx_sent.contract_address}") print_formatted_text(f"Tx paramters={ethereum_tx_sent.tx}")