def wait_for_ethereum_funds(self, w3: Web3, expected_amount: EthereumAmount, timeout: int = WEB3_TIMEOUT) -> EthereumAmount: time_remaining = timeout POLLING_INTERVAL = 1 block_with_balance = math.inf current_block = w3.eth.blockNumber while (current_block < block_with_balance + REQUIRED_BLOCK_CONFIRMATIONS and time_remaining > 0): current_block = w3.eth.blockNumber balance = self.get_ethereum_balance(w3) if balance >= expected_amount: if block_with_balance == math.inf: block_with_balance = w3.eth.blockNumber else: block_with_balance = math.inf time.sleep(POLLING_INTERVAL) time_remaining -= POLLING_INTERVAL log.debug(f"Balance is {balance}") return balance
def send_raw_transaction(w3, account, contract_function, *args, **kw): transaction_params = { "chainId": int(w3.net.version), "nonce": w3.eth.getTransactionCount(account.address), "gasPrice": kw.pop("gas_price", (w3.eth.generateGasPrice())), "gas": kw.pop("gas", None), } transaction_params.update(**kw) if not transaction_params.get("gas"): transaction_params["gas"] = estimate_gas(w3, account, contract_function, *args, **kw) gas_price = transaction_params["gasPrice"] gas = transaction_params["gas"] value = transaction_params.get("value", 0) estimated_cost = EthereumAmount(Wei((gas * gas_price) + value)) log.debug(f"Estimated cost: {estimated_cost.formatted}") result = contract_function(*args) transaction_data = result.buildTransaction(transaction_params) signed = w3.eth.account.signTransaction(transaction_data, account.private_key) tx_hash = w3.eth.sendRawTransaction(signed.rawTransaction) log.debug(f"transaction hash: {tx_hash.hex()}") return w3.eth.waitForTransactionReceipt(tx_hash, timeout=WEB3_TIMEOUT)
def send_raw_transaction(w3, account, contract_function, *args, **kw): transaction_params = { "chainId": w3.eth.chainId, "nonce": w3.eth.getTransactionCount(account.address, "pending"), "gasPrice": kw.pop("gas_price", (w3.eth.generateGasPrice())), "gas": kw.pop("gas", None), "from": to_checksum_address(kw.pop("from", account.address)) } transaction_params.update(**kw) if not transaction_params.get("gas"): transaction_params["gas"] = estimate_gas(w3, account, contract_function, *args, **kw) gas_price = transaction_params["gasPrice"] gas = transaction_params["gas"] value = transaction_params.get("value", 0) estimated_cost = EthereumAmount(Wei((gas * gas_price) + value)) log.debug(f"Estimated cost: {estimated_cost.formatted}") result = contract_function(*args) transaction_data = result.buildTransaction(transaction_params) signed = w3.eth.account.signTransaction(transaction_data, account.private_key) tx_hash = w3.eth.sendRawTransaction(signed.rawTransaction) log.debug(f"transaction hash: {tx_hash.hex()}") return tx_hash
def wait_for_ethereum_funds( self, w3: Web3, expected_amount: TokenAmount, timeout: int = 300 ) -> TokenAmount: time_remaining = timeout POLLING_INTERVAL = 1 balance = self.get_ethereum_balance(w3) while balance < expected_amount and time_remaining > 0: balance = self.get_ethereum_balance(w3) time.sleep(POLLING_INTERVAL) time_remaining -= POLLING_INTERVAL log.debug(f"Balance is {balance}") return balance
def wait_for_transaction(web3: Web3, transaction_receipt: Dict[str, Any]) -> None: if "blockNumber" not in transaction_receipt: raise KeyError("blockNumber not in transaction receipt.") block_with_transaction = transaction_receipt["blockNumber"] current_block = web3.eth.blockNumber while current_block < block_with_transaction: log.debug("wait for block with transaction to be fetched") current_block = web3.eth.blockNumber time.sleep(1)
def _ensure_ethereum_funds(self, ethereum_amount: EthereumAmount): w3 = self._get_web3() current_balance = self.account.get_ethereum_balance(w3) log.debug(f"Current balance: {current_balance.formatted}") network = self._get_network() if current_balance < ethereum_amount: needed_funds = EthereumAmount(Wei(ethereum_amount.as_wei - current_balance.as_wei)) print( f"Please send at least {needed_funds.formatted} to {self.account.address} on " f"{network.capitalized_name}" ) return self._wait_for_ethereum_funds(ethereum_amount=needed_funds)
def wait_for_transaction(w3: Web3, transaction_hash) -> None: log.debug("wait for block with transaction to be fetched") time_start = time.time() block_with_transaction = math.inf current_block = w3.eth.blockNumber while current_block < block_with_transaction + REQUIRED_BLOCK_CONFIRMATIONS: if time.time() - time_start >= WEB3_TIMEOUT: raise TransactionTimeoutError( f"Tx with hash {transaction_hash} was not found after {WEB3_TIMEOUT} seconds" ) try: tx_receipt = w3.eth.getTransactionReceipt(transaction_hash) block_with_transaction = tx_receipt["blockNumber"] except TransactionNotFound: pass current_block = w3.eth.blockNumber time.sleep(1)
def _execute_rdn_swap(self, exchange): log.debug(f"RDN balance is {self.rdn.balance}") swap_costs = exchange.calculate_transaction_costs(self.rdn_amount) exchange_rate = swap_costs["exchange_rate"] log.debug(f"{exchange.name} exchange rate: {exchange_rate.formatted} maximum") self._ensure_ethereum_funds(swap_costs["total"]) exchange.buy_tokens(self.rdn_amount) log.debug(f"RDN balance after swap is {self.rdn.balance} RDN")
def _deposit(self): network = self._get_network() w3 = self._get_web3() ethereum_balance = self.account.get_ethereum_balance(w3=w3) log.debug(f"Ethereum Balance: {ethereum_balance.formatted}") if not ethereum_balance.as_wei: network.fund(self.account) token = Erc20Token.find_by_ticker(settings.service_token.ticker) token_balance = get_token_balance(w3, self.account, token) log.debug(f"Token Balance: {token_balance.formatted}") if not token_balance.as_wei: mint_tokens(w3, self.account, token) token_balance = get_token_balance(w3, self.account, token) log.debug(f"Tokens minted. New Balance: {token_balance.formatted}") deposit_service_tokens(w3, self.account, token, token_balance.as_wei)
def _run_swap(self, **kw): try: configuration_file_name = kw.get("configuration_file_name") exchange_name = kw["exchange"] token_amount = kw["amount"] token_ticker = kw["token"] except (ValueError, KeyError, TypeError) as exc: self._send_error_message(f"Invalid request: {exc}") return try: configuration_file = RaidenConfigurationFile.get_by_filename( configuration_file_name) network_name = configuration_file.network.name form = TokenExchangeForm({ "network": [network_name], "exchange": [exchange_name], "token_amount": [token_amount], "token_ticker": [token_ticker], }) if form.validate(): account = configuration_file.account w3 = make_web3_provider( configuration_file.ethereum_client_rpc_endpoint, account) token = Erc20Token.find_by_ticker(form.data["token_ticker"], network_name) token_amount = TokenAmount(Wei(form.data["token_amount"]), token) exchange = Exchange.get_by_name(form.data["exchange"])(w3=w3) self._send_status_update(f"Starting swap at {exchange.name}") costs = exchange.calculate_transaction_costs( token_amount, account) needed_funds = costs["total"] exchange_rate = costs["exchange_rate"] balance_before_swap = account.get_ethereum_balance(w3) if needed_funds > balance_before_swap: raise ValueError(( f"Not enough ETH. {balance_before_swap.formatted} available, but " f"{needed_funds.formatted} needed")) self._send_status_update( (f"Best exchange rate found at {exchange.name}: " f"{exchange_rate} / {token_amount.ticker}")) self._send_status_update( f"Trying to acquire {token_amount} at this rate") self._send_status_update( f"maximal costs estimated: {needed_funds} ") transaction_receipt = exchange.buy_tokens( account, token_amount, costs) block_with_transaction = transaction_receipt["blockNumber"] current_block = w3.eth.blockNumber while current_block < block_with_transaction: log.debug("wait for block with transaction to be fetched") current_block = w3.eth.blockNumber time.sleep(1) token_balance = get_token_balance(w3, account, token) balance_after_swap = account.get_ethereum_balance(w3) actual_total_costs = balance_before_swap - balance_after_swap self._send_status_update( f"Swap complete. {token_balance.formatted} available") self._send_status_update(f"Actual costs: {actual_total_costs}") required = RequiredAmounts.for_network(network_name) service_token = Erc20Token.find_by_ticker( required.service_token.ticker, network_name) service_token_balance = get_token_balance( w3, account, service_token) transfer_token = Erc20Token.find_by_ticker( required.transfer_token.ticker, network_name) transfer_token_balance = get_token_balance( w3, account, transfer_token) time.sleep(2) if service_token_balance < required.service_token: self._send_redirect( self.reverse_url("swap", configuration_file.file_name, service_token.ticker)) elif transfer_token_balance < required.transfer_token: self._send_redirect( self.reverse_url("swap", configuration_file.file_name, transfer_token.ticker)) else: self._send_redirect( self.reverse_url("launch", configuration_file.file_name)) else: for key, error_list in form.errors.items(): error_message = f"{key}: {'/'.join(error_list)}" self._send_error_message(error_message) except (json.decoder.JSONDecodeError, KeyError, ExchangeError, ValueError) as exc: self._send_error_message(str(exc))