Exemplo n.º 1
0
    def _set_from_tx(self, tx: Dict) -> None:
        if not self.sender:
            self.sender = EthAddress(tx["from"])
        self.receiver = EthAddress(tx["to"]) if tx["to"] else None
        self.value = Wei(tx["value"])
        self.gas_price = tx["gasPrice"]
        self.gas_limit = tx["gas"]
        self.input = tx["input"]
        self.nonce = tx["nonce"]

        # if receiver is a known contract, set function name
        if not self.fn_name and state._find_contract(tx["to"]) is not None:
            contract = state._find_contract(tx["to"])
            self.contract_name = contract._name
            self.fn_name = contract.get_method(tx["input"])
Exemplo n.º 2
0
 def __init__(
     self,
     initial_speed: str = "standard",
     max_speed: str = "rapid",
     increment: float = 1.125,
     block_duration: int = 2,
     max_gas_price: Wei = None,
 ):
     super().__init__(block_duration)
     if initial_speed not in ("rapid", "fast", "standard", "slow"):
         raise ValueError("`initial_speed` must be one of: rapid, fast, standard, slow")
     self.initial_speed = initial_speed
     self.max_speed = max_speed
     self.increment = increment
     self.max_gas_price = Wei(max_gas_price) or 2**256 - 1
Exemplo n.º 3
0
    def _set_from_tx(self, tx):
        if not self.sender:
            self.sender = tx['from']
        self.receiver = tx['to']
        self.value = Wei(tx['value'])
        self.gas_price = tx['gasPrice']
        self.gas_limit = tx['gas']
        self.input = tx['input']
        self.nonce = tx['nonce']

        # if receiver is a known contract, set function name
        if tx['to'] and _contracts.find(tx['to']) is not None:
            self.receiver = _contracts.find(tx['to'])
            if not self.fn_name:
                self.contract_name = self.receiver._name
                self.fn_name = self.receiver.get_method(tx['input'])
Exemplo n.º 4
0
    def estimate_gas(self, to, amount, data=""):
        '''Estimates the gas cost for a transaction. Raises VirtualMachineError
        if the transaction would revert.

        Args:
            to: Account instance or address string of transaction recipient.
            amount: Amount of ether to send in wei.
            data: Transaction data hexstring.

        Returns:
            Estimated gas value in wei.'''
        return web3.eth.estimateGas({
            'from': self.address,
            'to': str(to),
            'value': Wei(amount),
            'data': HexBytes(data)
        })
Exemplo n.º 5
0
    def estimate_gas(
        self, to: "Account" = None, amount: int = 0, gas_price: int = None, data: str = None
    ) -> int:
        """
        Estimate the gas cost for a transaction.

        Raises VirtualMachineError if the transaction would revert.

        Arguments
        ---------
        to : Account, optional
            Account instance or address string of transaction recipient.
        amount : int, optional
            Amount of ether to send in wei.
        gas_price : int, optional
            Gas price of the transaction.
        data : str, optional
            Transaction data hexstring.

        Returns
        -------
        Estimated gas value in wei.
        """
        tx: Dict = {
            "from": self.address,
            "to": to_address(str(to)) if to else None,
            "value": Wei(amount),
            "data": HexBytes(data or ""),
        }
        if gas_price is not None:
            tx["gasPrice"] = web3.toHex(gas_price)
        try:
            return web3.eth.estimate_gas(tx)
        except ValueError as exc:
            revert_gas_limit = CONFIG.active_network["settings"]["reverting_tx_gas_limit"]
            if revert_gas_limit == "max":
                revert_gas_limit = web3.eth.get_block("latest")["gasLimit"]
                CONFIG.active_network["settings"]["reverting_tx_gas_limit"] = revert_gas_limit
            if revert_gas_limit:
                return revert_gas_limit

            msg = exc.args[0]["message"] if isinstance(exc.args[0], dict) else str(exc)
            raise ValueError(
                f"Gas estimation failed: '{msg}'. This transaction will likely revert. "
                "If you wish to broadcast, you must set the gas limit manually."
            )
