Exemple #1
0
    def _deploy_master_contract(
        ethereum_client: EthereumClient,
        deployer_account: LocalAccount,
        contract_fn: Callable[[Web3, Optional[str]], Contract],
    ) -> 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
        :param contract_fn: get contract function
        :return: deployed contract address
        """
        safe_contract = contract_fn(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 version=%s on address %s by %s",
            contract_fn(
                ethereum_client.w3,
                ethereum_tx_sent.contract_address).functions.VERSION().call(),
            ethereum_tx_sent.contract_address,
            deployer_account.address,
        )
        return ethereum_tx_sent
Exemple #2
0
    def deploy_proxy_contract(
            self,
            deployer_account: LocalAccount,
            master_copy: str,
            initializer: bytes = b'',
            gas: Optional[int] = None,
            gas_price: Optional[int] = None) -> EthereumTxSent:
        """
        Deploy proxy contract via ProxyFactory using `createProxy` function
        :param deployer_account: Ethereum account
        :param master_copy: Address the proxy will point at
        :param initializer: Initializer
        :param gas: Gas
        :param gas_price: Gas Price
        :return: EthereumTxSent
        """
        proxy_factory_contract = self.get_contract()
        create_proxy_fn = proxy_factory_contract.functions.createProxy(
            master_copy, initializer)

        tx_parameters = {'from': deployer_account.address}
        contract_address = create_proxy_fn.call(tx_parameters)

        if gas_price is not None:
            tx_parameters['gasPrice'] = gas_price

        if gas is not None:
            tx_parameters['gas'] = gas

        tx = create_proxy_fn.buildTransaction(tx_parameters)
        # Auto estimation of gas does not work. We use a little more gas just in case
        tx['gas'] = tx['gas'] + 50000
        tx_hash = self.ethereum_client.send_unsigned_transaction(
            tx, private_key=deployer_account.key)
        return EthereumTxSent(tx_hash, tx, contract_address)
Exemple #3
0
    def deploy_compatibility_fallback_handler(
            cls, ethereum_client: EthereumClient,
            deployer_account: LocalAccount) -> EthereumTxSent:
        """
        Deploy Compatibility Fallback handler v1.3.0

        :param ethereum_client:
        :param deployer_account: Ethereum account
        :return: deployed contract address
        """

        contract = get_compatibility_fallback_handler_V1_3_0_contract(
            ethereum_client.w3)
        constructor_tx = 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 Compatibility Fallback Handler version=%s on address %s by %s",
            "1.3.0",
            ethereum_tx_sent.contract_address,
            deployer_account.address,
        )
        return ethereum_tx_sent
Exemple #4
0
    def deploy_contract(ethereum_client: EthereumClient,
                        deployer_account: LocalAccount) -> EthereumTxSent:
        """
        Deploy proxy factory contract

        :param ethereum_client:
        :param deployer_account: Ethereum Account
        :return: deployed contract address
        """
        contract = get_multi_send_contract(ethereum_client.w3)
        tx = contract.constructor().buildTransaction(
            {"from": deployer_account.address})

        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
        assert tx_receipt["status"]
        contract_address = tx_receipt["contractAddress"]
        logger.info(
            "Deployed and initialized Proxy Factory Contract=%s by %s",
            contract_address,
            deployer_account.address,
        )
        return EthereumTxSent(tx_hash, tx, contract_address)
Exemple #5
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
Exemple #6
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)
Exemple #7
0
    def send_multisig_tx(self,
                         to: str,
                         value: int,
                         data: bytes,
                         operation: int,
                         safe_tx_gas: int,
                         base_gas: int,
                         gas_price: int,
                         gas_token: str,
                         refund_receiver: str,
                         signatures: bytes,
                         tx_sender_private_key: str,
                         tx_gas=None,
                         tx_gas_price=None,
                         block_identifier: Optional[BlockIdentifier] = 'latest') -> EthereumTxSent:
        """
        Build and send Safe tx
        :param to:
        :param value:
        :param data:
        :param operation:
        :param safe_tx_gas:
        :param base_gas:
        :param gas_price:
        :param gas_token:
        :param refund_receiver:
        :param signatures:
        :param tx_sender_private_key:
        :param tx_gas: Gas for the external tx. If not, `(safe_tx_gas + data_gas) * 2` will be used
        :param tx_gas_price: Gas price of the external tx. If not, `gas_price` will be used
        :param block_identifier:
        :return: Tuple(tx_hash, tx)
        :raises: InvalidMultisigTx: If user tx cannot go through the Safe
        """

        safe_tx = self.build_multisig_tx(to,
                                         value,
                                         data,
                                         operation,
                                         safe_tx_gas,
                                         base_gas,
                                         gas_price,
                                         gas_token,
                                         refund_receiver,
                                         signatures)

        tx_sender_address = Account.from_key(tx_sender_private_key).address
        safe_tx.call(tx_sender_address=tx_sender_address, block_identifier=block_identifier)

        tx_hash, tx = safe_tx.execute(tx_sender_private_key=tx_sender_private_key,
                                      tx_gas=tx_gas,
                                      tx_gas_price=tx_gas_price,
                                      block_identifier=block_identifier)

        return EthereumTxSent(tx_hash, tx, None)
Exemple #8
0
    def _deploy_proxy_factory_contract(ethereum_client: EthereumClient,
                                       deployer_account: LocalAccount, contract: Contract) -> EthereumTxSent:
        tx = contract.constructor().buildTransaction({'from': deployer_account.address})

        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
        assert tx_receipt['status']
        contract_address = tx_receipt['contractAddress']
        logger.info("Deployed and initialized Proxy Factory Contract=%s by %s", contract_address,
                    deployer_account.address)
        return EthereumTxSent(tx_hash, tx, contract_address)
Exemple #9
0
    def deploy_proxy_contract_with_nonce(
        self,
        deployer_account: LocalAccount,
        master_copy: ChecksumAddress,
        initializer: bytes,
        salt_nonce: int,
        gas: Optional[int] = None,
        gas_price: Optional[int] = None,
        nonce: Optional[int] = None,
    ) -> EthereumTxSent:
        """
        Deploy proxy contract via Proxy Factory using `createProxyWithNonce` (create2)

        :param deployer_account: Ethereum account
        :param master_copy: Address the proxy will point at
        :param initializer: Data for safe creation
        :param salt_nonce: Uint256 for `create2` salt
        :param gas: Gas
        :param gas_price: Gas Price
        :param nonce: Nonce
        :return: Tuple(tx-hash, tx, deployed contract address)
        """
        proxy_factory_contract = self.get_contract()
        create_proxy_fn = proxy_factory_contract.functions.createProxyWithNonce(
            master_copy, initializer, salt_nonce)

        tx_parameters = {"from": deployer_account.address}
        contract_address = create_proxy_fn.call(tx_parameters)

        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

        tx = create_proxy_fn.buildTransaction(tx_parameters)
        # Auto estimation of gas does not work. We use a little more gas just in case
        tx["gas"] = tx["gas"] + 50000
        tx_hash = self.ethereum_client.send_unsigned_transaction(
            tx, private_key=deployer_account.key)
        return EthereumTxSent(tx_hash, tx, contract_address)