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)
def setUpClass(cls): super().setUpClass() 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_DEFAULT_CALLBACK_HANDLER = _contract_addresses[ "compatibility_fallback_handler"] settings.SAFE_MULTISEND_ADDRESS = _contract_addresses["multi_send"] settings.SAFE_CONTRACT_ADDRESS = _contract_addresses["safe_v1_3_0"] settings.SAFE_V1_1_1_CONTRACT_ADDRESS = _contract_addresses[ "safe_V1_1_1"] 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_1_1_CONTRACT_ADDRESS, settings.SAFE_V1_0_0_CONTRACT_ADDRESS, settings.SAFE_V0_0_1_CONTRACT_ADDRESS, } cls.compatibility_fallback_handler = ( get_compatibility_fallback_handler_V1_3_0_contract( cls.w3, _contract_addresses["compatibility_fallback_handler"])) cls.safe_contract_address = _contract_addresses["safe_v1_3_0"] cls.safe_contract = get_safe_V1_3_0_contract(cls.w3, cls.safe_contract_address) cls.safe_contract_V1_1_1_address = _contract_addresses["safe_V1_1_1"] cls.safe_contract_V1_1_1 = get_safe_V1_1_1_contract( cls.w3, cls.safe_contract_V1_1_1_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)
def retrieve_modules(self, pagination: Optional[int] = 10, block_identifier: Optional[BlockIdentifier] = 'latest') -> List[str]: """ :param pagination: Number of modules to get per request :param block_identifier: :return: List of module addresses """ try: # Contracts with Safe version < 1.1.0 were not paginated contract = get_safe_V1_0_0_contract(self.ethereum_client.w3, address=self.address) return contract.functions.getModules().call(block_identifier=block_identifier) except BadFunctionCallOutput: pass contract = self.get_contract() address = SENTINEL_ADDRESS all_modules: List[str] = [] while True: (modules, address) = contract.functions.getModulesPaginated(address, pagination).call(block_identifier=block_identifier) all_modules.extend(modules) if address == SENTINEL_ADDRESS: break else: all_modules.append(address) return all_modules
def deploy_master_contract_v1_0_0(ethereum_client: EthereumClient, deployer_account: LocalAccount) -> EthereumTxSent: """ Deploy master contract. Takes deployer_account (if unlocked in the node) or the deployer private key :param ethereum_client: :param deployer_account: Ethereum account :return: deployed contract address """ safe_contract = get_safe_V1_0_0_contract(ethereum_client.w3) constructor_data = safe_contract.constructor().buildTransaction({'gas': 0})['data'] initializer_data = safe_contract.functions.setup( # We use 2 owners that nobody controls for the master copy ["0x0000000000000000000000000000000000000002", "0x0000000000000000000000000000000000000003"], 2, # Threshold. Maximum security NULL_ADDRESS, # Address for optional DELEGATE CALL b'', # Data for optional DELEGATE CALL NULL_ADDRESS, # Payment token 0, # Payment NULL_ADDRESS # Refund receiver ).buildTransaction({'to': NULL_ADDRESS})['data'] ethereum_tx_sent = ethereum_client.deploy_and_initialize_contract(deployer_account, constructor_data, HexBytes(initializer_data)) logger.info("Deployed and initialized Safe Master Contract=%s by %s", ethereum_tx_sent.contract_address, deployer_account.address) return ethereum_tx_sent
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 __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_V1_3_0_contract(dummy_w3).events.ExecutionFailure(), ] self.safe_tx_module_failure_events = [ get_safe_V1_3_0_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_last_status_cache: Dict[str, SafeStatus] = {} self.signature_breaking_versions = ( # Versions where signing changed Version("1.0.0"), # Safes >= 1.0.0 Renamed `baseGas` to `dataGas` Version("1.3.0"), # ChainId was included )
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))
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 __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 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
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))
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] = {}
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)