Beispiel #1
0
    def __init__(self, w3: Web3, master_copy_address: str,
                 proxy_factory_address: str):
        """
        Init builder for safe creation using create2
        :param w3: Web3 instance
        :param master_copy_address: `Gnosis Safe` master copy address
        :param proxy_factory_address: `Gnosis Proxy Factory` address
        """
        assert Web3.isChecksumAddress(master_copy_address)
        assert Web3.isChecksumAddress(proxy_factory_address)

        self.w3 = w3
        self.master_copy_address = master_copy_address
        self.proxy_factory_address = proxy_factory_address
        self.safe_version = get_safe_contract(
            w3, master_copy_address).functions.VERSION().call()
        if self.safe_version == '1.1.1':
            self.master_copy_contract = get_safe_contract(
                w3, master_copy_address)
        elif self.safe_version == '1.0.0':
            self.master_copy_contract = get_safe_V1_0_0_contract(
                w3, master_copy_address)
        else:
            raise ValueError('Safe version must be 1.1.1 or 1.0.0')
        self.proxy_factory_contract = get_proxy_factory_contract(
            w3, proxy_factory_address)
    def setUpTestData(cls):
        super().setUpTestData()

        for key, value in contract_addresses.items():
            if callable(value):
                contract_addresses[key] = value(cls.ethereum_client, cls.ethereum_test_account).contract_address

        settings.SAFE_CONTRACT_ADDRESS = contract_addresses['safe']
        settings.SAFE_MULTISEND_ADDRESS = contract_addresses['multi_send']
        settings.SAFE_V1_0_0_CONTRACT_ADDRESS = contract_addresses['safe_V1_0_0']
        settings.SAFE_V0_0_1_CONTRACT_ADDRESS = contract_addresses['safe_V0_0_1']
        settings.SAFE_PROXY_FACTORY_ADDRESS = contract_addresses['proxy_factory']
        settings.SAFE_PROXY_FACTORY_V1_0_0_ADDRESS = contract_addresses['proxy_factory_V1_0_0']
        settings.SAFE_VALID_CONTRACT_ADDRESSES = {settings.SAFE_CONTRACT_ADDRESS,
                                                  settings.SAFE_V1_0_0_CONTRACT_ADDRESS,
                                                  settings.SAFE_V0_0_1_CONTRACT_ADDRESS,
                                                  }
        cls.safe_contract_address = contract_addresses['safe']
        cls.safe_contract = get_safe_contract(cls.w3, cls.safe_contract_address)
        cls.safe_contract_V1_0_0_address = contract_addresses['safe_V1_0_0']
        cls.safe_contract_V1_0_0 = get_safe_V1_0_0_contract(cls.w3, cls.safe_contract_V1_0_0_address)
        cls.safe_contract_V0_0_1_address = contract_addresses['safe_V0_0_1']
        cls.safe_contract_V0_0_1 = get_safe_V1_0_0_contract(cls.w3, cls.safe_contract_V0_0_1_address)
        cls.proxy_factory_contract_address = contract_addresses['proxy_factory']
        cls.proxy_factory_contract = get_proxy_factory_contract(cls.w3, cls.proxy_factory_contract_address)
        cls.proxy_factory = ProxyFactory(cls.proxy_factory_contract_address, cls.ethereum_client)
        cls.multi_send_contract = get_multi_send_contract(cls.w3, contract_addresses['multi_send'])
        cls.multi_send = MultiSend(cls.multi_send_contract.address, cls.ethereum_client)
