diff --git a/gnosis/eth/contracts/__init__.py b/gnosis/eth/contracts/__init__.py index 2bc3da4aa..7a9b9c53c 100644 --- a/gnosis/eth/contracts/__init__.py +++ b/gnosis/eth/contracts/__init__.py @@ -1,3 +1,4 @@ +# flake8: noqa F401 """ Safe Addresses. Should be the same for every chain except for the ones with `chainId` protection. Check: https://github.com/safe-global/safe-deployments/tree/main/src/assets @@ -32,13 +33,16 @@ from gnosis.util import cache +from .abis.multicall import multicall_v3_abi, multicall_v3_bytecode +from .contract_base import ContractBase + def load_contract_interface(file_name): return _load_json_file(_abi_file_path(file_name)) def _abi_file_path(file): - return os.path.abspath(os.path.join(os.path.dirname(__file__), file)) + return os.path.abspath(os.path.join(os.path.dirname(__file__), "abis", file)) def _load_json_file(path): @@ -91,7 +95,7 @@ def fn(w3: Web3, address: Optional[ChecksumAddress] = None): # Anotate functions that will be generated later with `setattr` so typing does not complains -def get_safe_contract(w3: Web3, address: Optional[str] = None) -> Contract: +def get_safe_contract(w3: Web3, address: Optional[ChecksumAddress] = None) -> Contract: """ :param w3: :param address: @@ -100,116 +104,154 @@ def get_safe_contract(w3: Web3, address: Optional[str] = None) -> Contract: return get_safe_V1_3_0_contract(w3, address=address) -def get_safe_V1_3_0_contract(w3: Web3, address: Optional[str] = None) -> Contract: +def get_safe_V1_3_0_contract( + w3: Web3, address: Optional[ChecksumAddress] = None +) -> Contract: pass -def get_safe_V1_1_1_contract(w3: Web3, address: Optional[str] = None) -> Contract: +def get_safe_V1_1_1_contract( + w3: Web3, address: Optional[ChecksumAddress] = None +) -> Contract: pass -def get_safe_V1_0_0_contract(w3: Web3, address: Optional[str] = None) -> Contract: +def get_safe_V1_0_0_contract( + w3: Web3, address: Optional[ChecksumAddress] = None +) -> Contract: pass -def get_safe_V0_0_1_contract(w3: Web3, address: Optional[str] = None) -> Contract: +def get_safe_V0_0_1_contract( + w3: Web3, address: Optional[ChecksumAddress] = None +) -> Contract: pass def get_compatibility_fallback_handler_V1_3_0_contract( - w3: Web3, address: Optional[str] = None + w3: Web3, address: Optional[ChecksumAddress] = None ) -> Contract: pass -def get_erc20_contract(w3: Web3, address: Optional[str] = None) -> Contract: +def get_erc20_contract(w3: Web3, address: Optional[ChecksumAddress] = None) -> Contract: pass -def get_erc721_contract(w3: Web3, address: Optional[str] = None) -> Contract: +def get_erc721_contract( + w3: Web3, address: Optional[ChecksumAddress] = None +) -> Contract: pass -def get_erc1155_contract(w3: Web3, address: Optional[str] = None) -> Contract: +def get_erc1155_contract( + w3: Web3, address: Optional[ChecksumAddress] = None +) -> Contract: pass -def get_example_erc20_contract(w3: Web3, address: Optional[str] = None) -> Contract: +def get_example_erc20_contract( + w3: Web3, address: Optional[ChecksumAddress] = None +) -> Contract: pass def get_delegate_constructor_proxy_contract( - w3: Web3, address: Optional[str] = None + w3: Web3, address: Optional[ChecksumAddress] = None ) -> Contract: pass -def get_multi_send_contract(w3: Web3, address: Optional[str] = None) -> Contract: +def get_multi_send_contract( + w3: Web3, address: Optional[ChecksumAddress] = None +) -> Contract: pass -def get_paying_proxy_contract(w3: Web3, address: Optional[str] = None) -> Contract: +def get_paying_proxy_contract( + w3: Web3, address: Optional[ChecksumAddress] = None +) -> Contract: pass -def get_proxy_factory_contract(w3: Web3, address: Optional[str] = None) -> Contract: +def get_proxy_factory_contract( + w3: Web3, address: Optional[ChecksumAddress] = None +) -> Contract: return get_proxy_factory_V1_3_0_contract(w3, address) def get_proxy_factory_V1_3_0_contract( - w3: Web3, address: Optional[str] = None + w3: Web3, address: Optional[ChecksumAddress] = None ) -> Contract: pass def get_proxy_factory_V1_1_1_contract( - w3: Web3, address: Optional[str] = None + w3: Web3, address: Optional[ChecksumAddress] = None ) -> Contract: pass def get_proxy_factory_V1_0_0_contract( - w3: Web3, address: Optional[str] = None + w3: Web3, address: Optional[ChecksumAddress] = None ) -> Contract: pass -def get_proxy_contract(w3: Web3, address: Optional[str] = None) -> Contract: +def get_proxy_contract(w3: Web3, address: Optional[ChecksumAddress] = None) -> Contract: pass -def get_uniswap_exchange_contract(w3: Web3, address: Optional[str] = None) -> Contract: +def get_uniswap_exchange_contract( + w3: Web3, address: Optional[ChecksumAddress] = None +) -> Contract: pass -def get_uniswap_factory_contract(w3: Web3, address: Optional[str] = None) -> Contract: +def get_uniswap_factory_contract( + w3: Web3, address: Optional[ChecksumAddress] = None +) -> Contract: pass def get_uniswap_v2_factory_contract( - w3: Web3, address: Optional[str] = None + w3: Web3, address: Optional[ChecksumAddress] = None ) -> Contract: pass -def get_uniswap_v2_pair_contract(w3: Web3, address: Optional[str] = None) -> Contract: +def get_uniswap_v2_pair_contract( + w3: Web3, address: Optional[ChecksumAddress] = None +) -> Contract: pass -def get_uniswap_v2_router_contract(w3: Web3, address: Optional[str] = None) -> Contract: +def get_uniswap_v2_router_contract( + w3: Web3, address: Optional[ChecksumAddress] = None +) -> Contract: pass def get_kyber_network_proxy_contract( - w3: Web3, address: Optional[str] = None + w3: Web3, address: Optional[ChecksumAddress] = None ) -> Contract: pass -def get_cpk_factory_contract(w3: Web3, address: Optional[str] = None) -> Contract: +def get_cpk_factory_contract( + w3: Web3, address: Optional[ChecksumAddress] = None +) -> Contract: pass +def get_multicall_v3_contract(w3: Web3, address: Optional[ChecksumAddress] = None): + return w3.eth.contract( + address=address, + abi=multicall_v3_abi, + bytecode=multicall_v3_bytecode, + ) + + @cache def get_proxy_1_3_0_deployed_bytecode() -> bytes: return HexBytes(load_contract_interface("Proxy_V1_3_0.json")["deployedBytecode"]) diff --git a/gnosis/eth/contracts/CPKFactory.json b/gnosis/eth/contracts/abis/CPKFactory.json similarity index 100% rename from gnosis/eth/contracts/CPKFactory.json rename to gnosis/eth/contracts/abis/CPKFactory.json diff --git a/gnosis/eth/contracts/CompatibilityFallbackHandler_V1_3_0.json b/gnosis/eth/contracts/abis/CompatibilityFallbackHandler_V1_3_0.json similarity index 100% rename from gnosis/eth/contracts/CompatibilityFallbackHandler_V1_3_0.json rename to gnosis/eth/contracts/abis/CompatibilityFallbackHandler_V1_3_0.json diff --git a/gnosis/eth/contracts/DelegateConstructorProxy.json b/gnosis/eth/contracts/abis/DelegateConstructorProxy.json similarity index 100% rename from gnosis/eth/contracts/DelegateConstructorProxy.json rename to gnosis/eth/contracts/abis/DelegateConstructorProxy.json diff --git a/gnosis/eth/contracts/ERC1155.json b/gnosis/eth/contracts/abis/ERC1155.json similarity index 100% rename from gnosis/eth/contracts/ERC1155.json rename to gnosis/eth/contracts/abis/ERC1155.json diff --git a/gnosis/eth/contracts/ERC20.json b/gnosis/eth/contracts/abis/ERC20.json similarity index 100% rename from gnosis/eth/contracts/ERC20.json rename to gnosis/eth/contracts/abis/ERC20.json diff --git a/gnosis/eth/contracts/ERC20TestToken.json b/gnosis/eth/contracts/abis/ERC20TestToken.json similarity index 100% rename from gnosis/eth/contracts/ERC20TestToken.json rename to gnosis/eth/contracts/abis/ERC20TestToken.json diff --git a/gnosis/eth/contracts/ERC721.json b/gnosis/eth/contracts/abis/ERC721.json similarity index 100% rename from gnosis/eth/contracts/ERC721.json rename to gnosis/eth/contracts/abis/ERC721.json diff --git a/gnosis/eth/contracts/GnosisSafe_V0_0_1.json b/gnosis/eth/contracts/abis/GnosisSafe_V0_0_1.json similarity index 100% rename from gnosis/eth/contracts/GnosisSafe_V0_0_1.json rename to gnosis/eth/contracts/abis/GnosisSafe_V0_0_1.json diff --git a/gnosis/eth/contracts/GnosisSafe_V1_0_0.json b/gnosis/eth/contracts/abis/GnosisSafe_V1_0_0.json similarity index 100% rename from gnosis/eth/contracts/GnosisSafe_V1_0_0.json rename to gnosis/eth/contracts/abis/GnosisSafe_V1_0_0.json diff --git a/gnosis/eth/contracts/GnosisSafe_V1_1_1.json b/gnosis/eth/contracts/abis/GnosisSafe_V1_1_1.json similarity index 100% rename from gnosis/eth/contracts/GnosisSafe_V1_1_1.json rename to gnosis/eth/contracts/abis/GnosisSafe_V1_1_1.json diff --git a/gnosis/eth/contracts/GnosisSafe_V1_3_0.json b/gnosis/eth/contracts/abis/GnosisSafe_V1_3_0.json similarity index 100% rename from gnosis/eth/contracts/GnosisSafe_V1_3_0.json rename to gnosis/eth/contracts/abis/GnosisSafe_V1_3_0.json diff --git a/gnosis/eth/contracts/MultiSend.json b/gnosis/eth/contracts/abis/MultiSend.json similarity index 100% rename from gnosis/eth/contracts/MultiSend.json rename to gnosis/eth/contracts/abis/MultiSend.json diff --git a/gnosis/eth/contracts/PayingProxy.json b/gnosis/eth/contracts/abis/PayingProxy.json similarity index 100% rename from gnosis/eth/contracts/PayingProxy.json rename to gnosis/eth/contracts/abis/PayingProxy.json diff --git a/gnosis/eth/contracts/ProxyFactory_V1_0_0.json b/gnosis/eth/contracts/abis/ProxyFactory_V1_0_0.json similarity index 100% rename from gnosis/eth/contracts/ProxyFactory_V1_0_0.json rename to gnosis/eth/contracts/abis/ProxyFactory_V1_0_0.json diff --git a/gnosis/eth/contracts/ProxyFactory_V1_1_1.json b/gnosis/eth/contracts/abis/ProxyFactory_V1_1_1.json similarity index 100% rename from gnosis/eth/contracts/ProxyFactory_V1_1_1.json rename to gnosis/eth/contracts/abis/ProxyFactory_V1_1_1.json diff --git a/gnosis/eth/contracts/ProxyFactory_V1_3_0.json b/gnosis/eth/contracts/abis/ProxyFactory_V1_3_0.json similarity index 100% rename from gnosis/eth/contracts/ProxyFactory_V1_3_0.json rename to gnosis/eth/contracts/abis/ProxyFactory_V1_3_0.json diff --git a/gnosis/eth/contracts/Proxy_V1_0_0.json b/gnosis/eth/contracts/abis/Proxy_V1_0_0.json similarity index 100% rename from gnosis/eth/contracts/Proxy_V1_0_0.json rename to gnosis/eth/contracts/abis/Proxy_V1_0_0.json diff --git a/gnosis/eth/contracts/Proxy_V1_1_1.json b/gnosis/eth/contracts/abis/Proxy_V1_1_1.json similarity index 100% rename from gnosis/eth/contracts/Proxy_V1_1_1.json rename to gnosis/eth/contracts/abis/Proxy_V1_1_1.json diff --git a/gnosis/eth/contracts/Proxy_V1_3_0.json b/gnosis/eth/contracts/abis/Proxy_V1_3_0.json similarity index 100% rename from gnosis/eth/contracts/Proxy_V1_3_0.json rename to gnosis/eth/contracts/abis/Proxy_V1_3_0.json diff --git a/gnosis/eth/abis/__init__.py b/gnosis/eth/contracts/abis/__init__.py similarity index 100% rename from gnosis/eth/abis/__init__.py rename to gnosis/eth/contracts/abis/__init__.py diff --git a/gnosis/eth/contracts/kyber_network_proxy.json b/gnosis/eth/contracts/abis/kyber_network_proxy.json similarity index 100% rename from gnosis/eth/contracts/kyber_network_proxy.json rename to gnosis/eth/contracts/abis/kyber_network_proxy.json diff --git a/gnosis/eth/abis/multicall.py b/gnosis/eth/contracts/abis/multicall.py similarity index 100% rename from gnosis/eth/abis/multicall.py rename to gnosis/eth/contracts/abis/multicall.py diff --git a/gnosis/eth/contracts/uniswap_exchange.json b/gnosis/eth/contracts/abis/uniswap_exchange.json similarity index 100% rename from gnosis/eth/contracts/uniswap_exchange.json rename to gnosis/eth/contracts/abis/uniswap_exchange.json diff --git a/gnosis/eth/contracts/uniswap_factory.json b/gnosis/eth/contracts/abis/uniswap_factory.json similarity index 100% rename from gnosis/eth/contracts/uniswap_factory.json rename to gnosis/eth/contracts/abis/uniswap_factory.json diff --git a/gnosis/eth/contracts/uniswap_v2_factory.json b/gnosis/eth/contracts/abis/uniswap_v2_factory.json similarity index 100% rename from gnosis/eth/contracts/uniswap_v2_factory.json rename to gnosis/eth/contracts/abis/uniswap_v2_factory.json diff --git a/gnosis/eth/contracts/uniswap_v2_pair.json b/gnosis/eth/contracts/abis/uniswap_v2_pair.json similarity index 100% rename from gnosis/eth/contracts/uniswap_v2_pair.json rename to gnosis/eth/contracts/abis/uniswap_v2_pair.json diff --git a/gnosis/eth/contracts/uniswap_v2_router.json b/gnosis/eth/contracts/abis/uniswap_v2_router.json similarity index 100% rename from gnosis/eth/contracts/uniswap_v2_router.json rename to gnosis/eth/contracts/abis/uniswap_v2_router.json diff --git a/gnosis/eth/contracts/contract_base.py b/gnosis/eth/contracts/contract_base.py new file mode 100644 index 000000000..02b087e8c --- /dev/null +++ b/gnosis/eth/contracts/contract_base.py @@ -0,0 +1,30 @@ +from abc import ABCMeta, abstractmethod +from functools import cached_property +from logging import getLogger +from typing import Callable, Optional + +from eth_typing import ChecksumAddress +from web3 import Web3 +from web3.contract import Contract + +from .. import EthereumClient + +logger = getLogger(__name__) + + +class ContractBase(metaclass=ABCMeta): + def __init__(self, address: ChecksumAddress, ethereum_client: EthereumClient): + self.address = address + self.ethereum_client = ethereum_client + self.w3 = ethereum_client.w3 + + @abstractmethod + def get_contract_fn(self) -> Callable[[Web3, Optional[ChecksumAddress]], Contract]: + """ + :return: Contract function to get the proper contract + """ + raise NotImplementedError + + @cached_property + def contract(self) -> Contract: + return self.get_contract_fn()(self.ethereum_client.w3, self.address) diff --git a/gnosis/eth/contracts/contract_common.py b/gnosis/eth/contracts/contract_common.py deleted file mode 100644 index 81ee12af3..000000000 --- a/gnosis/eth/contracts/contract_common.py +++ /dev/null @@ -1,28 +0,0 @@ -from logging import getLogger -from typing import Optional - -from web3.types import TxParams - -logger = getLogger(__name__) - - -class ContractCommon: - def configure_tx_parameters( - self, - from_address: str, - gas: Optional[int] = None, - gas_price: Optional[int] = None, - nonce: Optional[int] = None, - ) -> TxParams: - tx_parameters = {"from": from_address} - - if gas_price is not None: - tx_parameters["gasPrice"] = gas_price - - if gas is not None: - tx_parameters["gas"] = gas - - if nonce is not None: - tx_parameters["nonce"] = nonce - - return tx_parameters diff --git a/gnosis/eth/ethereum_client.py b/gnosis/eth/ethereum_client.py index 2dc244fdb..4e65714f6 100644 --- a/gnosis/eth/ethereum_client.py +++ b/gnosis/eth/ethereum_client.py @@ -1411,7 +1411,7 @@ def deploy_and_initialize_contract( constructor_data: bytes, initializer_data: bytes = b"", check_receipt: bool = True, - ): + ) -> EthereumTxSent: contract_address: Optional[ChecksumAddress] = None for data in (constructor_data, initializer_data): # Because initializer_data is not mandatory @@ -1505,6 +1505,12 @@ def estimate_gas( @staticmethod def estimate_data_gas(data: bytes): + """ + Estimate gas costs only for "storage" of the ``data`` bytes provided + + :param data: + :return: + """ if isinstance(data, str): data = HexBytes(data) @@ -1698,6 +1704,57 @@ def get_blocks( def is_contract(self, contract_address: ChecksumAddress) -> bool: return bool(self.w3.eth.get_code(contract_address)) + @staticmethod + def build_tx_params( + from_address: Optional[ChecksumAddress] = None, + to_address: Optional[ChecksumAddress] = None, + value: Optional[int] = None, + gas: Optional[int] = None, + gas_price: Optional[int] = None, + nonce: Optional[int] = None, + chain_id: Optional[int] = None, + tx_params: Optional[TxParams] = None, + ) -> TxParams: + """ + Build tx params dictionary. + If an existing TxParams dictionary is provided the fields will be replaced by the provided ones + + :param from_address: + :param to_address: + :param value: + :param gas: + :param gas_price: + :param nonce: + :param chain_id: + :param tx_params: An existing TxParams dictionary will be replaced by the providen values + :return: + """ + + tx_params: TxParams = tx_params or {} + + if from_address: + tx_params["from"] = from_address + + if to_address: + tx_params["to"] = to_address + + if value is not None: + tx_params["value"] = value + + if gas_price is not None: + tx_params["gasPrice"] = gas_price + + if gas is not None: + tx_params["gas"] = gas + + if nonce is not None: + tx_params["nonce"] = nonce + + if chain_id is not None: + tx_params["chainId"] = chain_id + + return tx_params + @tx_with_exception_handling def send_transaction(self, transaction_dict: TxParams) -> HexBytes: return self.w3.eth.send_transaction(transaction_dict) diff --git a/gnosis/eth/multicall.py b/gnosis/eth/multicall.py index 4fd05bada..53554c3b2 100644 --- a/gnosis/eth/multicall.py +++ b/gnosis/eth/multicall.py @@ -4,7 +4,7 @@ """ import logging from dataclasses import dataclass -from typing import Any, List, Optional, Sequence, Tuple +from typing import Any, Callable, List, Optional, Sequence, Tuple import eth_abi from eth_abi.exceptions import DecodingError @@ -14,11 +14,11 @@ from web3 import Web3 from web3._utils.abi import map_abi_data from web3._utils.normalizers import BASE_RETURN_NORMALIZERS -from web3.contract.contract import ContractFunction +from web3.contract.contract import Contract, ContractFunction from web3.exceptions import ContractLogicError from . import EthereumClient, EthereumNetwork, EthereumNetworkNotSupported -from .abis.multicall import multicall_v3_abi, multicall_v3_bytecode +from .contracts import ContractBase, get_multicall_v3_contract from .ethereum_client import EthereumTxSent from .exceptions import BatchCallFunctionFailed @@ -37,7 +37,7 @@ class MulticallDecodedResult: return_data_decoded: Optional[Any] -class Multicall: +class Multicall(ContractBase): # https://github.com/mds1/multicall#deployments ADDRESSES = { EthereumNetwork.MAINNET: "0xcA11bde05977b3631167028862bE2a173976CA11", @@ -75,8 +75,6 @@ def __init__( ethereum_client: EthereumClient, multicall_contract_address: Optional[ChecksumAddress] = None, ): - self.ethereum_client = ethereum_client - self.w3 = ethereum_client.w3 ethereum_network = ethereum_client.get_network() address = multicall_contract_address or self.ADDRESSES.get(ethereum_network) if not address: @@ -86,12 +84,10 @@ def __init__( raise EthereumNetworkNotSupported( "Multicall contract not available for %s", ethereum_network.name ) - self.contract = self.get_contract(self.w3, address) + super().__init__(address, ethereum_client) - def get_contract(self, w3: Web3, address: Optional[ChecksumAddress] = None): - return w3.eth.contract( - address, abi=multicall_v3_abi, bytecode=multicall_v3_bytecode - ) + def get_contract_fn(self) -> Callable[[Web3, Optional[ChecksumAddress]], Contract]: + return get_multicall_v3_contract @classmethod def deploy_contract( @@ -104,17 +100,17 @@ def deploy_contract( :param deployer_account: Ethereum Account :return: deployed contract address """ - contract = cls.get_contract(cls, ethereum_client.w3) - tx = contract.constructor().build_transaction( - {"from": deployer_account.address} + contract_fn = cls.get_contract_fn(cls) + contract = contract_fn(ethereum_client.w3) + constructor_data = contract.constructor().build_transaction( + {"gas": 0, "gasPrice": 0} + )["data"] + + ethereum_tx_sent = ethereum_client.deploy_and_initialize_contract( + deployer_account, constructor_data ) - tx_hash = ethereum_client.send_unsigned_transaction( - tx, private_key=deployer_account.key - ) - tx_receipt = ethereum_client.get_transaction_receipt(tx_hash, timeout=120) - assert tx_receipt and tx_receipt["status"] - contract_address = tx_receipt["contractAddress"] + contract_address = ethereum_tx_sent.contract_address logger.info( "Deployed Multicall V2 Contract %s by %s", contract_address, @@ -122,7 +118,7 @@ def deploy_contract( ) # Add address to addresses dictionary cls.ADDRESSES[ethereum_client.get_network()] = contract_address - return EthereumTxSent(tx_hash, tx, contract_address) + return ethereum_tx_sent @staticmethod def _build_payload( diff --git a/gnosis/safe/proxy_factory.py b/gnosis/safe/proxy_factory.py index 02de4708e..b4d9bb488 100644 --- a/gnosis/safe/proxy_factory.py +++ b/gnosis/safe/proxy_factory.py @@ -1,5 +1,4 @@ -from abc import ABCMeta, abstractmethod -from functools import cached_property +from abc import ABCMeta from typing import Callable, Optional from eth_account.signers.local import LocalAccount @@ -9,6 +8,7 @@ from gnosis.eth import EthereumClient, EthereumTxSent from gnosis.eth.contracts import ( + ContractBase, get_paying_proxy_deployed_bytecode, get_proxy_1_0_0_deployed_bytecode, get_proxy_1_1_1_deployed_bytecode, @@ -18,30 +18,30 @@ get_proxy_factory_V1_1_1_contract, get_proxy_factory_V1_3_0_contract, ) -from gnosis.eth.contracts.contract_common import ContractCommon -from gnosis.eth.utils import compare_byte_code, fast_is_checksum_address +from gnosis.eth.utils import compare_byte_code from gnosis.util import cache -class ProxyFactoryBase(ContractCommon, metaclass=ABCMeta): - def __init__(self, address: ChecksumAddress, ethereum_client: EthereumClient): - assert fast_is_checksum_address(address), ( - "%s proxy factory address not valid" % address - ) - self.ethereum_client = ethereum_client - self.w3 = ethereum_client.w3 - self.address = address - - @abstractmethod - def get_contract_fn(self) -> Callable[[Web3, ChecksumAddress], Contract]: - """ - :return: Contract function to get the proper ProxyFactory contract +class ProxyFactoryBase(ContractBase, metaclass=ABCMeta): + @classmethod + def deploy_contract( + cls, ethereum_client: EthereumClient, deployer_account: LocalAccount + ) -> EthereumTxSent: """ - raise NotImplementedError + Deploy Proxy Factory contract - @cached_property - def contract(self): - return self.get_contract_fn()(self.ethereum_client.w3, self.address) + :param ethereum_client: + :param deployer_account: Ethereum Account + :return: deployed contract address + """ + contract_fn = cls.get_contract_fn(cls) + contract = contract_fn(ethereum_client.w3) + constructor_data = contract.constructor().build_transaction( + {"gas": 0, "gasPrice": 0} + )["data"] + return ethereum_client.deploy_and_initialize_contract( + deployer_account, constructor_data + ) def check_proxy_code(self, address: ChecksumAddress) -> bool: """ @@ -84,8 +84,11 @@ def _deploy_proxy_contract( :return: EthereumTxSent """ - tx_params = self.configure_tx_parameters( - deployer_account.address, gas=gas, gas_price=gas_price, nonce=nonce + tx_params = self.ethereum_client.build_tx_params( + from_address=deployer_account.address, + gas=gas, + gas_price=gas_price, + nonce=nonce, ) contract_address = deploy_fn.call(tx_params) tx = deploy_fn.build_transaction(tx_params) @@ -151,25 +154,6 @@ def deploy_proxy_contract_with_nonce( deployer_account, create_proxy_fn, gas=gas, gas_price=gas_price, nonce=nonce ) - @classmethod - def deploy_proxy_factory_contract( - cls, ethereum_client: EthereumClient, deployer_account: LocalAccount - ) -> EthereumTxSent: - """ - Deploy Proxy Factory contract - - :param ethereum_client: - :param deployer_account: Ethereum Account - :return: deployed contract address - """ - proxy_factory_contract = cls.get_contract_fn(cls)(ethereum_client.w3) - constructor_data = proxy_factory_contract.constructor().build_transaction( - {"gas": 0, "gasPrice": 0} - )["data"] - return ethereum_client.deploy_and_initialize_contract( - deployer_account, constructor_data - ) - @cache def get_proxy_runtime_code(self): """ diff --git a/gnosis/safe/safe.py b/gnosis/safe/safe.py index 1e886edc0..870b00745 100644 --- a/gnosis/safe/safe.py +++ b/gnosis/safe/safe.py @@ -21,6 +21,7 @@ from gnosis.eth import EthereumClient, EthereumTxSent from gnosis.eth.constants import GAS_CALL_DATA_BYTE, NULL_ADDRESS, SENTINEL_ADDRESS from gnosis.eth.contracts import ( + ContractBase, get_compatibility_fallback_handler_V1_3_0_contract, get_delegate_constructor_proxy_contract, get_safe_contract, @@ -29,7 +30,6 @@ get_safe_V1_1_1_contract, get_safe_V1_3_0_contract, ) -from gnosis.eth.contracts.contract_common import ContractCommon from gnosis.eth.utils import ( fast_bytes_to_checksum_address, fast_is_checksum_address, @@ -75,7 +75,7 @@ class SafeInfo: version: str -class SafeBase(ContractCommon, metaclass=ABCMeta): +class SafeBase(ContractBase, metaclass=ABCMeta): """ Collection of methods and utilies to handle a Safe """ @@ -94,16 +94,6 @@ class SafeBase(ContractCommon, metaclass=ABCMeta): "60b3cbf8b4a223d68d641b3b6ddf9a298e7f33710cf3d3a9d1146b5a6150fbca" ) - def __init__(self, address: ChecksumAddress, ethereum_client: EthereumClient): - """ - :param address: Safe address - :param ethereum_client: Initialized ethereum client - """ - - self.ethereum_client = ethereum_client - self.w3 = self.ethereum_client.w3 - self.address = address - def __str__(self): return f"Safe={self.address}" @@ -114,17 +104,6 @@ def get_version(self) -> str: """ raise NotImplementedError - @abstractmethod - def get_contract_fn(self) -> Callable[[Web3, ChecksumAddress], Contract]: - """ - :return: Contract function to get the proper Safe contract - """ - raise NotImplementedError - - @cached_property - def contract(self) -> Contract: - return self.get_contract_fn()(self.ethereum_client.w3, self.address) - @cached_property def chain_id(self) -> int: return self.ethereum_client.get_chain_id() @@ -146,7 +125,7 @@ def domain_separator(self) -> Optional[bytes]: return None @classmethod - def deploy_master_contract( + def deploy_contract( cls, ethereum_client: EthereumClient, deployer_account: LocalAccount, @@ -850,7 +829,7 @@ def get_contract_fn(self) -> Callable[[Web3, ChecksumAddress], Contract]: return get_safe_V0_0_1_contract @staticmethod - def deploy_master_contract( + def deploy_contract( ethereum_client: EthereumClient, deployer_account: LocalAccount ) -> EthereumTxSent: """ @@ -895,7 +874,7 @@ def get_contract_fn(self) -> Contract: return get_safe_V1_0_0_contract @staticmethod - def deploy_master_contract( + def deploy_contract( ethereum_client: EthereumClient, deployer_account: LocalAccount ) -> EthereumTxSent: """ diff --git a/gnosis/safe/tests/safe_test_case.py b/gnosis/safe/tests/safe_test_case.py index cf70e1a13..3a98020ba 100644 --- a/gnosis/safe/tests/safe_test_case.py +++ b/gnosis/safe/tests/safe_test_case.py @@ -30,13 +30,13 @@ _contract_addresses = { - "safe_V0_0_1": SafeV001.deploy_master_contract, - "safe_V1_0_0": SafeV100.deploy_master_contract, - "safe_V1_1_1": SafeV111.deploy_master_contract, - "safe_v1_3_0": SafeV130.deploy_master_contract, + "safe_V0_0_1": SafeV001.deploy_contract, + "safe_V1_0_0": SafeV100.deploy_contract, + "safe_V1_1_1": SafeV111.deploy_contract, + "safe_v1_3_0": SafeV130.deploy_contract, "compatibility_fallback_handler": Safe.deploy_compatibility_fallback_handler, - "proxy_factory": ProxyFactoryV130.deploy_proxy_factory_contract, - "proxy_factory_V1_0_0": ProxyFactoryV111.deploy_proxy_factory_contract, + "proxy_factory": ProxyFactoryV130.deploy_contract, + "proxy_factory_V1_0_0": ProxyFactoryV111.deploy_contract, "multi_send": MultiSend.deploy_contract, } diff --git a/gnosis/safe/tests/test_proxy_factory/test_proxy_factory.py b/gnosis/safe/tests/test_proxy_factory/test_proxy_factory.py index 360d5048f..66073a858 100644 --- a/gnosis/safe/tests/test_proxy_factory/test_proxy_factory.py +++ b/gnosis/safe/tests/test_proxy_factory/test_proxy_factory.py @@ -55,7 +55,7 @@ def test_check_proxy_code(self): ] for version, ProxyFactoryVersion, get_proxy_deployed_bytecode_fn in versions: with self.subTest(version=version): - deployed_proxy_tx = ProxyFactoryVersion.deploy_proxy_factory_contract( + deployed_proxy_tx = ProxyFactoryVersion.deploy_contract( self.ethereum_client, self.ethereum_test_account ) proxy_factory = ProxyFactory(