def test_personal_sendTransaction_deprecated(
        self,
        web3: "Web3",
        unlockable_account_dual_type: ChecksumAddress,
        unlockable_account_pw: str,
    ) -> None:
        with pytest.warns(DeprecationWarning):
            assert web3.eth.getBalance(
                unlockable_account_dual_type) > web3.toWei(1, 'ether')
            txn_params: TxParams = {
                'from': unlockable_account_dual_type,
                'to': unlockable_account_dual_type,
                'gas': Wei(21000),
                'value': Wei(1),
                'gasPrice': web3.toWei(1, 'gwei'),
            }
            txn_hash = web3.parity.personal.sendTransaction(
                txn_params, unlockable_account_pw)
            assert txn_hash
            transaction = web3.eth.getTransaction(txn_hash)

            assert is_same_address(transaction['from'],
                                   cast(ChecksumAddress, txn_params['from']))
            assert is_same_address(transaction['to'],
                                   cast(ChecksumAddress, txn_params['to']))
            assert transaction['gas'] == txn_params['gas']
            assert transaction['value'] == txn_params['value']
            assert transaction['gasPrice'] == txn_params['gasPrice']
Ejemplo n.º 2
0
    def gas_price_strategy_eth_gas_station_or_with_margin(
            web3: Web3, transaction_params):
        # FIXME: This is a temporary fix to speed up gas price generation
        # by fetching from eth_gas_station if possible.
        # Once we have a reliable gas price calculation this can be removed
        if int(web3.net.version) == 1:
            try:
                response = requests.get(ETH_GAS_STATION_API)
                if response and response.status_code == 200:
                    data = response.json()
                    log.debug(
                        f"fetched gas price: {Wei(int(data['fast'] * 10e7 * 1.1))} Wei"
                    )
                    return Wei(int(data["fast"] * 10e7 * 1.1))
            except (TimeoutError, ConnectionError, KeyError):
                log.debug(
                    "Could not fetch from ethgasstation. Falling back to web3 gas estimation."
                )

        gas_price_strategy = construct_time_based_gas_price_strategy(
            max_wait_seconds=15, sample_size=25)
        gas_price = Wei(
            int(
                gas_price_strategy(web3, transaction_params) *
                GAS_PRICE_MARGIN))
        return gas_price
Ejemplo n.º 3
0
    def test_eth_modifyTransaction(self, web3: "Web3",
                                   unlocked_account: ChecksumAddress) -> None:
        txn_params: TxParams = {
            'from': unlocked_account,
            'to': unlocked_account,
            'value': Wei(1),
            'gas': Wei(21000),
            'gasPrice': web3.eth.gasPrice,
        }
        txn_hash = web3.eth.sendTransaction(txn_params)

        modified_txn_hash = web3.eth.modifyTransaction(
            txn_hash,
            gasPrice=(cast(int, txn_params['gasPrice']) * 2),
            value=2)
        modified_txn = web3.eth.getTransaction(modified_txn_hash)

        assert is_same_address(modified_txn['from'],
                               cast(ChecksumAddress, txn_params['from']))
        assert is_same_address(modified_txn['to'],
                               cast(ChecksumAddress, txn_params['to']))
        assert modified_txn['value'] == 2
        assert modified_txn['gas'] == 21000
        assert modified_txn['gasPrice'] == cast(int,
                                                txn_params['gasPrice']) * 2
Ejemplo n.º 4
0
 def approve(self,
             token_address: ChecksumAddress,
             v2: bool = False,
             max_approval: Optional[int] = None) -> bool:
     max_approval = self.max_approval_int if not max_approval else max_approval
     token_contract = self.get_token_contract(token_address=token_address)
     router_address = self.addr.router_v2 if v2 else self.addr.router_v1
     func = token_contract.functions.approve(router_address, max_approval)
     logger.info(
         f'Approving {self.get_token_symbol(token_address=token_address)} - {token_address}...'
     )
     try:
         gas_limit = Wei(
             int(
                 Decimal(
                     func.estimateGas({
                         'from': self.wallet,
                         'value': Wei(0)
                     })) * Decimal(1.5)))
     except Exception:
         gas_limit = Wei(100000)
     tx_params = self.get_tx_params(gas=gas_limit)
     tx = self.build_and_send_tx(func, tx_params=tx_params)
     receipt = self.w3.eth.wait_for_transaction_receipt(tx, timeout=6000)
     if receipt['status'] == 0:  # fail
         logger.error(
             f'Approval call failed at tx {Web3.toHex(primitive=receipt["transactionHash"])}'
         )
         return False
     self.approved.add((str(token_address), v2))
     time.sleep(10)  # let tx propagate
     logger.success('Approved wallet for trading.')
     return True