Beispiel #3
0
    def test_contract_signature(self):
        safe_account = self.ethereum_test_account
        safe = self.deploy_test_safe(owners=[safe_account.address], initial_funding_wei=Web3.toWei(0.01, 'ether'))
        safe_contract = get_safe_contract(self.ethereum_client.w3, safe.safe_address)
        safe_tx_hash = Web3.keccak(text='test')
        signature_r = HexBytes(safe.safe_address.replace('0x', '').rjust(64, '0'))
        signature_s = HexBytes('0' * 62 + '41')  # Position of end of signature
        signature_v = HexBytes('00')
        contract_signature = HexBytes('0' * 64)
        signature = signature_r + signature_s + signature_v + contract_signature

        safe_signature = SafeContractSignature(signature, safe_tx_hash, self.ethereum_client)
        self.assertFalse(safe_signature.ok)

        # Approve the hash
        tx = safe_contract.functions.approveHash(
            safe_tx_hash
        ).buildTransaction({'from': safe_account.address})
        self.ethereum_client.send_unsigned_transaction(tx, private_key=safe_account.key)

        safe_signature = SafeContractSignature(signature, safe_tx_hash, self.ethereum_client)
        self.assertFalse(safe_signature.ok)

        # Test with an owner signature
        safe_tx_hash_2 = Web3.keccak(text='test2')
        safe_tx_hash_2_message_hash = safe_contract.functions.getMessageHash(safe_tx_hash_2).call()
        safe_signature = SafeContractSignature(signature, safe_tx_hash_2, self.ethereum_client)
        self.assertFalse(safe_signature.ok)
        contract_signature = encode_single('bytes', safe_account.signHash(safe_tx_hash_2_message_hash)['signature'])
        signature = signature_r + signature_s + signature_v + contract_signature

        safe_signature = SafeContractSignature(signature, safe_tx_hash_2, self.ethereum_client)
        self.assertTrue(safe_signature.ok)
 def __init__(self):
     # This safe_tx_failure events allow us to detect a failed safe transaction
     self.safe_tx_failure_events = [get_safe_V1_0_0_contract(Web3()).events.ExecutionFailed(),
                                    get_safe_contract(Web3()).events.ExecutionFailure()]
     self.safe_tx_failure_events_topics = {event_abi_to_log_topic(event.abi) for event
                                           in self.safe_tx_failure_events}
     self.safe_status_cache: Dict[str, SafeStatus] = {}
    def test_safe_creation_tx_builder_with_payment(self):
        logger.info("Test Safe Proxy creation With Payment".center(LOG_TITLE_WIDTH, '-'))
        w3 = self.w3

        s = generate_valid_s()

        funder_account = self.ethereum_test_account
        owners = [get_eth_address_with_key()[0] for _ in range(2)]
        threshold = len(owners) - 1
        gas_price = self.gas_price

        safe_creation_tx = SafeCreationTx(w3=w3,
                                          owners=owners,
                                          threshold=threshold,
                                          signature_s=s,
                                          master_copy=self.safe_contract_V0_0_1_address,
                                          gas_price=gas_price,
                                          funder=funder_account.address)

        user_external_account = Account.create()
        # Send some ether to that account
        safe_balance = w3.toWei(0.01, 'ether')
        self.send_tx({
            'to': user_external_account.address,
            'value': safe_balance * 2
        }, funder_account)

        logger.info("Send %d ether to safe %s", w3.fromWei(safe_balance, 'ether'), safe_creation_tx.safe_address)
        self.send_tx({
            'to': safe_creation_tx.safe_address,
            'value': safe_balance
        }, user_external_account)
        self.assertEqual(w3.eth.getBalance(safe_creation_tx.safe_address), safe_balance)

        logger.info("Send %d gwei to deployer %s", w3.fromWei(safe_creation_tx.payment_ether, 'gwei'),
                    safe_creation_tx.deployer_address)
        self.send_tx({
            'to': safe_creation_tx.deployer_address,
            'value': safe_creation_tx.payment_ether
        }, funder_account)

        logger.info("Create proxy contract with address %s", safe_creation_tx.safe_address)

        funder_balance = w3.eth.getBalance(funder_account.address)

        # This tx will create the Safe Proxy and return ether to the funder
        tx_hash = w3.eth.sendRawTransaction(safe_creation_tx.tx_raw)
        tx_receipt = w3.eth.waitForTransactionReceipt(tx_hash)
        self.assertEqual(tx_receipt.contractAddress, safe_creation_tx.safe_address)

        self.assertEqual(w3.eth.getBalance(funder_account.address),
                         funder_balance + safe_creation_tx.payment)

        logger.info("Deployer account has still %d gwei left (will be lost)",
                    w3.fromWei(w3.eth.getBalance(safe_creation_tx.deployer_address), 'gwei'))

        deployed_safe_proxy_contract = get_safe_contract(w3, tx_receipt.contractAddress)

        self.assertEqual(deployed_safe_proxy_contract.functions.getThreshold().call(), threshold)
        self.assertEqual(deployed_safe_proxy_contract.functions.getOwners().call(), owners)
    def deploy_test_safe(
            self,
            number_owners: int = 3,
            threshold: Optional[int] = None,
            owners: Optional[List[str]] = None,
            initial_funding_wei: int = 0,
            fallback_handler: Optional[str] = None) -> SafeCreate2Tx:
        owners = owners if owners else [
            Account.create().address for _ in range(number_owners)
        ]
        if not threshold:
            threshold = len(owners) - 1 if len(owners) > 1 else 1
        safe_creation_tx = self.build_test_safe(
            threshold=threshold,
            owners=owners,
            fallback_handler=fallback_handler)
        funder_account = self.ethereum_test_account

        ethereum_tx_sent = self.proxy_factory.deploy_proxy_contract_with_nonce(
            funder_account, self.safe_contract_address,
            safe_creation_tx.safe_setup_data, safe_creation_tx.salt_nonce)

        safe_address = ethereum_tx_sent.contract_address
        if initial_funding_wei:
            self.send_ether(safe_address, initial_funding_wei)

        safe_instance = get_safe_contract(self.w3, safe_address)

        self.assertEqual(safe_instance.functions.getThreshold().call(),
                         threshold)
        self.assertEqual(safe_instance.functions.getOwners().call(), owners)
        self.assertEqual(safe_address, safe_creation_tx.safe_address)
        return safe_creation_tx
    def __init__(self):
        #TODO  Refactor this using inheritance
        self.dummy_w3 = Web3()
        exchanges = [
            get_uniswap_exchange_contract(self.dummy_w3),
            self.dummy_w3.eth.contract(abi=gnosis_protocol_abi)
        ]
        sight_contracts = [
            self.dummy_w3.eth.contract(abi=abi)
            for abi in (conditional_token_abi, market_maker_abi,
                        market_maker_factory_abi)
        ]
        erc_contracts = [
            get_erc721_contract(self.dummy_w3),
            get_erc20_contract(self.dummy_w3)
        ]
        safe_contracts = [
            get_safe_V0_0_1_contract(self.dummy_w3),
            get_safe_V1_0_0_contract(self.dummy_w3),
            get_safe_contract(self.dummy_w3)
        ]

        # Order is important. If signature is the same (e.g. renaming of `baseGas`) last elements in the list
        # will take preference
        self.supported_contracts = exchanges + sight_contracts + erc_contracts + safe_contracts

        # Web3 generates possible selectors every time. We cache that and use a dict to do a fast check
        # Store selectors with abi
        self.supported_fn_selectors: Dict[bytes, ContractFunction] = {}
        for supported_contract in self.supported_contracts:
            self.supported_fn_selectors.update(
                self._generate_selectors_with_abis_from_contract(
                    supported_contract))