Exemplo n.º 6
0
    def estimate_gas(self,
                     to: "Account" = None,
                     amount: int = 0,
                     gas_price: int = None,
                     data: str = None) -> int:
        """
        Estimate the gas cost for a transaction.

        Raises VirtualMachineError if the transaction would revert.

        Arguments
        ---------
        to : Account, optional
            Account instance or address string of transaction recipient.
        amount : int, optional
            Amount of ether to send in wei.
        gas_price : int, optional
            Gas price of the transaction.
        data : str, optional
            Transaction data hexstring.

        Returns
        -------
        Estimated gas value in wei.
        """
        tx: Dict = {
            "from": self.address,
            "to": str(to) if to else None,
            "value": Wei(amount),
            "data": HexBytes(data or ""),
        }
        if gas_price is not None:
            tx["gasPrice"] = gas_price
        try:
            return web3.eth.estimateGas(tx)
        except ValueError:
            revert_gas_limit = CONFIG.active_network["settings"][
                "reverting_tx_gas_limit"]
            if revert_gas_limit == "max":
                revert_gas_limit = web3.eth.getBlock("latest")["gasLimit"]
                CONFIG.active_network["settings"][
                    "reverting_tx_gas_limit"] = revert_gas_limit
            if revert_gas_limit:
                return revert_gas_limit
            raise
Exemplo n.º 7
0
def gas_price(*args):
    '''Gets and optionally sets the default gas price.

    * If an integer value is given, this will be the default gas price.
    * If set to None, True or False, the gas price is determined
    automatically.'''
    if not is_connected():
        raise ConnectionError("Not connected to any network")
    if args:
        if args[0] in (None, False, True):
            CONFIG['active_network']['gas_price'] = False
        else:
            try:
                price = Wei(args[0])
            except ValueError:
                raise TypeError(f"Invalid gas price '{args[0]}'")
            CONFIG['active_network']['gas_price'] = price
    return CONFIG['active_network']['gas_price']
Exemplo n.º 8
0
def gas_price(*args: Tuple[Union[int, str, bool, None]]) -> Union[int, bool]:
    """Gets and optionally sets the default gas price.

    * If an integer value is given, this will be the default gas price.
    * If set to 'auto', the gas price is determined automatically."""

    if not is_connected():
        raise ConnectionError("Not connected to any network")
    if args:
        if args[0] in (None, False, True, "auto"):
            CONFIG["active_network"]["gas_price"] = False
        else:
            try:
                price = Wei(args[0])
            except ValueError:
                raise TypeError(f"Invalid gas price '{args[0]}'")
            CONFIG["active_network"]["gas_price"] = price
    return CONFIG["active_network"]["gas_price"]
Exemplo n.º 9
0
def max_fee(*args: Tuple[Union[int, str, bool, None]]) -> Union[int, bool]:
    """
    Gets and optionally sets the default max fee per gas.

    * If a Wei value is given, this will be the default max fee.
    * If set to None or False, transactions will default to using gas price.
    """
    if not is_connected():
        raise ConnectionError("Not connected to any network")
    if args:
        if args[0] in (None, False):
            CONFIG.active_network["settings"]["max_fee"] = None
        else:
            try:
                price = Wei(args[0])
            except ValueError:
                raise TypeError(f"Invalid max fee '{args[0]}'")
            CONFIG.active_network["settings"]["max_fee"] = price
    return CONFIG.active_network["settings"]["max_fee"]
Exemplo n.º 10
0
    def estimate_gas(
        self, to: Union[str, "Accounts"], amount: Optional[int], data: str = ""
    ) -> int:
        """Estimates the gas cost for a transaction. Raises VirtualMachineError
        if the transaction would revert.

        Args:
            to: Account instance or address string of transaction recipient.
            amount: Amount of ether to send in wei.
            data: Transaction data hexstring.

        Returns:
            Estimated gas value in wei."""
        try:
            return web3.eth.estimateGas(
                {"from": self.address, "to": str(to), "value": Wei(amount), "data": HexBytes(data)}
            )
        except ValueError:
            if CONFIG["active_network"]["reverting_tx_gas_limit"]:
                return CONFIG["active_network"]["reverting_tx_gas_limit"]
            raise