Ejemplo n.º 5
0
    def test_personal_send_transaction(
        self,
        web3: "Web3",
        unlockable_account_dual_type: ChecksumAddress,
        unlockable_account_pw: str,
    ) -> None:
        assert web3.eth.get_balance(
            unlockable_account_dual_type) > constants.WEI_PER_ETHER
        txn_params: TxParams = {
            'from': unlockable_account_dual_type,
            'to': unlockable_account_dual_type,
            'gas': Wei(21000),
            'value': Wei(1),
            'gasPrice': web3.toWei(1, 'gwei'),
        }
        txn_hash = web3.geth.personal.send_transaction(txn_params,
                                                       unlockable_account_pw)
        assert txn_hash
        transaction = web3.eth.get_transaction(txn_hash)

        assert is_same_address(transaction['from'],
                               cast(ChecksumAddress, txn_params['from']))
        assert is_same_address(transaction['to'],
                               cast(ChecksumAddress, txn_params['to']))
        assert transaction['gas'] == txn_params['gas']
        assert transaction['value'] == txn_params['value']
        assert transaction['gasPrice'] == txn_params['gasPrice']
    def _get_initial_setup_safe_data(self, owners: List[str], threshold: int,
                                     fallback_handler: str = NULL_ADDRESS,
                                     payment_token: str = NULL_ADDRESS,
                                     payment: int = 0,
                                     payment_receiver: str = NULL_ADDRESS) -> bytes:
        empty_params: TxParams = {
                'gas': Wei(1),
                'gasPrice': Wei(1),
            }

        if self.safe_version == '1.1.1':
            return HexBytes(self.master_copy_contract.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(empty_params)['data'])
        elif self.safe_version == '1.0.0':
            return HexBytes(self.master_copy_contract.functions.setup(
                owners,
                threshold,
                NULL_ADDRESS,  # Contract address for optional delegate call
                b'',  # Data payload for optional delegate call
                payment_token,
                payment,
                payment_receiver
            ).buildTransaction(empty_params)['data'])
        else:
            raise ValueError('Safe version must be 1.1.1 or 1.0.0')
Ejemplo n.º 7
0
 def get_token_balance_wei(self, token_address: ChecksumAddress) -> Wei:
     token_contract = self.get_token_contract(token_address)
     try:
         return Wei(token_contract.functions.balanceOf(self.wallet).call())
     except (ABIFunctionNotFound, ContractLogicError):
         logger.error(
             f'Contract {token_address} does not have function "balanceOf"')
     return Wei(0)
Ejemplo n.º 8
0
async def get_partners_rewards(network: str, from_block: BlockNumber,
                               to_block: BlockNumber,
                               total_reward: Wei) -> Tuple[Rewards, Wei]:
    """Fetches partners rewards."""
    result: Dict = await execute_sw_gql_query(
        network=network,
        query=PARTNERS_QUERY,
        variables=dict(block_number=to_block, ),
    )
    partners = result["partners"]
    reward_token_address = NETWORKS[network]["REWARD_TOKEN_CONTRACT_ADDRESS"]

    # process partners
    points: Dict[ChecksumAddress, int] = {}
    total_points = 0
    total_contributed = 0
    for partner in partners:
        account = Web3.toChecksumAddress(partner["id"])
        if account == EMPTY_ADDR_HEX:
            continue

        contributed_amount = Wei(int(partner["contributedAmount"]))
        total_contributed += contributed_amount

        revenue_share = int(partner["revenueShare"])
        prev_account_points = int(partner["distributorPoints"])
        updated_at_block = BlockNumber(int(partner["updatedAtBlock"]))
        if from_block > updated_at_block:
            updated_at_block = from_block
            prev_account_points = 0

        account_points = prev_account_points + (contributed_amount *
                                                revenue_share *
                                                (to_block - updated_at_block))
        if account_points <= 0:
            continue

        points[account] = account_points
        total_points += account_points

    if total_contributed <= 0:
        return {}, total_reward

    partners_reward = Wei(
        (total_reward * total_points) // (total_contributed * 10000 *
                                          (to_block - from_block)))
    if partners_reward <= 0:
        return {}, total_reward

    partners_reward = min(total_reward, partners_reward)
    rewards = calculate_points_based_rewards(
        total_reward=partners_reward,
        points=points,
        total_points=total_points,
        reward_token=reward_token_address,
    )

    return rewards, Wei(total_reward - partners_reward)