Beispiel #8
0
    def deploy_master_contract(
            ethereum_client: EthereumClient,
            deployer_account: LocalAccount) -> EthereumTxSent:
        """
        Deploy master contract. Takes deployer_account (if unlocked in the node) or the deployer private key
        Safe with version > v1.1.1 doesn't need to be initialized as it already has a constructor
        :param ethereum_client:
        :param deployer_account: Ethereum account
        :return: deployed contract address
        """

        safe_contract = get_safe_contract(ethereum_client.w3)
        constructor_tx = safe_contract.constructor().buildTransaction()
        tx_hash = ethereum_client.send_unsigned_transaction(
            constructor_tx, private_key=deployer_account.key)
        tx_receipt = ethereum_client.get_transaction_receipt(tx_hash,
                                                             timeout=60)
        assert tx_receipt
        assert tx_receipt['status']

        ethereum_tx_sent = EthereumTxSent(tx_hash, constructor_tx,
                                          tx_receipt['contractAddress'])
        logger.info("Deployed and initialized Safe Master Contract=%s by %s",
                    ethereum_tx_sent.contract_address,
                    deployer_account.address)
        return ethereum_tx_sent
Beispiel #9
0
    def create(ethereum_client: EthereumClient,
               deployer_account: LocalAccount,
               master_copy_address: str,
               owners: List[str],
               threshold: int,
               fallback_handler: str = NULL_ADDRESS,
               proxy_factory_address: Optional[str] = None,
               payment_token: str = NULL_ADDRESS,
               payment: int = 0,
               payment_receiver: str = NULL_ADDRESS) -> EthereumTxSent:
        """
        Deploy new Safe proxy pointing to the specified `master_copy` address and configured
        with the provided `owners` and `threshold`. By default, payment for the deployer of the tx will be `0`.
        If `proxy_factory_address` is set deployment will be done using the proxy factory instead of calling
        the `constructor` of a new `DelegatedProxy`
        Using `proxy_factory_address` is recommended, as it takes less gas.
        (Testing with `Ganache` and 1 owner 261534 without proxy vs 229022 with Proxy)
        """

        assert owners, 'At least one owner must be set'
        assert threshold >= len(owners), 'Threshold=%d must be >= %d' % (
            threshold, len(owners))

        initializer = get_safe_contract(
            ethereum_client.w3, NULL_ADDRESS
        ).functions.setup(
            owners,
            threshold,
            NULL_ADDRESS,  # Contract address for optional delegate call
            b'',  # Data payload for optional delegate call
            fallback_handler,  # Handler for fallback calls to this contract,
            payment_token,
            payment,
            payment_receiver).buildTransaction({
                'gas': 1,
                'gasPrice': 1
            })['data']

        if proxy_factory_address:
            proxy_factory = ProxyFactory(proxy_factory_address,
                                         ethereum_client)
            return proxy_factory.deploy_proxy_contract(deployer_account,
                                                       master_copy_address,
                                                       initializer=initializer)

        proxy_contract = get_delegate_constructor_proxy_contract(
            ethereum_client.w3)
        tx = proxy_contract.constructor(master_copy_address,
                                        initializer).buildTransaction(
                                            {'from': deployer_account.address})
        tx['gas'] = tx['gas'] * 100000
        tx_hash = ethereum_client.send_unsigned_transaction(
            tx, private_key=deployer_account.key)
        tx_receipt = ethereum_client.get_transaction_receipt(tx_hash,
                                                             timeout=60)
        assert tx_receipt.status

        contract_address = tx_receipt.contractAddress
        return EthereumTxSent(tx_hash, tx, contract_address)
Beispiel #10
0
    def __init__(self):
        self.dummy_w3 = Web3()
        self.safe_contracts = [get_safe_V0_0_1_contract(self.dummy_w3), get_safe_V1_0_0_contract(self.dummy_w3),
                               get_safe_contract(self.dummy_w3)]

        # Order is important. If signature is the same (e.g. renaming of `baseGas`) last elements in the list
        # will take preference
        self.supported_contracts = self.safe_contracts
    def test_safe_creation_tx_builder(self):
        logger.info(
            "Test Safe Proxy creation without payment".center(LOG_TITLE_WIDTH, "-")
        )
        w3 = self.w3

        s = generate_valid_s()

        funder_account = self.ethereum_test_account
        owners = [get_eth_address_with_key()[0] for _ in range(4)]
        threshold = len(owners) - 1
        gas_price = self.gas_price

        safe_creation_tx = SafeCreationTx(
            w3=w3,
            owners=owners,
            threshold=threshold,
            signature_s=s,
            master_copy=self.safe_contract_V0_0_1_address,
            gas_price=gas_price,
            funder=NULL_ADDRESS,
        )

        logger.info(
            "Send %d gwei to deployer %s",
            w3.fromWei(safe_creation_tx.payment_ether, "gwei"),
            safe_creation_tx.deployer_address,
        )

        self.send_tx(
            {
                "to": safe_creation_tx.deployer_address,
                "value": safe_creation_tx.payment_ether,
            },
            funder_account,
        )

        logger.info(
            "Create proxy contract with address %s", safe_creation_tx.safe_address
        )

        tx_hash = w3.eth.send_raw_transaction(safe_creation_tx.tx_raw)
        tx_receipt = w3.eth.wait_for_transaction_receipt(tx_hash)
        self.assertEqual(tx_receipt.contractAddress, safe_creation_tx.safe_address)

        deployed_safe_proxy_contract = get_safe_contract(w3, tx_receipt.contractAddress)

        logger.info(
            "Deployer account has still %d gwei left (will be lost)",
            w3.fromWei(w3.eth.get_balance(safe_creation_tx.deployer_address), "gwei"),
        )

        self.assertEqual(
            deployed_safe_proxy_contract.functions.getThreshold().call(), threshold
        )
        self.assertEqual(
            deployed_safe_proxy_contract.functions.getOwners().call(), owners
        )
