def _build_proxy_contract_creation_constructor( self, master_copy: str, initializer: bytes, funder: str, payment_token: str, payment: int) -> ContractConstructor: """ :param master_copy: Master Copy of Gnosis Safe already deployed :param initializer: Data initializer to send to GnosisSafe setup method :param funder: Address that should get the payment (if payment set) :param payment_token: Address if a token is used. If not set, 0x0 will be ether :param payment: Payment :return: Transaction dictionary """ if not funder or funder == NULL_ADDRESS: funder = NULL_ADDRESS payment = 0 return get_paying_proxy_contract(self.w3).constructor( master_copy, initializer, funder, payment_token, payment)
def test_create_multisig_tx(self): w3 = self.w3 # The balance we will send to the safe safe_balance = w3.toWei(0.02, 'ether') # Create Safe funder_account = self.ethereum_test_account funder = funder_account.address accounts = [self.create_account(), self.create_account()] # Signatures must be sorted! accounts.sort(key=lambda account: account.address.lower()) owners = [x.address for x in accounts] threshold = len(accounts) safe_creation = self.deploy_test_safe(owners=owners, threshold=threshold) my_safe_address = safe_creation.safe_address my_safe_contract = get_safe_contract(w3, my_safe_address) SafeContractFactory(address=my_safe_address) to = funder value = safe_balance // 4 data = HexBytes('') operation = 0 safe_tx_gas = 100000 data_gas = 300000 gas_price = self.transaction_service._get_minimum_gas_price() gas_token = NULL_ADDRESS refund_receiver = NULL_ADDRESS safe = Safe(my_safe_address, self.ethereum_client) nonce = safe.retrieve_nonce() safe_tx = safe.build_multisig_tx(to, value, data, operation, safe_tx_gas, data_gas, gas_price, gas_token, refund_receiver, safe_nonce=nonce).safe_tx_hash # Just to make sure we are not miscalculating tx_hash contract_multisig_tx_hash = my_safe_contract.functions.getTransactionHash( to, value, data, operation, safe_tx_gas, data_gas, gas_price, gas_token, refund_receiver, nonce).call() self.assertEqual(safe_tx, contract_multisig_tx_hash) signatures = [account.signHash(safe_tx) for account in accounts] # Check owners are the same contract_owners = my_safe_contract.functions.getOwners().call() self.assertEqual(set(contract_owners), set(owners)) invalid_proxy = self.deploy_example_erc20(1, Account.create().address) with self.assertRaises(InvalidProxyContract): SafeContractFactory(address=invalid_proxy.address) self.transaction_service.create_multisig_tx( invalid_proxy.address, to, value, data, operation, safe_tx_gas, data_gas, gas_price, gas_token, refund_receiver, nonce, signatures, ) # Use invalid master copy random_master_copy = Account.create().address proxy_create_tx = get_paying_proxy_contract(self.w3).constructor( random_master_copy, b'', NULL_ADDRESS, NULL_ADDRESS, 0).buildTransaction({'from': self.ethereum_test_account.address}) tx_hash = self.ethereum_client.send_unsigned_transaction( proxy_create_tx, private_key=self.ethereum_test_account.key) tx_receipt = self.ethereum_client.get_transaction_receipt(tx_hash, timeout=60) proxy_address = tx_receipt.contractAddress with self.assertRaises(InvalidMasterCopyAddress): SafeContractFactory(address=proxy_address) self.transaction_service.create_multisig_tx( proxy_address, to, value, data, operation, safe_tx_gas, data_gas, gas_price, gas_token, refund_receiver, nonce, signatures, ) with self.assertRaises(NotEnoughFundsForMultisigTx): self.transaction_service.create_multisig_tx( my_safe_address, to, value, data, operation, safe_tx_gas, data_gas, gas_price, gas_token, refund_receiver, nonce, signatures, ) # Send something to the safe self.send_tx({ 'to': my_safe_address, 'value': safe_balance }, funder_account) bad_refund_receiver = get_eth_address_with_key()[0] with self.assertRaises(InvalidRefundReceiver): self.transaction_service.create_multisig_tx( my_safe_address, to, value, data, operation, safe_tx_gas, data_gas, gas_price, gas_token, bad_refund_receiver, nonce, signatures, ) invalid_gas_price = 0 with self.assertRaises(RefundMustBeEnabled): self.transaction_service.create_multisig_tx( my_safe_address, to, value, data, operation, safe_tx_gas, data_gas, invalid_gas_price, gas_token, refund_receiver, nonce, signatures, ) with self.assertRaises(GasPriceTooLow): self.transaction_service.create_multisig_tx( my_safe_address, to, value, data, operation, safe_tx_gas, data_gas, self.transaction_service._estimate_tx_gas_price( self.transaction_service._get_minimum_gas_price(), gas_token) - 1, gas_token, refund_receiver, nonce, signatures) with self.assertRaises(InvalidGasToken): invalid_gas_token = Account.create().address self.transaction_service.create_multisig_tx( my_safe_address, to, value, data, operation, safe_tx_gas, data_gas, gas_price, invalid_gas_token, refund_receiver, nonce, reversed(signatures)) with self.assertRaises(SignaturesNotSorted): self.transaction_service.create_multisig_tx( my_safe_address, to, value, data, operation, safe_tx_gas, data_gas, gas_price, gas_token, refund_receiver, nonce, reversed(signatures)) with self.assertRaises(SignerIsBanned): for account in accounts: BannedSignerFactory(address=account.address) self.transaction_service.create_multisig_tx( my_safe_address, to, value, data, operation, safe_tx_gas, data_gas, gas_price, gas_token, refund_receiver, nonce, signatures) BannedSigner.objects.all().delete() self.assertEqual(BannedSigner.objects.count(), 0) sender = self.transaction_service.tx_sender_account.address sender_balance = w3.eth.getBalance(sender) safe_balance = w3.eth.getBalance(my_safe_address) safe_multisig_tx = self.transaction_service.create_multisig_tx( my_safe_address, to, value, data, operation, safe_tx_gas, data_gas, gas_price, gas_token, refund_receiver, nonce, signatures, ) with self.assertRaises(SafeMultisigTxExists): self.transaction_service.create_multisig_tx( my_safe_address, to, value, data, operation, safe_tx_gas, data_gas, gas_price, gas_token, refund_receiver, nonce, signatures, ) tx_receipt = w3.eth.waitForTransactionReceipt( safe_multisig_tx.ethereum_tx.tx_hash) self.assertTrue(tx_receipt['status']) self.assertEqual(w3.toChecksumAddress(tx_receipt['from']), sender) self.assertEqual(w3.toChecksumAddress(tx_receipt['to']), my_safe_address) self.assertGreater(safe_multisig_tx.ethereum_tx.gas_price, gas_price) # We used minimum gas price sender_new_balance = w3.eth.getBalance(sender) gas_used = tx_receipt['gasUsed'] tx_fees = gas_used * safe_multisig_tx.ethereum_tx.gas_price estimated_refund = ( safe_multisig_tx.data_gas + safe_multisig_tx.safe_tx_gas) * safe_multisig_tx.gas_price real_refund = safe_balance - w3.eth.getBalance(my_safe_address) - value # Real refund can be less if not all the `safe_tx_gas` is used self.assertGreaterEqual(estimated_refund, real_refund) self.assertEqual(sender_new_balance, sender_balance - tx_fees + real_refund) self.assertEqual(safe.retrieve_nonce(), 1) # Send again the tx and check that works nonce += 1 value = 0 safe_tx = safe.build_multisig_tx(to, value, data, operation, safe_tx_gas, data_gas, gas_price, gas_token, refund_receiver, safe_nonce=nonce) # Use invalid signatures with self.assertRaises(InvalidOwners): signatures = [ Account.create().signHash(safe_tx.safe_tx_hash) for _ in range(len(accounts)) ] self.transaction_service.create_multisig_tx( my_safe_address, to, value, data, operation, safe_tx_gas, data_gas, gas_price, gas_token, refund_receiver, nonce, signatures, ) signatures = [ account.signHash(safe_tx.safe_tx_hash) for account in accounts ] safe_multisig_tx = self.transaction_service.create_multisig_tx( my_safe_address, to, value, data, operation, safe_tx_gas, data_gas, gas_price, gas_token, refund_receiver, nonce, signatures, ) tx_receipt = w3.eth.waitForTransactionReceipt( safe_multisig_tx.ethereum_tx.tx_hash) self.assertTrue(tx_receipt['status'])