Ejemplo n.º 9
0
 def _get_tx_params(self, value: Wei = Wei(0), gas: Wei = Wei(250000)) -> TxParams:
     """Get generic transaction parameters."""
     return {
         "from": _addr_to_str(self.address),
         "value": value,
         "gas": gas,
         "nonce": max(
             self.last_nonce, self.w3.eth.getTransactionCount(self.address)
         ),
     }
Ejemplo n.º 10
0
 def buy_tokens(
     self,
     token_address: ChecksumAddress,
     amount_bnb: Wei,
     slippage_percent: int,
     gas_price: Optional[str],
     v2: bool = True,
 ) -> Tuple[bool, Decimal, str]:
     balance_bnb = self.w3.eth.get_balance(self.wallet)
     if amount_bnb > balance_bnb - Wei(
             2000000000000000):  # leave 0.002 BNB for future gas fees
         logger.error('Not enough BNB balance')
         return False, Decimal(0), 'Not enough BNB balance'
     slippage_ratio = (Decimal(100) -
                       Decimal(slippage_percent)) / Decimal(100)
     final_gas_price = self.w3.eth.gas_price
     if gas_price is not None and gas_price.startswith('+'):
         offset = Web3.toWei(Decimal(gas_price) * Decimal(10**9),
                             unit='wei')
         final_gas_price = Wei(final_gas_price + offset)
     elif gas_price is not None:
         final_gas_price = Web3.toWei(gas_price, unit='wei')
     router_contract = self.contracts.router_v2 if v2 else self.contracts.router_v1
     predicted_out = router_contract.functions.getAmountsOut(
         amount_bnb, [self.addr.wbnb, token_address]).call()[-1]
     min_output_tokens = Web3.toWei(slippage_ratio * predicted_out,
                                    unit='wei')
     receipt = self.buy_tokens_with_params(
         token_address=token_address,
         amount_bnb=amount_bnb,
         min_output_tokens=min_output_tokens,
         gas_price=final_gas_price,
         v2=v2,
     )
     if receipt is None:
         logger.error('Can\'t get gas estimate')
         return False, Decimal(0), 'Can\'t get gas estimate'
     txhash = Web3.toHex(primitive=receipt["transactionHash"])
     if receipt['status'] == 0:  # fail
         logger.error(f'Buy transaction failed at tx {txhash}')
         return False, Decimal(0), txhash
     amount_out = Decimal(0)
     logs = self.get_token_contract(
         token_address=token_address).events.Transfer().processReceipt(
             receipt)
     for log in reversed(logs):  # only get last withdrawal call
         if log['address'] != token_address:
             continue
         if log['args']['to'] != self.wallet:
             continue
         amount_out = Decimal(log['args']['value']) / Decimal(
             10**self.get_token_decimals(token_address))
         break
     logger.success(f'Buy transaction succeeded at tx {txhash}')
     return True, amount_out, txhash
Ejemplo n.º 11
0
 def test_eth_getTransactionReceipt_unmined(
         self, web3: "Web3",
         unlocked_account_dual_type: ChecksumAddress) -> None:
     txn_hash = web3.eth.sendTransaction({
         'from': unlocked_account_dual_type,
         'to': unlocked_account_dual_type,
         'value': Wei(1),
         'gas': Wei(21000),
         'gasPrice': web3.eth.gasPrice,
     })
     with pytest.raises(TransactionNotFound):
         web3.eth.getTransactionReceipt(txn_hash)
Ejemplo n.º 12
0
def get_tx_params(value: Wei = Wei(0), gas: Wei = Wei(250000)) -> TxParams:
    """
    Get generic transaction parameters.
    """

    logging.info('get_tx_params function was called.')

    return {
        "from": addr_to_str(WALLET_ADDRESS),
        "value": value,
        "gas": gas,
        "nonce": w3.eth.getTransactionCount(WALLET_ADDRESS),
    }