Beispiel #12
0
    def test_safe_create2_tx_builder_with_payment_receiver(self):
        w3 = self.w3

        salt_nonce = generate_salt_nonce()
        payment_receiver = Account.create().address
        funder_account = self.ethereum_test_account
        owners = [Account.create().address for _ in range(4)]
        threshold = len(owners) - 1
        gas_price = self.gas_price

        safe_creation_tx = SafeCreate2TxBuilder(
            w3=w3,
            master_copy_address=self.safe_contract_address,
            proxy_factory_address=self.proxy_factory_contract_address).build(
                owners=owners,
                threshold=threshold,
                salt_nonce=salt_nonce,
                gas_price=gas_price,
                payment_receiver=payment_receiver)

        self.assertEqual(safe_creation_tx.payment,
                         safe_creation_tx.payment_ether)
        self.send_tx(
            {
                'to': safe_creation_tx.safe_address,
                'value': safe_creation_tx.payment,
            }, funder_account)

        ethereum_tx_sent = self.proxy_factory.deploy_proxy_contract_with_nonce(
            funder_account,
            self.safe_contract_address,
            safe_creation_tx.safe_setup_data,
            salt_nonce,
            gas=safe_creation_tx.gas,
            gas_price=safe_creation_tx.gas_price)
        tx_receipt = w3.eth.wait_for_transaction_receipt(
            ethereum_tx_sent.tx_hash)
        self.assertEqual(tx_receipt.status, 1)
        logs = self.proxy_factory_contract.events.ProxyCreation(
        ).processReceipt(tx_receipt)
        log = logs[0]
        self.assertIsNone(tx_receipt.contractAddress)
        self.assertEqual(log['event'], 'ProxyCreation')
        proxy_address = log['args']['proxy']
        self.assertEqual(proxy_address, safe_creation_tx.safe_address)
        self.assertEqual(ethereum_tx_sent.contract_address,
                         safe_creation_tx.safe_address)

        deployed_safe_proxy_contract = get_safe_contract(w3, proxy_address)
        self.assertEqual(
            deployed_safe_proxy_contract.functions.getThreshold().call(),
            threshold)
        self.assertEqual(
            deployed_safe_proxy_contract.functions.getOwners().call(), owners)
        self.assertEqual(self.ethereum_client.get_balance(proxy_address), 0)

        self.assertEqual(self.ethereum_client.get_balance(payment_receiver),
                         safe_creation_tx.payment)
Beispiel #13
0
 def w3_tx(self):
     """
     :return: Web3 contract tx prepared for `call`, `transact` or `buildTransaction`
     """
     safe_contract = get_safe_contract(self.w3, address=self.safe_address)
     return safe_contract.functions.execTransaction(
         self.to, self.value, self.data, self.operation, self.safe_tx_gas,
         self.base_gas, self.gas_price, self.gas_token,
         self.refund_receiver, self.signatures)
    def get_supported_abis(self) -> List[ABI]:
        safe_abis = [get_safe_V0_0_1_contract(self.dummy_w3).abi,
                     get_safe_V1_0_0_contract(self.dummy_w3).abi,
                     get_safe_V1_3_0_contract(self.dummy_w3).abi,
                     get_safe_contract(self.dummy_w3).abi]

        # Order is important. If signature is the same (e.g. renaming of `baseGas`) last elements in the list
        # will take preference
        return safe_abis
Beispiel #15
0
 def _check_signature(self) -> bool:
     contract_signature_len = int.from_bytes(
         self.signature[self.s:self.s + 32], 'big')
     contract_signature = self.signature[self.s + 32:self.s + 32 +
                                         contract_signature_len]
     safe_contract = get_safe_contract(self.ethereum_client.w3, self.owner)
     return safe_contract.functions.isValidSignature(
         self.safe_tx_hash,
         contract_signature).call() == self.EIP1271_MAGIC_VALUE
Beispiel #16
0
 def __init__(self, network, address, rpc_endpoint_url, safe_relay_url):
     """Initializes a Safe.
     """
     self.network = network
     self.address = address
     self.w3 = web3.Web3(web3.HTTPProvider(rpc_endpoint_url))
     # https://web3py.readthedocs.io/en/stable/middleware.html#geth-style-proof-of-authority
     self.w3.middleware_stack.inject(geth_poa_middleware, layer=0)
     self.contract = get_safe_contract(w3=self.w3, address=self.address)
     self.safe_relay = Relay(safe_relay_url)
 def is_valid(self, ethereum_client: EthereumClient, *args) -> bool:
     safe_contract = get_safe_contract(ethereum_client.w3, self.owner)
     try:
         for block_identifier in ('pending', 'latest'):
             return safe_contract.functions.isValidSignature(
                 self.safe_tx_hash, self.contract_signature).call(
                     block_identifier=block_identifier
                 ) == self.EIP1271_MAGIC_VALUE
     except BadFunctionCallOutput as e:  # Error using `pending` block identifier
         pass
     raise e  # This should never happen
 def is_valid(self, ethereum_client: EthereumClient,
              safe_address: str) -> bool:
     safe_contract = get_safe_contract(ethereum_client.w3, safe_address)
     try:
         for block_identifier in ('pending', 'latest'):
             return safe_contract.functions.approvedHashes(
                 self.owner, self.safe_tx_hash).call(
                     block_identifier=block_identifier) == 1
     except BadFunctionCallOutput as e:  # Error using `pending` block identifier
         pass
     raise e  # This should never happen