Exemplo n.º 11
0
 def _add_internal_xfer(self, from_: str, to: str, value: str) -> None:
     self._internal_transfers.append(  # type: ignore
         {"from": EthAddress(from_), "to": EthAddress(to), "value": Wei(f"0x{value}")}
     )
Exemplo n.º 12
0
 def balance(self):
     '''Returns the current ether balance of the contract, in wei.'''
     balance = web3.eth.getBalance(self.address)
     return Wei(balance)
Exemplo n.º 13
0
 def balance(self):
     '''Returns the current balance at the address, in wei.'''
     balance = web3.eth.getBalance(self.address)
     return Wei(balance)
Exemplo n.º 14
0
def test_lt():
    assert Wei("1 ether") < "2 ether"
Exemplo n.º 15
0
def test_ge():
    assert Wei("2 ether") >= "1 ether"
    assert Wei("2 ether") >= "2 ether"
Exemplo n.º 16
0
    def deploy(
        self,
        contract: Any,
        *args: Tuple,
        amount: int = 0,
        gas_limit: Optional[int] = None,
        gas_buffer: Optional[float] = None,
        gas_price: Optional[int] = None,
        nonce: Optional[int] = None,
        required_confs: int = 1,
        allow_revert: bool = None,
        silent: bool = None,
        publish_source: bool = False,
    ) -> Any:
        """Deploys a contract.

        Args:
            contract: ContractContainer instance.
            *args: Constructor arguments. The last argument may optionally be
                   a dictionary of transaction values.

        Kwargs:
            amount: Amount of ether to send with transaction, in wei.
            gas_limit: Gas limit of the transaction.
            gas_buffer: Multiplier to apply to gas limit.
            gas_price: Gas price of the transaction.
            nonce: Nonce to use for the transaction.

        Returns:
            * Contract instance if the transaction confirms and the contract exists
            * TransactionReceipt if the transaction is pending or reverts
        """
        if gas_limit and gas_buffer:
            raise ValueError("Cannot set gas_limit and gas_buffer together")

        data = contract.deploy.encode_input(*args)
        if silent is None:
            silent = bool(CONFIG.mode == "test" or CONFIG.argv["silent"])
        with self._lock:
            try:
                gas_price, gas_strategy, gas_iter = self._gas_price(gas_price)
                gas_limit = Wei(gas_limit) or self._gas_limit(
                    None, amount, gas_price, gas_buffer, data)
                txid = self._transact(  # type: ignore
                    {
                        "from": self.address,
                        "value": Wei(amount),
                        "nonce": nonce if nonce is not None else self._pending_nonce(),
                        "gasPrice": gas_price,
                        "gas": gas_limit,
                        "data": HexBytes(data),
                    },
                    allow_revert,
                )
                exc, revert_data = None, None
            except ValueError as e:
                exc = VirtualMachineError(e)
                if not hasattr(exc, "txid"):
                    raise exc from None
                txid = exc.txid
                revert_data = (exc.revert_msg, exc.pc, exc.revert_type)

            receipt = TransactionReceipt(
                txid,
                self,
                silent=silent,
                required_confs=required_confs,
                is_blocking=False,
                name=contract._name + ".constructor",
                revert_data=revert_data,
            )

        receipt = self._await_confirmation(receipt, required_confs,
                                           gas_strategy, gas_iter)

        add_thread = threading.Thread(target=contract._add_from_tx,
                                      args=(receipt, ),
                                      daemon=True)
        add_thread.start()

        if rpc.is_active():
            undo_thread = threading.Thread(
                target=Chain()._add_to_undo_buffer,
                args=(
                    receipt,
                    self.deploy,
                    (contract, *args),
                    {
                        "amount": amount,
                        "gas_limit": gas_limit,
                        "gas_buffer": gas_buffer,
                        "gas_price": gas_price,
                    },
                ),
                daemon=True,
            )
            undo_thread.start()

        if receipt.status != 1:
            receipt._raise_if_reverted(exc)
            return receipt

        add_thread.join()
        try:
            deployed_contract = contract.at(receipt.contract_address)
            if publish_source:
                contract.publish_source(deployed_contract, silent=silent)
            return deployed_contract
        except ContractNotFound:
            # if the contract self-destructed during deployment
            return receipt