Ejemplo n.º 13
0
 def get_tx_params(self,
                   value: Wei = Wei(0),
                   gas: Wei = Wei(100000),
                   gas_price: Optional[Wei] = None) -> TxParams:
     # 100000 gas is OK for approval tx, so it's the default
     params: TxParams = {
         'from': self.wallet,
         'value': value,
         'gas': gas,
         'nonce': self.last_nonce,
     }
     if gas_price:
         params['gasPrice'] = gas_price
     return params
Ejemplo n.º 14
0
    def test_eth_replaceTransaction_gas_price_too_low(
            self, web3: "Web3",
            unlocked_account_dual_type: ChecksumAddress) -> None:
        txn_params: TxParams = {
            'from': unlocked_account_dual_type,
            'to': unlocked_account_dual_type,
            'value': Wei(1),
            'gas': Wei(21000),
            'gasPrice': Wei(10),
        }
        txn_hash = web3.eth.sendTransaction(txn_params)

        txn_params['gasPrice'] = Wei(9)
        with pytest.raises(ValueError):
            web3.eth.replaceTransaction(txn_hash, txn_params)
Ejemplo n.º 15
0
 def test_eth_replaceTransaction_non_existing_transaction(
         self, web3: "Web3",
         unlocked_account_dual_type: ChecksumAddress) -> None:
     txn_params: TxParams = {
         'from': unlocked_account_dual_type,
         'to': unlocked_account_dual_type,
         'value': Wei(1),
         'gas': Wei(21000),
         'gasPrice': web3.eth.gasPrice,
     }
     with pytest.raises(TransactionNotFound):
         web3.eth.replaceTransaction(
             HexStr(
                 '0x98e8cc09b311583c5079fa600f6c2a3bea8611af168c52e4b60b5b243a441997'
             ), txn_params)
Ejemplo n.º 16
0
def get_buffered_gas_estimate(
    web3: "Web3", transaction: TxParams, gas_buffer: Wei = Wei(100000)) -> Wei:
    gas_estimate_transaction = cast(TxParams, dict(**transaction))

    gas_estimate = web3.eth.estimateGas(gas_estimate_transaction)

    gas_limit = get_block_gas_limit(web3)

    if gas_estimate > gas_limit:
        raise ValueError(
            "Contract does not appear to be deployable within the "
            "current network gas limits.  Estimated: {0}. Current gas "
            "limit: {1}".format(gas_estimate, gas_limit))

    return Wei(min(gas_limit, gas_estimate + gas_buffer))
Ejemplo n.º 17
0
def get_balance(address: str,
                network: str = config["network"],
                provider: str = config["provider"],
                token: Optional[str] = None) -> Wei:
    """
    Get Ethereum balance.

    :param address: Ethereum address.
    :type address: str
    :param network: Ethereum network, defaults to ``mainnet``.
    :type network: str
    :param provider: Ethereum network provider, defaults to ``http``.
    :type provider: str
    :param token: Infura API endpoint token, defaults to ``4414fea5f7454211956b1627621450b4``.
    :type token: str

    :returns: Wei -- Ethereum balance (Wei).

    >>> from swap.providers.ethereum.rpc import get_balance
    >>> get_balance("0x70c1eb09363603a3b6391deb2daa6d2561a62f52", "ropsten")
    25800000
    """

    # Check parameter instances
    if not is_address(address=address):
        raise AddressError(f"Invalid Ethereum '{address}' address.")

    web3: Web3 = get_web3(network=network, provider=provider, token=token)
    balance: int = web3.eth.get_balance(to_checksum_address(address=address))
    return Wei(balance)
