def build_test_safe( self, number_owners: int = 3, threshold: Optional[int] = None, owners: Optional[List[str]] = None, fallback_handler: Optional[str] = None, ) -> SafeCreate2Tx: salt_nonce = generate_salt_nonce() owners = (owners if owners else [Account.create().address for _ in range(number_owners)]) threshold = threshold if threshold else len(owners) - 1 gas_price = self.ethereum_client.w3.eth.gas_price return Safe.build_safe_create2_tx( self.ethereum_client, self.safe_contract_address, self.proxy_factory_contract_address, salt_nonce, owners, threshold, fallback_handler=fallback_handler, gas_price=gas_price, payment_token=None, fixed_creation_cost=0, )
def test_deploy_proxy_contract_with_nonce(self): salt_nonce = generate_salt_nonce() owners = [Account.create().address for _ in range(2)] threshold = 2 payment_token = None safe_create2_tx = Safe.build_safe_create2_tx( self.ethereum_client, self.safe_contract_address, self.proxy_factory_contract_address, salt_nonce, owners, threshold, self.gas_price, payment_token) # Send ether for safe deploying costs self.send_tx( { 'to': safe_create2_tx.safe_address, 'value': safe_create2_tx.payment }, self.ethereum_test_account) proxy_factory = ProxyFactory(self.proxy_factory_contract_address, self.ethereum_client) ethereum_tx_sent = proxy_factory.deploy_proxy_contract_with_nonce( self.ethereum_test_account, safe_create2_tx.master_copy_address, safe_create2_tx.safe_setup_data, salt_nonce, safe_create2_tx.gas, gas_price=self.gas_price) receipt = self.ethereum_client.get_transaction_receipt( ethereum_tx_sent.tx_hash, timeout=20) self.assertEqual(receipt.status, 1) safe = Safe(ethereum_tx_sent.contract_address, self.ethereum_client) self.assertEqual(ethereum_tx_sent.contract_address, safe_create2_tx.safe_address) self.assertEqual(set(safe.retrieve_owners()), set(owners)) self.assertEqual(safe.retrieve_master_copy_address(), safe_create2_tx.master_copy_address)
def predict_address(self, salt_nonce: int, owners: Iterable[str], threshold: int, payment_token: Optional[str]) -> str: """ Return the predicted Safe address :param salt_nonce: Random value for solidity `create2` salt :param owners: Owners of the new Safe :param threshold: Minimum number of users required to operate the Safe :param payment_token: Address of the payment token, otherwise `ether` is used :rtype: str :raises: InvalidPaymentToken """ payment_token = payment_token or NULL_ADDRESS payment_token_eth_value = self._get_token_eth_value_or_raise( payment_token) gas_price: int = self._get_configured_gas_price() current_block_number = self.ethereum_client.current_block_number logger.info( 'Safe.build_safe_create2_tx params: %s %s %s %s %s %s %s %s %s %s %s', self.safe_contract_address, self.proxy_factory.address, salt_nonce, owners, threshold, gas_price, payment_token, self.funder_account.address, self.default_callback_handler, payment_token_eth_value, self.safe_fixed_creation_cost) safe_creation_tx = Safe.build_safe_create2_tx( self.ethereum_client, self.safe_contract_address, self.proxy_factory.address, salt_nonce, owners, threshold, gas_price, payment_token, payment_receiver=self.funder_account.address, fallback_handler=self.default_callback_handler, payment_token_eth_value=payment_token_eth_value, fixed_creation_cost=self.safe_fixed_creation_cost) return safe_creation_tx.safe_address
def create2_safe_tx(self, salt_nonce: int, owners: Iterable[str], threshold: int, payment_token: Optional[str]) -> SafeCreation2: """ Prepare creation tx for a new safe using CREATE2 method :param salt_nonce: Random value for solidity `create2` salt :param owners: Owners of the new Safe :param threshold: Minimum number of users required to operate the Safe :param payment_token: Address of the payment token, otherwise `ether` is used :rtype: SafeCreation2 :raises: InvalidPaymentToken """ payment_token = payment_token or NULL_ADDRESS payment_token_eth_value = self._get_token_eth_value_or_raise( payment_token) gas_price: int = self._get_configured_gas_price() current_block_number = self.ethereum_client.current_block_number logger.debug('Building safe create2 tx with gas price %d', gas_price) safe_creation_tx = Safe.build_safe_create2_tx( self.ethereum_client, self.safe_contract_address, self.proxy_factory.address, salt_nonce, owners, threshold, gas_price, payment_token, payment_receiver=self.funder_account.address, fallback_handler=self.default_callback_handler, payment_token_eth_value=payment_token_eth_value, fixed_creation_cost=self.safe_fixed_creation_cost) safe_contract, created = SafeContract.objects.get_or_create( address=safe_creation_tx.safe_address, defaults={'master_copy': safe_creation_tx.master_copy_address}) if not created: raise SafeAlreadyExistsException( f'Safe={safe_contract.address} cannot be created, already exists' ) # Enable tx and erc20 tracing SafeTxStatus.objects.create(safe=safe_contract, initial_block_number=current_block_number, tx_block_number=current_block_number, erc_20_block_number=current_block_number) return SafeCreation2.objects.create( safe=safe_contract, master_copy=safe_creation_tx.master_copy_address, proxy_factory=safe_creation_tx.proxy_factory_address, salt_nonce=salt_nonce, owners=owners, threshold=threshold, # to # Contract address for optional delegate call # data # Data payload for optional delegate call payment_token=None if safe_creation_tx.payment_token == NULL_ADDRESS else safe_creation_tx.payment_token, payment=safe_creation_tx.payment, payment_receiver=safe_creation_tx.payment_receiver, setup_data=safe_creation_tx.safe_setup_data, gas_estimated=safe_creation_tx.gas, gas_price_estimated=safe_creation_tx.gas_price, )
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}')