def get_current_rate(self, token_amount: TokenAmount) -> EthereumAmount: eth_address = to_checksum_address( kyber_tokens.get_token_network_address(self.chain_id, TokenTicker("ETH")) ) token_network_address = to_checksum_address( kyber_tokens.get_token_network_address(self.chain_id, 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 get_token_network_address(self, ticker: TokenTicker): try: token_network_address = kyber_tokens.get_token_network_address( self.chain_id, ticker) return token_network_address and to_checksum_address( token_network_address) except KeyError: return None
def get_token_network_address(self, ticker: TokenTicker) -> Address: try: token_network_address = to_canonical_address( kyber_tokens.get_token_network_address(self.chain_id, ticker)) return token_network_address except (KeyError, TypeError) as exc: raise ExchangeError( f"{self.name} is not listing {ticker}") from exc
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 _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, }