Ejemplo n.º 18
0
    def __init__(self, order_record: Order, net: Network,
                 dispatcher: Dispatcher, chat_id: int):
        self.order_record = order_record
        self.token_record: Token = order_record.token
        self.net = net
        self.dispatcher = dispatcher
        self.chat_id = chat_id

        self.type = order_record.type  # buy (tokens for BNB) or sell (tokens for BNB)
        self.limit_price: Optional[Decimal] = (Decimal(
            order_record.limit_price) if order_record.limit_price else None
                                               )  # decimal stored as string
        self.above = order_record.above  # Above = True, below = False
        self.trailing_stop: Optional[
            int] = order_record.trailing_stop  # in percent
        self.amount = Wei(
            int(order_record.amount)
        )  # in wei, either BNB (buy) or token (sell) depending on "type"
        self.slippage = order_record.slippage  # in percent
        # gas price in wei or offset from default in gwei (starts with +), if null then use network gas price
        self.gas_price: Optional[str] = order_record.gas_price
        self.created = order_record.created
        self.active = True
        self.finished = False
        self.min_price: Optional[Decimal] = None
        self.max_price: Optional[Decimal] = None
Ejemplo n.º 19
0
def get_balance(address: str, network: str = config["network"], provider: str = config["provider"],
                token: Optional[str] = None) -> Wei:
    """
    Get Ethereum balance.

    :param address: Ethereum address.
    :type address: str
    :param network: Ethereum network, defaults to ``mainnet``.
    :type network: str
    :param provider: Ethereum network provider, defaults to ``http``.
    :type provider: str
    :param token: Infura API endpoint token, defaults to ``4414fea5f7454211956b1627621450b4``.
    :type token: str

    :returns: Wei -- Ethereum balance (Wei).

    >>> from swap.providers.ethereum.rpc import get_balance
    >>> get_balance(address="0xbaF2Fc3829B6D25739BeDC18a5A83bF519c6Fe8c", network="testnet")
    99937915760000000000
    """

    # Check parameter instances
    if not is_address(address=address):
        raise AddressError(f"Invalid Ethereum '{address}' address.")

    web3: Web3 = get_web3(network=network, provider=provider, token=token)
    balance: int = web3.eth.get_balance(
        to_checksum_address(address=address)
    )
    return Wei(balance)
Ejemplo n.º 20
0
    def test_eth_replaceTransaction_incorrect_nonce(
            self, web3: "Web3", unlocked_account: ChecksumAddress) -> None:
        txn_params: TxParams = {
            'from': unlocked_account,
            'to': unlocked_account,
            'value': Wei(1),
            'gas': Wei(21000),
            'gasPrice': web3.eth.gasPrice,
        }
        txn_hash = web3.eth.sendTransaction(txn_params)
        txn = web3.eth.getTransaction(txn_hash)

        txn_params['gasPrice'] = Wei(web3.eth.gasPrice * 2)
        txn_params['nonce'] = Nonce(txn['nonce'] + 1)
        with pytest.raises(ValueError):
            web3.eth.replaceTransaction(txn_hash, txn_params)
Ejemplo n.º 21
0
    def test_eth_replaceTransaction_already_mined(
            self, web3: "Web3",
            unlocked_account_dual_type: ChecksumAddress) -> None:
        txn_params: TxParams = {
            'from': unlocked_account_dual_type,
            'to': unlocked_account_dual_type,
            'value': Wei(1),
            'gas': Wei(21000),
            'gasPrice': web3.eth.gasPrice,
        }
        txn_hash = web3.eth.sendTransaction(txn_params)
        web3.eth.waitForTransactionReceipt(txn_hash)

        txn_params['gasPrice'] = Wei(web3.eth.gasPrice * 2)
        with pytest.raises(ValueError, match="Supplied transaction with hash"):
            web3.eth.replaceTransaction(txn_hash, txn_params)
Ejemplo n.º 22
0
def get_balance(address: str,
                network: str = config["network"],
                provider: str = config["provider"]) -> Wei:
    """
    Get XinFin balance.

    :param address: XinFin address.
    :type address: str
    :param network: XinFin network, defaults to ``mainnet``.
    :type network: str
    :param provider: XinFin network provider, defaults to ``http``.
    :type provider: str

    :returns: Wei -- XinFin balance (Wei).

    >>> from swap.providers.xinfin.rpc import get_balance
    >>> get_balance("xdc70c1eb09363603a3b6391deb2daa6d2561a62f52", "mainnet")
    71560900
    """

    # Check parameter instances
    if not is_address(address=address):
        raise AddressError(f"Invalid XinFin '{address}' address.")

    web3: Web3 = get_web3(network=network, provider=provider)
    balance: int = web3.eth.get_balance(
        to_checksum_address(address=address, prefix="0x"))
    return Wei(balance)
