def get_current_rate(self, token_amount: TokenAmount) -> EthereumAmount: eth_address = self.get_token_network_address(TokenTicker("ETH")) token_network_address = self.get_token_network_address( token_amount.ticker) expected_rate, slippage_rate = self.network_contract_proxy.functions.getExpectedRate( token_network_address, eth_address, token_amount.as_wei).call() if expected_rate == 0 or slippage_rate == 0: raise ExchangeError( "Trade not possible at the moment due to lack of liquidity") return EthereumAmount(Wei(max(expected_rate, slippage_rate)))
def buy_tokens(self, account: Account, token_amount: TokenAmount, transaction_costs=None): if transaction_costs is None: transaction_costs = dict() if self.network.name not in self.SUPPORTED_NETWORKS: raise ExchangeError( f"{self.name} does not list {token_amount.ticker} on {self.network.name}" ) if not transaction_costs: transaction_costs = self.calculate_transaction_costs( token_amount, account) if transaction_costs is None: raise ExchangeError("Failed to get transactions costs") eth_to_sell = transaction_costs["eth_sold"] exchange_rate = transaction_costs["exchange_rate"] gas_price = transaction_costs["gas_price"] gas = transaction_costs["gas"] eth_address = to_checksum_address( kyber_tokens.get_token_network_address(self.chain_id, TokenTicker("ETH"))) transaction_params = { "from": account.address, "gas_price": gas_price.as_wei, "gas": gas, "value": eth_to_sell.as_wei, } return send_raw_transaction( self.w3, account, self.network_contract_proxy.functions.trade, eth_address, eth_to_sell.as_wei, self.get_token_network_address(token_amount.ticker), account.address, token_amount.as_wei, exchange_rate.as_wei, account.address, **transaction_params, )
def _buy_tokens(self, account: Account, token_amount: TokenAmount, transaction_costs: dict): exchange_rate = transaction_costs["exchange_rate"] eth_to_sell = transaction_costs["eth_sold"] eth_address = self.get_token_network_address(TokenTicker("ETH")) return self._send_buy_transaction( account, transaction_costs, self.network_contract_proxy.functions.trade, eth_address, eth_to_sell.as_wei, self.get_token_network_address(token_amount.ticker), account.address, token_amount.as_wei, exchange_rate.as_wei, account.address, )
def _estimate_gas(self, token_amount: TokenAmount, account: Account, transaction_params: dict, **kw): exchange_rate = kw.get("exchange_rate") if exchange_rate is None: raise ExchangeError( f"An exchange rate is needed to estimate gas for a swap on {self.name}" ) eth_address = self.get_token_network_address(TokenTicker("ETH")) return estimate_gas( self.w3, account, self.network_contract_proxy.functions.trade, eth_address, transaction_params["value"], self.get_token_network_address(token_amount.ticker), account.address, token_amount.as_wei, exchange_rate.as_wei, account.address, **transaction_params, )
def _calculate_transaction_costs(self, token_amount: TokenAmount, account: Account) -> dict: exchange_rate = self.get_current_rate(token_amount) eth_sold = EthereumAmount(token_amount.value * exchange_rate.value * Decimal(1.2)) web3_gas_price = Wei( int(self.w3.eth.generateGasPrice() * self.GAS_PRICE_MARGIN)) kyber_max_gas_price = self.network_contract_proxy.functions.maxGasPrice( ).call() max_gas_price = min(web3_gas_price, kyber_max_gas_price) gas_price = EthereumAmount(Wei(max_gas_price)) log.debug(f"gas price: {gas_price}") token_network_address = self.get_token_network_address( token_amount.ticker) transaction_params = { "from": account.address, "value": eth_sold.as_wei } eth_address = to_checksum_address( kyber_tokens.get_token_network_address(self.chain_id, TokenTicker("ETH"))) gas = estimate_gas( self.w3, account, self.network_contract_proxy.functions.trade, eth_address, eth_sold.as_wei, token_network_address, account.address, token_amount.as_wei, exchange_rate.as_wei, account.address, **transaction_params, ) block = self.w3.eth.getBlock(self.w3.eth.blockNumber) max_gas_limit = Wei(int(block["gasLimit"] * 0.9)) gas_with_margin = Wei(int(gas * self.GAS_PRICE_MARGIN)) gas = min(gas_with_margin, max_gas_limit) log.debug("Gas Limit", gas_with_margin=gas_with_margin, max_gas_limit=max_gas_limit) if max_gas_limit < gas_with_margin: log.debug( f"calculated gas was higher than block's gas limit {max_gas_limit}. Using this limit." ) gas_cost = EthereumAmount(Wei(gas * gas_price.as_wei)) total = EthereumAmount(gas_cost.value + eth_sold.value) log.debug("transaction cost", gas_price=gas_price, gas=gas, eth=eth_sold) return { "gas_price": gas_price, "gas": gas, "eth_sold": eth_sold, "total": total, "exchange_rate": exchange_rate, }