Exemplo n.º 17
0
 def _gas_price(self) -> Wei:
     gas_price = CONFIG.active_network["settings"]["gas_price"]
     if isinstance(gas_price, bool) or gas_price in (None, "auto"):
         return web3.eth.gasPrice
     return Wei(gas_price)
Exemplo n.º 18
0
 def __init__(self, initial_gas_price: Wei, max_gas_price: Wei, time_duration: int = 30):
     super().__init__(time_duration)
     self.initial_gas_price = Wei(initial_gas_price)
     self.max_gas_price = Wei(max_gas_price)
Exemplo n.º 19
0
def test_le():
    assert Wei("1 ether") <= "2 ether"
    assert Wei("1 ether") <= "1 ether"
Exemplo n.º 20
0
    def transfer(
        self,
        to: "Accounts" = None,
        amount: int = 0,
        gas_limit: Optional[int] = None,
        gas_price: Optional[int] = None,
        data: str = None,
        nonce: Optional[int] = None,
        required_confs: int = 1,
        silent: bool = False,
    ) -> "TransactionReceipt":
        """
        Broadcast a transaction from this account.

        Kwargs:
            to: Account instance or address string to transfer to.
            amount: Amount of ether to send, in wei.
            gas_limit: Gas limit of the transaction.
            gas_price: Gas price of the transaction.
            nonce: Nonce to use for the transaction.
            data: Hexstring of data to include in transaction.
            silent: Toggles console verbosity.

        Returns:
            TransactionReceipt object
        """
        with self._lock:
            tx = {
                "from":
                self.address,
                "value":
                Wei(amount),
                "nonce":
                nonce if nonce is not None else self._pending_nonce(),
                "gasPrice":
                Wei(gas_price) if gas_price is not None else self._gas_price(),
                "gas":
                Wei(gas_limit) or self._gas_limit(to, amount, data),
                "data":
                HexBytes(data or ""),
            }
            if to:
                tx["to"] = to_address(str(to))
            try:
                txid = self._transact(tx)  # type: ignore
                exc, revert_data = None, None
            except ValueError as e:
                exc = VirtualMachineError(e)
                if not hasattr(exc, "txid"):
                    raise exc from None
                txid = exc.txid
                revert_data = (exc.revert_msg, exc.pc, exc.revert_type)

            receipt = TransactionReceipt(txid,
                                         self,
                                         required_confs=required_confs,
                                         silent=silent,
                                         revert_data=revert_data)
        if rpc.is_active():
            undo_thread = threading.Thread(
                target=rpc._add_to_undo_buffer,
                args=(receipt, self.transfer, (to, amount, gas_limit,
                                               gas_price, data, None), {}),
                daemon=True,
            )
            undo_thread.start()
        receipt._raise_if_reverted(exc)
        return receipt