Ejemplo n.º 23
0
class Economics:

    _default_min_authorization = TToken(40_000, 'T').to_units()
    _default_min_operator_seconds = 60 * 60 * 24  # one day in seconds
    _default_fee_rate = Wei(Web3.toWei(1, 'gwei'))

    # TODO: Reintroduce Adjudicator
    # Slashing parameters
    # HASH_ALGORITHM_KECCAK256 = 0
    # HASH_ALGORITHM_SHA256 = 1
    # HASH_ALGORITHM_RIPEMD160 = 2
    # _default_hash_algorithm = HASH_ALGORITHM_SHA256
    # _default_base_penalty = 2
    # _default_penalty_history_coefficient = 0
    # _default_percentage_penalty_coefficient = 100000  # 0.001%
    # _default_reward_coefficient = 2

    def __init__(
        self,
        min_operator_seconds: int = _default_min_operator_seconds,
        min_authorization: int = _default_min_authorization,
        fee_rate: Wei = _default_fee_rate,

        # Adjudicator
        # hash_algorithm: int = _default_hash_algorithm,
        # base_penalty: int = _default_base_penalty,
        # penalty_history_coefficient: int = _default_penalty_history_coefficient,
        # percentage_penalty_coefficient: int = _default_percentage_penalty_coefficient,
        # reward_coefficient: int = _default_reward_coefficient
    ):
        """
        :param min_operator_seconds: Min amount of seconds while an operator can't be changed
        :param min_authorization: Amount of minimum allowable authorization

        """
        # TODO: Reintroduce Adjudicator
        # :param hash_algorithm: Hashing algorithm
        # :param base_penalty: Base for the penalty calculation
        # :param penalty_history_coefficient: Coefficient for calculating the penalty depending on the history
        # :param percentage_penalty_coefficient: Coefficient for calculating the percentage penalty
        # :param reward_coefficient: Coefficient for calculating the reward

        # self.hash_algorithm = hash_algorithm
        # self.base_penalty = base_penalty
        # self.penalty_history_coefficient = penalty_history_coefficient
        # self.percentage_penalty_coefficient = percentage_penalty_coefficient
        # self.reward_coefficient = reward_coefficient

        self.min_operator_seconds = min_operator_seconds
        self.min_authorization = min_authorization
        self.fee_rate = fee_rate

    @property
    def pre_application_deployment_parameters(self) -> Tuple[int, ...]:
        """Cast coefficient attributes to uint256 compatible type for solidity+EVM"""
        deploy_parameters = (  # note: order-sensitive
            self.min_authorization,
            self.min_operator_seconds,
        )
        return tuple(map(int, deploy_parameters))
Ejemplo n.º 24
0
 def make_trade(
     self,
     input_token: AddressLike,
     output_token: AddressLike,
     qty: Union[int, Wei],
     gwei,
     my_address,
     my_pk,
     recipient: AddressLike = None,
 ) -> HexBytes:
     """Make a trade by defining the qty of the input token."""
     if input_token == ETH_ADDRESS:
         return self._eth_to_token_swap_input(gwei, my_address,
                                              my_pk, output_token,
                                              Wei(qty), recipient)
     else:
         balance = self.get_token_balance(input_token)
         if balance < qty:
             raise InsufficientBalance(balance, qty)
         if output_token == ETH_ADDRESS:
             return self._token_to_eth_swap_input(
                 gwei, my_address, my_pk, input_token, qty, recipient)
         else:
             return self._token_to_token_swap_input(
                 gwei, my_address, my_pk, input_token, qty,
                 output_token, recipient)
Ejemplo n.º 25
0
    def make_trade_output(
        self,
        input_token: AddressLike,
        output_token: AddressLike,
        qty: Union[int, Wei],
        recipient: AddressLike = None,
        fee: int = None,
        slippage: float = None,
    ) -> HexBytes:
        """Make a trade by defining the qty of the output token."""
        if fee is None:
            fee = 3000
            if self.version == 3:
                logger.warning("No fee set, assuming 0.3%")

        if slippage is None:
            slippage = self.default_slippage

        if input_token == ETH_ADDRESS:
            balance = self.get_eth_balance()
            need = self._get_eth_token_output_price(output_token, qty)
            if balance < need:
                raise InsufficientBalance(balance, need)
            return self._eth_to_token_swap_output(output_token, qty, recipient,
                                                  fee, slippage)
        elif output_token == ETH_ADDRESS:
            qty = Wei(qty)
            return self._token_to_eth_swap_output(input_token, qty, recipient,
                                                  fee, slippage)
        else:
            return self._token_to_token_swap_output(input_token, output_token,
                                                    qty, recipient, fee,
                                                    slippage)
