Esempio n. 1
0
    def update_version(self) -> Optional[bool]:
        """
        Update Safe Master Copy and Fallback handler to the last version
        :return:
        """
        if self.is_version_updated():
            raise SafeAlreadyUpdatedException()

        multisend = MultiSend(LAST_MULTISEND_CONTRACT, self.ethereum_client)
        tx_params = {'from': self.address, 'gas': 0, 'gasPrice': 0}
        multisend_txs = [
            MultiSendTx(MultiSendOperation.CALL, self.address, 0, data)
            for data in (self.safe_contract.functions.changeMasterCopy(
                LAST_SAFE_CONTRACT).buildTransaction(tx_params)['data'],
                         self.safe_contract.functions.setFallbackHandler(
                             LAST_DEFAULT_CALLBACK_HANDLER).buildTransaction(
                                 tx_params)['data'])
        ]

        multisend_data = multisend.build_tx_data(multisend_txs)

        if self.execute_safe_transaction(
                multisend.address,
                0,
                multisend_data,
                operation=SafeOperation.DELEGATE_CALL):
            self.safe_cli_info.master_copy = LAST_SAFE_CONTRACT
            self.safe_cli_info.fallback_handler = LAST_DEFAULT_CALLBACK_HANDLER
            self.safe_cli_info.version = self.safe.retrieve_version()
    def batch_txs(self, safe_nonce: int,
                  safe_tx_hashes: Sequence[bytes]) -> bool:
        """
        Submit signatures to the tx service. It's recommended to be on Safe v1.3.0 to prevent issues
        with `safeTxGas` and gas estimation.

        :return:
        """

        if not self.ethereum_client.is_contract(
                LAST_MULTISEND_CALL_ONLY_CONTRACT):
            print_formatted_text(
                HTML(
                    f"<ansired>Multisend call only contract {LAST_MULTISEND_CALL_ONLY_CONTRACT} "
                    f"is not deployed on this network and it's required for batching txs</ansired>"
                ))

        multisend_txs = []
        for safe_tx_hash in safe_tx_hashes:
            safe_tx, _ = self.safe_tx_service.get_safe_transaction(
                safe_tx_hash)
            # Check if call is already a Multisend call
            inner_txs = MultiSend.from_transaction_data(safe_tx.data)
            if inner_txs:
                multisend_txs.extend(inner_txs)
            else:
                multisend_txs.append(
                    MultiSendTx(MultiSendOperation.CALL, safe_tx.to,
                                safe_tx.value, safe_tx.data))

        if len(multisend_txs) > 1:
            multisend = MultiSend(LAST_MULTISEND_CALL_ONLY_CONTRACT,
                                  self.ethereum_client)
            safe_tx = SafeTx(
                self.ethereum_client,
                self.address,
                LAST_MULTISEND_CALL_ONLY_CONTRACT,
                0,
                multisend.build_tx_data(multisend_txs),
                SafeOperation.DELEGATE_CALL.value,
                0,
                0,
                0,
                None,
                None,
                safe_nonce=safe_nonce,
            )
        else:
            safe_tx.safe_tx_gas = 0
            safe_tx.base_gas = 0
            safe_tx.gas_price = 0
            safe_tx.signatures = b""
            safe_tx.safe_nonce = safe_nonce  # Resend single transaction
        safe_tx = self.sign_transaction(safe_tx)
        if not safe_tx.signatures:
            print_formatted_text(
                HTML("<ansired>At least one owner must be loaded</ansired>"))
            return False
        else:
            return self.post_transaction_to_tx_service(safe_tx)
Esempio n. 3
0
 def multisend_from_receipts(self, receipts: List[TransactionReceipt] = None, safe_nonce: int = None) -> SafeTx:
     """
     Convert multiple Brownie transaction receipts (or history) to a multisend Safe transaction.
     """
     if receipts is None:
         receipts = history.from_sender(self.address)
     
     if safe_nonce is None:
         safe_nonce = self.pending_nonce()
     
     txs = [MultiSendTx(MultiSendOperation.CALL, tx.receiver, tx.value, tx.input) for tx in receipts]
     data = MultiSend(self.multisend, self.ethereum_client).build_tx_data(txs)
     return self.build_multisig_tx(self.multisend, 0, data, SafeOperation.DELEGATE_CALL.value, safe_nonce=safe_nonce)
Esempio n. 4
0
    def update_version(self) -> Optional[bool]:
        """
        Update Safe Master Copy and Fallback handler to the last version
        :return:
        """
        if self.is_version_updated():
            raise SafeAlreadyUpdatedException()

        addresses = (LAST_SAFE_CONTRACT, LAST_DEFAULT_CALLBACK_HANDLER)
        if not all(
                self.ethereum_client.is_contract(contract)
                for contract in addresses):
            raise UpdateAddressesNotValid("Not valid addresses to update Safe",
                                          *addresses)

        multisend = MultiSend(LAST_MULTISEND_CONTRACT, self.ethereum_client)
        tx_params = {"from": self.address, "gas": 0, "gasPrice": 0}
        multisend_txs = [
            MultiSendTx(MultiSendOperation.CALL, self.address, 0, data)
            for data in (
                self.safe_contract_1_1_0.functions.changeMasterCopy(
                    LAST_SAFE_CONTRACT).buildTransaction(tx_params)["data"],
                self.safe_contract_1_1_0.functions.setFallbackHandler(
                    LAST_DEFAULT_CALLBACK_HANDLER).buildTransaction(tx_params)
                ["data"],
            )
        ]

        multisend_data = multisend.build_tx_data(multisend_txs)

        if self.prepare_and_execute_safe_transaction(
                multisend.address,
                0,
                multisend_data,
                operation=SafeOperation.DELEGATE_CALL):
            self.safe_cli_info.master_copy = LAST_SAFE_CONTRACT
            self.safe_cli_info.fallback_handler = LAST_DEFAULT_CALLBACK_HANDLER
            self.safe_cli_info.version = self.safe.retrieve_version()