Exemplo n.º 21
0
    def deploy(
        self,
        contract: Any,
        *args: Tuple,
        amount: int = 0,
        gas_limit: Optional[int] = None,
        gas_price: Optional[int] = None,
        nonce: Optional[int] = None,
        required_confs: int = 1,
    ) -> Any:
        """Deploys a contract.

        Args:
            contract: ContractContainer instance.
            *args: Constructor arguments. The last argument may optionally be
                   a dictionary of transaction values.

        Kwargs:
            amount: Amount of ether to send with transaction, in wei.
            gas_limit: Gas limit of the transaction.
            gas_price: Gas price of the transaction.
            nonce: Nonce to use for the transaction.

        Returns:
            * Contract instance if the transaction confirms and the contract exists
            * TransactionReceipt if the transaction is pending or reverts
        """

        evm = contract._build["compiler"]["evm_version"]
        if rpc.is_active() and not rpc.evm_compatible(evm):
            raise IncompatibleEVMVersion(
                f"Local RPC using '{rpc.evm_version()}' but contract was compiled for '{evm}'"
            )
        data = contract.deploy.encode_input(*args)
        with self._lock:
            try:
                txid = self._transact(  # type: ignore
                    {
                        "from": self.address,
                        "value": Wei(amount),
                        "nonce": nonce if nonce is not None else self._pending_nonce(),
                        "gasPrice": Wei(gas_price) or self._gas_price(),
                        "gas": Wei(gas_limit) or self._gas_limit(None, amount, data),
                        "data": HexBytes(data),
                    }
                )
                exc, revert_data = None, None
            except ValueError as e:
                exc = VirtualMachineError(e)
                if not hasattr(exc, "txid"):
                    raise exc from None
                txid = exc.txid
                revert_data = (exc.revert_msg, exc.pc, exc.revert_type)

            receipt = TransactionReceipt(
                txid,
                self,
                required_confs=required_confs,
                name=contract._name + ".constructor",
                revert_data=revert_data,
            )
        add_thread = threading.Thread(target=contract._add_from_tx,
                                      args=(receipt, ),
                                      daemon=True)
        add_thread.start()

        if rpc.is_active():
            undo_thread = threading.Thread(
                target=rpc._add_to_undo_buffer,
                args=(
                    receipt,
                    self.deploy,
                    (contract, *args),
                    {
                        "amount": amount,
                        "gas_limit": gas_limit,
                        "gas_price": gas_price
                    },
                ),
                daemon=True,
            )
            undo_thread.start()

        if receipt.status != 1:
            receipt._raise_if_reverted(exc)
            return receipt

        add_thread.join()
        try:
            return contract.at(receipt.contract_address)
        except ContractNotFound:
            # if the contract self-destructed during deployment
            return receipt
Exemplo n.º 22
0
def test_bytes():
    assert Wei(b"\xff") == 255
Exemplo n.º 23
0
def test_nonetype():
    assert Wei(None) == 0
Exemplo n.º 24
0
    def deploy(
        self,
        contract: Any,
        *args: Tuple,
        amount: int = 0,
        gas_limit: Optional[int] = None,
        gas_price: Optional[int] = None,
    ) -> Any:
        """Deploys a contract.

        Args:
            contract: ContractContainer instance.
            *args: Constructor arguments. The last argument may optionally be
                   a dictionary of transaction values.

        Kwargs:
            amount: Amount of ether to send with transaction, in wei.
            gas_limit: Gas limit of the transaction.
            gas_price: Gas price of the transaction.

        Returns:
            * Contract instance if the transaction confirms
            * TransactionReceipt if the transaction is pending or reverts"""

        evm = contract._build["compiler"]["evm_version"]
        if rpc.is_active() and not rpc.evm_compatible(evm):
            raise IncompatibleEVMVersion(
                f"Local RPC using '{rpc.evm_version()}' but contract was compiled for '{evm}'"
            )
        data = contract.deploy.encode_input(*args)
        try:
            txid = self._transact(  # type: ignore
                {
                    "from": self.address,
                    "value": Wei(amount),
                    "nonce": self.nonce,
                    "gasPrice": Wei(gas_price) or self._gas_price(),
                    "gas": Wei(gas_limit) or self._gas_limit(None, amount, data),
                    "data": HexBytes(data),
                }
            )
            revert_data = None
        except ValueError as e:
            txid, revert_data = _raise_or_return_tx(e)
        tx = TransactionReceipt(txid,
                                self,
                                name=contract._name + ".constructor",
                                revert_data=revert_data)
        add_thread = threading.Thread(target=contract._add_from_tx,
                                      args=(tx, ),
                                      daemon=True)
        add_thread.start()
        if rpc.is_active():
            rpc._add_to_undo_buffer(
                self.deploy,
                (contract, *args),
                {
                    "amount": amount,
                    "gas_limit": gas_limit,
                    "gas_price": gas_price
                },
            )
        if tx.status != 1:
            return tx
        add_thread.join()
        return contract.at(tx.contract_address)