Ejemplo n.º 26
0
    def test_eth_replaceTransaction_gas_price_defaulting_minimum(
            self, web3: "Web3", unlocked_account: ChecksumAddress) -> None:
        txn_params: TxParams = {
            'from': unlocked_account,
            'to': unlocked_account,
            'value': Wei(1),
            'gas': Wei(21000),
            'gasPrice': Wei(10),
        }
        txn_hash = web3.eth.sendTransaction(txn_params)

        txn_params.pop('gasPrice')
        replace_txn_hash = web3.eth.replaceTransaction(txn_hash, txn_params)
        replace_txn = web3.eth.getTransaction(replace_txn_hash)

        assert replace_txn['gasPrice'] == 12  # minimum gas price
Ejemplo n.º 27
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': Wei(1), 'gasPrice': Wei(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=HexBytes(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
        assert tx_receipt['status']

        contract_address = tx_receipt['contractAddress']
        return EthereumTxSent(tx_hash, tx, contract_address)
Ejemplo n.º 28
0
 def test_eth_signTransaction(self, web3: "Web3", unlocked_account: ChecksumAddress) -> None:
     txn_params: TxParams = {
         'from': unlocked_account,
         'to': unlocked_account,
         'value': Wei(1),
         'gas': Wei(21000),
         'gasPrice': web3.eth.gasPrice,
         'nonce': Nonce(0),
     }
     result = web3.eth.signTransaction(txn_params)
     signatory_account = web3.eth.account.recover_transaction(result['raw'])
     assert unlocked_account == signatory_account
     assert result['tx']['to'] == txn_params['to']
     assert result['tx']['value'] == txn_params['value']
     assert result['tx']['gas'] == txn_params['gas']
     assert result['tx']['gasPrice'] == txn_params['gasPrice']
     assert result['tx']['nonce'] == txn_params['nonce']
Ejemplo n.º 29
0
 def sell_tokens_with_params(
     self,
     token_address: ChecksumAddress,
     amount_tokens: Wei,
     min_output_bnb: Wei,
     gas_price: Wei,
     v2: bool,
 ) -> Optional[TxReceipt]:
     router_contract = self.contracts.router_v2 if v2 else self.contracts.router_v1
     func = router_contract.functions.swapExactTokensForETHSupportingFeeOnTransferTokens(
         amount_tokens, min_output_bnb, [token_address, self.addr.wbnb],
         self.wallet, self.deadline(60))
     try:
         gas_limit = Wei(
             int(
                 Decimal(
                     func.estimateGas({
                         'from': self.wallet,
                         'value': Wei(0)
                     })) * Decimal(1.5)))
     except Exception as e:
         logger.warning(
             f'Error estimating gas price, trying again with older method: {e}'
         )
         func = router_contract.functions.swapExactTokensForETH(
             amount_tokens, min_output_bnb, [token_address, self.addr.wbnb],
             self.wallet, self.deadline(60))
         try:
             gas_limit = Wei(
                 int(
                     Decimal(
                         func.estimateGas({
                             'from': self.wallet,
                             'value': Wei(0)
                         })) * Decimal(1.5)))
         except Exception:
             logger.error(
                 'Can\'t get gas estimate, cancelling transaction.')
             return None
     if gas_limit > GAS_LIMIT_FAILSAFE:
         gas_limit = GAS_LIMIT_FAILSAFE
     params = self.get_tx_params(value=Wei(0),
                                 gas=gas_limit,
                                 gas_price=gas_price)
     tx = self.build_and_send_tx(func=func, tx_params=params)
     return self.w3.eth.wait_for_transaction_receipt(tx, timeout=60)
Ejemplo n.º 30
0
 def validate_price(self, value: Wei, duration: Wei, shares: int, *args,
                    **kwargs) -> bool:
     expected_price = Wei(shares * duration * self.rate)
     if value != expected_price:
         raise ValueError(
             f"Policy value ({value}) doesn't match expected value ({expected_price})"
         )
     return True