Beispiel #19
0
    def test_send_previously_approved_tx(self):
        number_owners = 4
        accounts = [self.create_account(initial_ether=0.01) for _ in range(number_owners)]
        accounts.sort(key=lambda x: x.address.lower())
        owners = [account.address for account in accounts]

        safe_creation = self.deploy_test_safe(threshold=2, owners=owners,
                                              initial_funding_wei=self.w3.toWei(0.01, 'ether'))
        safe_address = safe_creation.safe_address
        safe = Safe(safe_address, self.ethereum_client)
        safe_instance = get_safe_contract(self.w3, safe_address)

        to, _ = get_eth_address_with_key()
        value = self.w3.toWei(0.001, 'ether')
        data = b''
        operation = 0
        safe_tx_gas = 500000
        data_gas = 500000
        gas_price = 1
        gas_token = NULL_ADDRESS
        refund_receiver = NULL_ADDRESS
        nonce = safe.retrieve_nonce()

        self.assertEqual(nonce, 0)

        safe_tx_hash = 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

        safe_tx_contract_hash = safe_instance.functions.getTransactionHash(to, value, data, operation,
                                                                           safe_tx_gas, data_gas, gas_price, gas_token,
                                                                           refund_receiver, nonce).call()

        self.assertEqual(safe_tx_hash, safe_tx_contract_hash)

        approve_hash_fn = safe_instance.functions.approveHash(safe_tx_hash)
        for account in accounts[:2]:
            self.send_tx(approve_hash_fn.buildTransaction({'from': account.address}), account)

        for owner in (owners[0], owners[1]):
            is_approved = safe.retrieve_is_hash_approved(owner, safe_tx_hash)
            self.assertTrue(is_approved)

        # Prepare signatures. v must be 1 for previously signed and r the owner
        signatures = (1, int(owners[0], 16), 0), (1, int(owners[1], 16), 0)
        signature_bytes = signatures_to_bytes(signatures)

        safe.send_multisig_tx(to, value, data, operation, safe_tx_gas,
                              data_gas, gas_price, gas_token, refund_receiver, signature_bytes,
                              self.ethereum_test_account.key)

        balance = self.w3.eth.get_balance(to)
        self.assertEqual(value, balance)
Beispiel #20
0
    def __init__(self, ethereum_client: EthereumClient):
        # This safe_tx_failure events allow us to detect a failed safe transaction
        self.ethereum_client = ethereum_client
        dummy_w3 = Web3()
        self.safe_tx_failure_events = [
            get_safe_V1_0_0_contract(dummy_w3).events.ExecutionFailed(),
            get_safe_contract(dummy_w3).events.ExecutionFailure()
        ]
        self.safe_tx_module_failure_events = [
            get_safe_contract(dummy_w3).events.ExecutionFromModuleFailure()
        ]

        self.safe_tx_failure_events_topics = {
            event_abi_to_log_topic(event.abi)
            for event in self.safe_tx_failure_events
        }
        self.safe_tx_module_failure_topics = {
            event_abi_to_log_topic(event.abi)
            for event in self.safe_tx_module_failure_events
        }
        self.safe_status_cache: Dict[str, SafeStatus] = {}
Beispiel #21
0
    def __init__(self):
        self.dummy_w3 = Web3()
        # Order is important. If signature is the same (e.g. renaming of `baseGas`) last elements in the list
        # will take preference
        self.supported_contracts = [get_safe_V0_0_1_contract(self.dummy_w3),
                                    get_safe_V1_0_0_contract(self.dummy_w3),
                                    get_safe_contract(self.dummy_w3)]

        # Web3 generates possible selectors every time. We cache that and use a dict to do a fast check
        # Store selectors with abi
        self.supported_fn_selectors: Dict[bytes, ContractFunction] = {}
        for supported_contract in self.supported_contracts:
            self.supported_fn_selectors.update(self._generate_selectors_with_abis_from_contract(supported_contract))
Beispiel #22
0
    def is_version_updated(self) -> bool:
        """
        :return: True if Safe Master Copy is updated, False otherwise
        """

        if self._safe_cli_info.master_copy == LAST_SAFE_CONTRACT:
            return True
        else:  # Check versions, maybe safe-cli addresses were not updated
            safe_contract = get_safe_contract(self.ethereum_client.w3, LAST_SAFE_CONTRACT)
            try:
                safe_contract_version = safe_contract.functions.VERSION().call()
            except BadFunctionCallOutput:  # Safe master copy is not deployed or errored, maybe custom network
                return True  # We cannot say you are not updated ¯\_(ツ)_/¯
            return semantic_version.parse(self.safe_cli_info.version) >= semantic_version.parse(safe_contract_version)
Beispiel #23
0
 def is_valid(self, ethereum_client: EthereumClient, *args) -> bool:
     safe_contract = get_safe_contract(ethereum_client.w3, self.owner)
     for block_identifier in ('pending', 'latest'):
         try:
             return safe_contract.functions.isValidSignature(
                 self.safe_tx_hash, self.contract_signature).call(
                     block_identifier=block_identifier
                 ) == self.EIP1271_MAGIC_VALUE
         except (BadFunctionCallOutput, DecodingError):
             # Error using `pending` block identifier or contract does not exist
             logger.warning(
                 'Cannot check EIP1271 signature from contract %s',
                 self.owner)
     return False