Exemplo n.º 25
0
 def block_gas_limit(self) -> Wei:
     if time.time() > self._block_gas_time + 3600:
         block = web3.eth.getBlock("latest")
         self._block_gas_limit = block["gasLimit"]
         self._block_gas_time = block["timestamp"]
     return Wei(self._block_gas_limit)
Exemplo n.º 26
0
 def _gas_limit(self, to, amount, data=""):
     if CONFIG['active_network']['gas_limit'] not in (True, False, None):
         return Wei(CONFIG['active_network']['gas_limit'])
     return self.estimate_gas(to, amount, data)
Exemplo n.º 27
0
 def balance(self) -> Wei:
     """Returns the current balance at the address, in wei."""
     balance = web3.eth.get_balance(self.address)
     return Wei(balance)
Exemplo n.º 28
0
 def _gas_price(self):
     return Wei(CONFIG['active_network']['gas_price'] or web3.eth.gasPrice)
Exemplo n.º 29
0
    def transfer(
        self,
        to: "Account" = None,
        amount: int = 0,
        gas_limit: Optional[int] = None,
        gas_buffer: Optional[float] = None,
        gas_price: Optional[int] = None,
        data: str = None,
        nonce: Optional[int] = None,
        required_confs: int = 1,
        allow_revert: bool = None,
        silent: bool = None,
    ) -> TransactionReceipt:
        """
        Broadcast a transaction from this account.

        Kwargs:
            to: Account instance or address string to transfer to.
            amount: Amount of ether to send, in wei.
            gas_limit: Gas limit of the transaction.
            gas_buffer: Multiplier to apply to gas limit.
            gas_price: Gas price of the transaction.
            nonce: Nonce to use for the transaction.
            data: Hexstring of data to include in transaction.
            silent: Toggles console verbosity.

        Returns:
            TransactionReceipt object
        """
        if gas_limit and gas_buffer:
            raise ValueError("Cannot set gas_limit and gas_buffer together")
        if silent is None:
            silent = bool(CONFIG.mode == "test" or CONFIG.argv["silent"])
        with self._lock:
            gas_price, gas_strategy, gas_iter = self._gas_price(gas_price)
            gas_limit = Wei(gas_limit) or self._gas_limit(
                to, amount, gas_price, gas_buffer, data)
            tx = {
                "from": self.address,
                "value": Wei(amount),
                "nonce": nonce if nonce is not None else self._pending_nonce(),
                "gasPrice": gas_price,
                "gas": gas_limit,
                "data": HexBytes(data or ""),
            }
            if to:
                tx["to"] = to_address(str(to))
            try:
                txid = self._transact(tx, allow_revert)  # type: ignore
                exc, revert_data = None, None
            except ValueError as e:
                exc = VirtualMachineError(e)
                if not hasattr(exc, "txid"):
                    raise exc from None
                txid = exc.txid
                revert_data = (exc.revert_msg, exc.pc, exc.revert_type)

            receipt = TransactionReceipt(
                txid,
                self,
                required_confs=required_confs,
                is_blocking=False,
                silent=silent,
                revert_data=revert_data,
            )

        receipt = self._await_confirmation(receipt, required_confs,
                                           gas_strategy, gas_iter)

        if rpc.is_active():
            undo_thread = threading.Thread(
                target=Chain()._add_to_undo_buffer,
                args=(
                    receipt,
                    self.transfer,
                    (to, amount, gas_limit, gas_buffer, gas_price, data, None),
                    {},
                ),
                daemon=True,
            )
            undo_thread.start()

        receipt._raise_if_reverted(exc)
        return receipt
Exemplo n.º 30
0
def test_gt():
    assert Wei("2 ether") > "1 ether"