Beispiel #24
0
 def is_valid(self, ethereum_client: EthereumClient, safe_address: str) -> bool:
     safe_contract = get_safe_contract(ethereum_client.w3, safe_address)
     exception: Exception
     for block_identifier in ("pending", "latest"):
         try:
             return (
                 safe_contract.functions.approvedHashes(
                     self.owner, self.safe_tx_hash
                 ).call(block_identifier=block_identifier)
                 == 1
             )
         except BadFunctionCallOutput as e:  # Error using `pending` block identifier
             exception = e
     raise exception  # This should never happen
Beispiel #25
0
    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'])
Beispiel #26
0
    def test_safe_create2_tx_builder_v_1_0_0(self):
        w3 = self.w3
        tx_hash = get_safe_V1_0_0_contract(self.w3).constructor().transact(
            {'from': self.ethereum_test_account.address})
        tx_receipt = w3.eth.wait_for_transaction_receipt(tx_hash)
        master_copy = tx_receipt['contractAddress']

        salt_nonce = generate_salt_nonce()
        funder_account = self.ethereum_test_account
        owners = [Account.create().address for _ in range(4)]
        threshold = len(owners) - 1
        gas_price = self.gas_price

        safe_creation_tx = SafeCreate2TxBuilder(
            w3=w3,
            master_copy_address=master_copy,
            proxy_factory_address=self.proxy_factory_contract_address).build(
                owners=owners,
                threshold=threshold,
                salt_nonce=salt_nonce,
                gas_price=gas_price)

        self.assertEqual(safe_creation_tx.payment,
                         safe_creation_tx.payment_ether)
        self.send_tx(
            {
                'to': safe_creation_tx.safe_address,
                'value': safe_creation_tx.payment,
            }, funder_account)

        funder_balance = self.ethereum_client.get_balance(
            funder_account.address)
        ethereum_tx_sent = self.proxy_factory.deploy_proxy_contract_with_nonce(
            funder_account, master_copy, safe_creation_tx.safe_setup_data,
            salt_nonce, safe_creation_tx.gas, safe_creation_tx.gas_price)
        tx_receipt = w3.eth.wait_for_transaction_receipt(
            ethereum_tx_sent.tx_hash)
        self.assertEqual(tx_receipt.status, 1)

        # Funder balance must be bigger after a Safe deployment, as Safe deployment is a little overpriced
        self.assertGreater(
            self.ethereum_client.get_balance(funder_account.address),
            funder_balance)
        logs = self.proxy_factory_contract.events.ProxyCreation(
        ).processReceipt(tx_receipt)
        log = logs[0]
        self.assertIsNone(tx_receipt.contractAddress)
        self.assertEqual(log['event'], 'ProxyCreation')
        proxy_address = log['args']['proxy']
        self.assertEqual(proxy_address, safe_creation_tx.safe_address)
        self.assertEqual(ethereum_tx_sent.contract_address,
                         safe_creation_tx.safe_address)

        deployed_safe_proxy_contract = get_safe_contract(w3, proxy_address)
        self.assertEqual(
            deployed_safe_proxy_contract.functions.VERSION().call(), '1.0.0')
        self.assertEqual(
            deployed_safe_proxy_contract.functions.getThreshold().call(),
            threshold)
        self.assertEqual(
            deployed_safe_proxy_contract.functions.getOwners().call(), owners)
        self.assertEqual(self.ethereum_client.get_balance(proxy_address), 0)
Beispiel #27
0
    def test_safe_create2_tx_builder_with_token_payment(self):
        w3 = self.w3

        salt_nonce = generate_salt_nonce()
        erc20_deployer = Account.create()
        funder_account = self.ethereum_test_account
        owners = [Account.create().address for _ in range(4)]
        threshold = len(owners) - 1
        gas_price = self.gas_price
        token_amount = int(1e18)
        erc20_contract = self.deploy_example_erc20(token_amount,
                                                   erc20_deployer.address)
        self.assertEqual(
            erc20_contract.functions.balanceOf(erc20_deployer.address).call(),
            token_amount)

        # Send something to the erc20 deployer
        self.send_tx(
            {
                'to': erc20_deployer.address,
                'value': w3.toWei(1, 'ether')
            }, funder_account)

        safe_creation_tx = SafeCreate2TxBuilder(
            w3=w3,
            master_copy_address=self.safe_contract_address,
            proxy_factory_address=self.proxy_factory_contract_address).build(
                owners=owners,
                threshold=threshold,
                salt_nonce=salt_nonce,
                gas_price=gas_price,
                payment_token=erc20_contract.address)

        self.assertEqual(safe_creation_tx.payment_token,
                         erc20_contract.address)
        self.assertGreater(safe_creation_tx.payment, 0)
        self.assertEqual(safe_creation_tx.payment_ether,
                         safe_creation_tx.gas * safe_creation_tx.gas_price)

        self.send_tx(
            erc20_contract.functions.transfer(
                safe_creation_tx.safe_address,
                safe_creation_tx.payment).buildTransaction(
                    {'from': erc20_deployer.address}), erc20_deployer)
        self.assertEqual(
            erc20_contract.functions.balanceOf(
                safe_creation_tx.safe_address).call(),
            safe_creation_tx.payment)

        ethereum_tx_sent = self.proxy_factory.deploy_proxy_contract_with_nonce(
            funder_account,
            self.safe_contract_address,
            safe_creation_tx.safe_setup_data,
            salt_nonce,
            gas=safe_creation_tx.gas,
            gas_price=safe_creation_tx.gas_price)
        tx_receipt = w3.eth.wait_for_transaction_receipt(
            ethereum_tx_sent.tx_hash)
        self.assertEqual(tx_receipt.status, 1)
        logs = self.proxy_factory_contract.events.ProxyCreation(
        ).processReceipt(tx_receipt)
        log = logs[0]
        self.assertIsNone(tx_receipt.contractAddress)
        self.assertEqual(log['event'], 'ProxyCreation')
        proxy_address = log['args']['proxy']
        self.assertEqual(proxy_address, safe_creation_tx.safe_address)
        self.assertEqual(ethereum_tx_sent.contract_address,
                         safe_creation_tx.safe_address)

        deployed_safe_proxy_contract = get_safe_contract(w3, proxy_address)
        self.assertEqual(
            deployed_safe_proxy_contract.functions.getThreshold().call(),
            threshold)
        self.assertEqual(
            deployed_safe_proxy_contract.functions.getOwners().call(), owners)
        self.assertEqual(self.ethereum_client.get_balance(proxy_address), 0)
Beispiel #28
0
    def test_send_multisig_tx(self):
        # Create Safe
        w3 = self.w3
        funder_account = self.ethereum_test_account
        funder = funder_account.address
        owners_with_keys = [
            get_eth_address_with_key(),
            get_eth_address_with_key()
        ]
        # Signatures must be sorted!
        owners_with_keys.sort(key=lambda x: x[0].lower())
        owners = [x[0] for x in owners_with_keys]
        keys = [x[1] for x in owners_with_keys]
        threshold = len(owners_with_keys)

        safe = self.deploy_test_safe(threshold=threshold, owners=owners)
        my_safe_address = safe.address

        # The balance we will send to the safe
        safe_balance = w3.toWei(0.02, "ether")

        # Send something to the owner[0], who will be sending the tx
        owner0_balance = safe_balance
        self.send_tx({
            "to": owners[0],
            "value": owner0_balance
        }, funder_account)

        my_safe_contract = get_safe_contract(w3, my_safe_address)
        safe = Safe(my_safe_address, self.ethereum_client)

        to = funder
        value = safe_balance // 2
        data = HexBytes("")
        operation = 0
        safe_tx_gas = 100000
        base_gas = 300000
        gas_price = 1
        gas_token = NULL_ADDRESS
        refund_receiver = NULL_ADDRESS
        nonce = None
        safe_multisig_tx = safe.build_multisig_tx(
            to=to,
            value=value,
            data=data,
            operation=operation,
            safe_tx_gas=safe_tx_gas,
            base_gas=base_gas,
            gas_price=gas_price,
            gas_token=gas_token,
            refund_receiver=refund_receiver,
            safe_nonce=nonce,
        )
        safe_multisig_tx_hash = safe_multisig_tx.safe_tx_hash

        nonce = safe.retrieve_nonce()
        self.assertEqual(
            safe.build_multisig_tx(
                to=to,
                value=value,
                data=data,
                operation=operation,
                safe_tx_gas=safe_tx_gas,
                base_gas=base_gas,
                gas_price=gas_price,
                gas_token=gas_token,
                refund_receiver=refund_receiver,
                safe_nonce=nonce,
            ).safe_tx_hash,
            safe_multisig_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,
            base_gas,
            gas_price,
            gas_token,
            refund_receiver,
            nonce,
        ).call()

        self.assertEqual(safe_multisig_tx_hash, contract_multisig_tx_hash)

        for private_key in keys:
            safe_multisig_tx.sign(private_key)

        signatures = safe_multisig_tx.signatures
        self.assertEqual(set(safe_multisig_tx.signers), set(owners))

        # Check owners are the same
        contract_owners = my_safe_contract.functions.getOwners().call()
        self.assertEqual(set(contract_owners), set(owners))
        self.assertEqual(w3.eth.get_balance(owners[0]), owner0_balance)

        # with self.assertRaises(CouldNotPayGasWithEther):
        # Ganache v7 does not raise CouldNotPayGasWithEther anymore
        with self.assertRaises(InvalidInternalTx):
            safe.send_multisig_tx(
                to,
                value,
                data,
                operation,
                safe_tx_gas,
                base_gas,
                gas_price,
                gas_token,
                refund_receiver,
                signatures,
                tx_sender_private_key=keys[0],
                tx_gas_price=self.gas_price,
            )

        # Send something to the safe
        self.send_tx({
            "to": my_safe_address,
            "value": safe_balance
        }, funder_account)

        ethereum_tx_sent = safe.send_multisig_tx(
            to,
            value,
            data,
            operation,
            safe_tx_gas,
            base_gas,
            gas_price,
            gas_token,
            refund_receiver,
            signatures,
            tx_sender_private_key=keys[0],
            tx_gas_price=self.gas_price,
        )

        tx_receipt = w3.eth.wait_for_transaction_receipt(
            ethereum_tx_sent.tx_hash)
        self.assertTrue(tx_receipt["status"])
        owner0_new_balance = w3.eth.get_balance(owners[0])
        gas_used = tx_receipt["gasUsed"]
        gas_cost = gas_used * self.gas_price
        estimated_payment = (base_gas + gas_used) * gas_price
        real_payment = owner0_new_balance - (owner0_balance - gas_cost)
        # Estimated payment will be bigger, because it uses all the tx gas. Real payment only uses gas left
        # in the point of calculation of the payment, so it will be slightly lower
        self.assertTrue(estimated_payment > real_payment > 0)
        self.assertTrue(owner0_new_balance > owner0_balance -
                        ethereum_tx_sent.tx["gas"] * self.gas_price)
        self.assertEqual(safe.retrieve_nonce(), 1)
Beispiel #29
0
 def get_contract(self):
     return get_safe_contract(self.w3, address=self.address)
    def test_safe_creation_tx_builder_with_token_payment(self):
        logger.info("Test Safe Proxy creation With Gas Payment".center(LOG_TITLE_WIDTH, '-'))
        w3 = self.w3

        s = generate_valid_s()

        erc20_deployer = Account.create()
        funder_account = self.ethereum_test_account

        # Send something to the erc20 deployer
        self.send_tx({
            'to': erc20_deployer.address,
            'value': w3.toWei(1, 'ether')
        }, funder_account)

        funder = funder_account.address
        owners = [get_eth_address_with_key()[0] for _ in range(2)]
        threshold = len(owners) - 1
        gas_price = self.gas_price
        token_amount = int(1e18)
        erc20_contract = self.deploy_example_erc20(token_amount, erc20_deployer.address)
        self.assertEqual(erc20_contract.functions.balanceOf(erc20_deployer.address).call(), token_amount)

        safe_creation_tx = SafeCreationTx(w3=w3,
                                          owners=owners,
                                          threshold=threshold,
                                          signature_s=s,
                                          master_copy=self.safe_contract_V0_0_1_address,
                                          gas_price=gas_price,
                                          payment_token=erc20_contract.address,
                                          funder=funder)

        # In this test we will pretend that ether value = token value, so we send tokens as ether payment
        payment = safe_creation_tx.payment
        deployer_address = safe_creation_tx.deployer_address
        safe_address = safe_creation_tx.safe_address
        logger.info("Send %d tokens to safe %s", payment, safe_address)
        self.send_tx(erc20_contract.functions.transfer(safe_address,
                                                       payment).buildTransaction({'from': erc20_deployer.address}),
                     erc20_deployer)
        self.assertEqual(erc20_contract.functions.balanceOf(safe_address).call(), payment)

        logger.info("Send %d ether to deployer %s", w3.fromWei(payment, 'ether'), deployer_address)
        self.send_tx({
            'to': safe_creation_tx.deployer_address,
            'value': safe_creation_tx.payment
        }, funder_account)

        logger.info("Create proxy contract with address %s", safe_creation_tx.safe_address)
        funder_balance = w3.eth.getBalance(funder)

        # This tx will create the Safe Proxy and return tokens to the funder
        tx_hash = w3.eth.sendRawTransaction(safe_creation_tx.tx_raw)
        tx_receipt = w3.eth.waitForTransactionReceipt(tx_hash)
        self.assertEqual(tx_receipt.contractAddress, safe_address)
        self.assertEqual(w3.eth.getBalance(funder), funder_balance)
        self.assertEqual(erc20_contract.functions.balanceOf(funder).call(), payment)
        self.assertEqual(erc20_contract.functions.balanceOf(safe_address).call(), 0)

        logger.info("Deployer account has still %d gwei left (will be lost)",
                    w3.fromWei(w3.eth.getBalance(safe_creation_tx.deployer_address), 'gwei'))

        deployed_safe_proxy_contract = get_safe_contract(w3, tx_receipt.contractAddress)

        self.assertEqual(deployed_safe_proxy_contract.functions.getThreshold().call(), threshold)
        self.assertEqual(deployed_safe_proxy_contract.functions.getOwners().call(), owners)

        # Payment should be <= when payment_token_eth_value is 1.0
        # Funder will already have tokens so no storage need to be paid)
        safe_creation_tx_2 = SafeCreationTx(w3=w3,
                                            owners=owners,
                                            threshold=threshold,
                                            signature_s=s,
                                            master_copy=self.safe_contract_V0_0_1_address,
                                            gas_price=gas_price,
                                            payment_token=erc20_contract.address,
                                            payment_token_eth_value=1.0,
                                            funder=funder)
        self.assertLessEqual(safe_creation_tx_2.payment, safe_creation_tx.payment)

        # Now payment should be equal when payment_token_eth_value is 1.0
        safe_creation_tx_3 = SafeCreationTx(w3=w3,
                                            owners=owners,
                                            threshold=threshold,
                                            signature_s=s,
                                            master_copy=self.safe_contract_V0_0_1_address,
                                            gas_price=gas_price,
                                            payment_token=erc20_contract.address,
                                            payment_token_eth_value=1.0,
                                            funder=funder)
        self.assertEqual(safe_creation_tx_3.payment, safe_creation_tx_2.payment)

        # Check that payment is less when payment_token_eth_value is set(token value > ether)
        safe_creation_tx_4 = SafeCreationTx(w3=w3,
                                            owners=owners,
                                            threshold=threshold,
                                            signature_s=s,
                                            master_copy=self.safe_contract_V0_0_1_address,
                                            gas_price=gas_price,
                                            payment_token=erc20_contract.address,
                                            payment_token_eth_value=1.1,
                                            funder=funder)
        self.assertLess(safe_creation_tx_4.payment, safe_creation_tx.payment)

        # Check that payment is more when payment_token_eth_value is set(token value < ether)
        safe_creation_tx_5 = SafeCreationTx(w3=w3,
                                            owners=owners,
                                            threshold=threshold,
                                            signature_s=s,
                                            master_copy=self.safe_contract_V0_0_1_address,
                                            gas_price=gas_price,
                                            payment_token=erc20_contract.address,
                                            payment_token_eth_value=0.1,
                                            funder=funder)
        self.assertGreater(safe_creation_tx_5.payment, safe_creation_tx.payment)