def deposit_service_tokens(w3: Web3, account: Account, token: Erc20Token, amount: Wei): token_proxy = _make_token_proxy(w3=w3, token=token) deposit_proxy = _make_deposit_proxy(w3=w3, token=token) current_deposit_amount = TokenAmount( Wei(deposit_proxy.functions.total_deposit(account.address).call()), token) new_deposit_amount = TokenAmount(amount, token) total_deposit = current_deposit_amount + new_deposit_amount send_raw_transaction( w3, account, token_proxy.functions.approve, deposit_proxy.address, total_deposit.as_wei, gas=GAS_REQUIRED_FOR_APPROVE, ) return send_raw_transaction( w3, account, deposit_proxy.functions.deposit, account.address, total_deposit.as_wei, gas=GAS_REQUIRED_FOR_DEPOSIT, )
def test_udc_deposit_when_already_deposited(self, ws_client, config, settings, unlocked, mock_deposit_service_tokens): required_deposit = Wei(settings.service_token.amount_required) token_balance_patch = patch( "raiden_installer.web.get_token_balance", side_effect=lambda w3, account, token: TokenAmount(10, token)) token_deposit_patch = patch("raiden_installer.web.get_token_deposit", side_effect=lambda w3, account, token: TokenAmount(required_deposit, token)) with token_balance_patch, token_deposit_patch: data = { "method": "udc_deposit", "configuration_file_name": config.file_name, } ws_client.write_message(json.dumps(data)) message = json.loads((yield ws_client.read_message())) assert message["type"] == "status-update" message = json.loads((yield ws_client.read_message())) assert message["type"] == "summary" message = json.loads((yield ws_client.read_message())) assert message["type"] == "redirect" assert message["redirect_url"] == (f"/launch/{config.file_name}") mock_deposit_service_tokens.assert_not_called()
def test_udc_deposit(self, ws_client, config, settings, unlocked, mock_deposit_service_tokens, mock_wait_for_transaction): token_balance_patch = patch( "raiden_installer.web.get_token_balance", side_effect=lambda w3, account, token: TokenAmount(10, token)) token_deposit_patch_web = patch( "raiden_installer.web.get_token_deposit", side_effect=lambda w3, account, token: TokenAmount(0, token)) token_deposit_patch_shared = patch( "raiden_installer.shared_handlers.get_token_deposit", side_effect=lambda w3, account, token: TokenAmount(10, token)) with token_balance_patch, token_deposit_patch_web, token_deposit_patch_shared: data = { "method": "udc_deposit", "configuration_file_name": config.file_name, } ws_client.write_message(json.dumps(data)) for _ in range(3): message = json.loads((yield ws_client.read_message())) assert message["type"] == "status-update" message = json.loads((yield ws_client.read_message())) assert message["type"] == "summary" message = json.loads((yield ws_client.read_message())) assert message["type"] == "redirect" assert message["redirect_url"] == (f"/launch/{config.file_name}") mock_deposit_service_tokens.assert_called_once()
def test_swap(self, ws_client, config, settings, unlocked, mock_get_exchange, mock_deposit_service_tokens, mock_wait_for_transaction): def token_balance(w3, account, token): return (TokenAmount(0, token) if token.ticker == settings.transfer_token.ticker else TokenAmount( 10, token)) eth_balance_patch = patch( "raiden_installer.account.Account.get_ethereum_balance", return_value=EthereumAmount(100)) token_balance_patch = patch("raiden_installer.web.get_token_balance", side_effect=token_balance) total_tokens_patch = patch( "raiden_installer.web.get_total_token_owned", side_effect=lambda w3, account, token: TokenAmount(10, token)) token_deposit_patch = patch( "raiden_installer.shared_handlers.get_token_deposit", side_effect=lambda w3, account, token: TokenAmount(10, token)) with eth_balance_patch, token_balance_patch, total_tokens_patch, token_deposit_patch: mock_exchange = mock_get_exchange()() mock_exchange.calculate_transaction_costs.return_value = { "gas_price": EthereumAmount(Wei(1000000000)), "gas": Wei(500000), "eth_sold": EthereumAmount(0.5), "total": EthereumAmount(0.505), "exchange_rate": EthereumAmount(0.05), } mock_exchange.buy_tokens.return_value = os.urandom(32) mock_exchange.name = "uniswap" data = { "method": "swap", "configuration_file_name": config.file_name, "amount": "10000000000000000000", "token": settings.service_token.ticker, "exchange": "uniswap" } ws_client.write_message(json.dumps(data)) for _ in range(8): message = json.loads((yield ws_client.read_message())) assert message["type"] == "status-update" message = json.loads((yield ws_client.read_message())) assert message["type"] == "summary" message = json.loads((yield ws_client.read_message())) assert message["type"] == "redirect" assert message["redirect_url"] == ( f"/swap/{config.file_name}/{settings.transfer_token.ticker}") mock_exchange.calculate_transaction_costs.assert_called_once() mock_exchange.buy_tokens.assert_called_once() mock_deposit_service_tokens.assert_called_once()
def test_create_swap_amounts(self): swap_amounts = SwapAmounts.from_settings(self.settings) self.assertEqual( swap_amounts.service_token, TokenAmount(Wei(self.settings.service_token.swap_amount), self.service_token) ) self.assertEqual( swap_amounts.transfer_token, TokenAmount(Wei(self.settings.transfer_token.swap_amount), self.transfer_token) )
def post(self, configuration_file_name): configuration_file = RaidenConfigurationFile.get_by_filename( configuration_file_name) account = configuration_file.account try_unlock(account) w3 = make_web3_provider( configuration_file.ethereum_client_rpc_endpoint, account) ex_currency_amt = json_decode(self.request.body) exchange = Exchange.get_by_name(ex_currency_amt["exchange"])(w3=w3) currency = Erc20Token.find_by_ticker(ex_currency_amt["currency"], configuration_file.network) token_amount = TokenAmount(ex_currency_amt["target_amount"], currency) try: exchange_costs = exchange.calculate_transaction_costs( token_amount, account) total_cost = exchange_costs["total"] self.render_json({ "exchange": exchange.name, "currency": currency.ticker, "target_amount": ex_currency_amt["target_amount"], "as_wei": total_cost.as_wei, "formatted": total_cost.formatted, "utc_seconds": int(time.time()), }) except ExchangeError as ex: log.error("There was an error preparing the exchange", exc_info=ex) self.set_status( status_code=409, reason=str(ex), )
def addLiquidity(w3, account, router_proxy, current_rate): max_tokens = get_token_balance(w3, account, WIZ_TOKEN) approve(w3, account, router_proxy.address, max_tokens.as_wei, WIZ_TOKEN) deadline = generateDeadline(w3) eth_amount = EthereumAmount(0.001) token_amount_desired = TokenAmount(eth_amount.value / current_rate.value, WIZ_TOKEN) transaction_params = { "from": account.address, "value": eth_amount.as_wei, "gas_price": w3.eth.generateGasPrice(), "gas": GAS_LIMIT, } tx_hash = send_raw_transaction( w3, account, router_proxy.functions.addLiquidityETH, WIZ_TOKEN.address, token_amount_desired.as_wei, int(token_amount_desired.as_wei * 0.8), int(eth_amount.as_wei * 0.8), account.address, deadline, **transaction_params, ) wait_for_transaction(w3, tx_hash)
def test_buy_tokens(test_account, patch_kyber_support, kyber): knc_balance_before = get_token_balance(kyber.w3, test_account, KNC_TOKEN) buy_amount = TokenAmount(10, KNC_TOKEN) tx_hash = kyber.buy_tokens(test_account, buy_amount) kyber.w3.eth.waitForTransactionReceipt(tx_hash) knc_balance_after = get_token_balance(kyber.w3, test_account, KNC_TOKEN) assert knc_balance_after == knc_balance_before + buy_amount
def get_token_deposit(w3: Web3, account: Account, token: Erc20Token) -> TokenAmount: deposit_proxy = _make_deposit_proxy(w3=w3, token=token) amount = Wei( deposit_proxy.functions.effectiveBalance(account.address).call()) return TokenAmount(amount, token)
def deposit_service_tokens(w3: Web3, account: Account, token: Erc20Token, amount: Wei): token_proxy = _make_token_proxy(w3=w3, token=token) deposit_proxy = _make_deposit_proxy(w3=w3, token=token) current_deposit_amount = TokenAmount( Wei(deposit_proxy.functions.total_deposit(account.address).call()), token) gas_price = Wei(int(w3.eth.generateGasPrice() * GAS_PRICE_MARGIN)) new_deposit_amount = TokenAmount(amount, token) total_deposit = current_deposit_amount + new_deposit_amount nonce = w3.eth.getTransactionCount(account.address) send_raw_transaction( w3, account, token_proxy.functions.approve, deposit_proxy.address, 0, gas=GAS_REQUIRED_FOR_APPROVE, gas_price=gas_price, nonce=nonce, ) send_raw_transaction( w3, account, token_proxy.functions.approve, deposit_proxy.address, total_deposit.as_wei, gas=GAS_REQUIRED_FOR_APPROVE, gas_price=gas_price, nonce=nonce + 1, ) return send_raw_transaction( w3, account, deposit_proxy.functions.deposit, account.address, total_deposit.as_wei, gas=GAS_REQUIRED_FOR_DEPOSIT, gas_price=gas_price, nonce=nonce + 2, )
def test_buy_tokens(funded_account, provide_liquidity, uniswap): w3 = uniswap.w3 wiz_balance_before = get_token_balance(w3, funded_account, WIZ_TOKEN) buy_amount = TokenAmount(1, WIZ_TOKEN) tx_hash = uniswap.buy_tokens(funded_account, buy_amount) wait_for_transaction(w3, tx_hash) wiz_balance_after = get_token_balance(w3, funded_account, WIZ_TOKEN) assert wiz_balance_after == wiz_balance_before + buy_amount
def test_create_required_amounts(self): required_amounts = RequiredAmounts.from_settings(self.settings) self.assertEqual( required_amounts.eth, EthereumAmount(Wei(self.settings.ethereum_amount_required)) ) self.assertEqual( required_amounts.eth_after_swap, EthereumAmount(Wei(self.settings.ethereum_amount_required_after_swap)) ) self.assertEqual( required_amounts.service_token, TokenAmount(Wei(self.settings.service_token.amount_required), self.service_token) ) self.assertEqual( required_amounts.transfer_token, TokenAmount(Wei(self.settings.transfer_token.amount_required), self.transfer_token) )
def run_action_swap_kyber(): account = prompt_account_selection() ethereum_rpc_endpoint = prompt_ethereum_rpc_endpoint_selection() w3 = make_web3_provider(ethereum_rpc_endpoint.url, account) kyber = Exchange.get_by_name("kyber")(w3=w3) amount = Wei(5 * (10**18)) kyber.buy_tokens(account, TokenAmount(amount, Erc20Token.find_by_ticker("RDN"))) return main_prompt()
def test_cannot_buy_without_eth(test_account, patch_kyber_support, kyber): tx = { "to": "0x0000000000000000000000000000000000000000", "from": to_checksum_address(test_account.address), "value": test_account.get_ethereum_balance(kyber.w3).as_wei, "gasPrice": 0, } tx_hash = kyber.w3.eth.sendTransaction(tx) kyber.w3.eth.waitForTransactionReceipt(tx_hash) with pytest.raises(ValueError): kyber.buy_tokens(test_account, TokenAmount(10, KNC_TOKEN))
def test_can_get_costs(self): print("\t".join( [st.rjust(36) for st in ("RDN to buy", "Kyber", "Uniswap")])) for amount in self.TOKEN_AMOUNTS: rdn_amount = TokenAmount(amount, self._get_RDN()) kyber_costs = self.kyber.calculate_transaction_costs( rdn_amount, self.account) uniswap_costs = self.uniswap.calculate_transaction_costs( rdn_amount, self.account) print("\t".join([ st.rjust(36) for st in ( f"{rdn_amount.formatted}", f"{kyber_costs['total'].formatted}", f"{uniswap_costs['total'].formatted}", ) ]))
def provide_liquidity(infura, create_account, uniswap): account = create_account() w3 = make_web3_provider(infura.url, account) fund_account(account, w3) tx_hash = mint_tokens(w3, account, WIZ_TOKEN) wait_for_transaction(w3, tx_hash) current_rate = uniswap.get_current_rate(TokenAmount(1000, WIZ_TOKEN)) router_proxy = w3.eth.contract( abi=uniswap_contracts.UNISWAP_ROUTER02_ABI, address=Uniswap.ROUTER02_ADDRESS, ) addLiquidity(w3, account, router_proxy, current_rate) yield removeLiquidity(w3, account, router_proxy) empty_account(w3, account)
def post(self, configuration_file_name): configuration_file = RaidenConfigurationFile.get_by_filename( configuration_file_name) account = configuration_file.account w3 = make_web3_provider( configuration_file.ethereum_client_rpc_endpoint, account) ex_currency_amt = json_decode(self.request.body) exchange = Exchange.get_by_name(ex_currency_amt["exchange"])(w3=w3) currency = Erc20Token.find_by_ticker(ex_currency_amt["currency"], configuration_file.network) token_amount = TokenAmount(ex_currency_amt["target_amount"], currency) exchange_costs = exchange.calculate_transaction_costs( token_amount, account) total_cost = exchange_costs["total"] self.render_json({ "exchange": exchange.name, "currency": currency.ticker, "target_amount": ex_currency_amt["target_amount"], "as_wei": total_cost.as_wei, "formatted": total_cost.formatted, "utc_seconds": int(time.time()), })
def get_token_balance(w3: Web3, account: Account, token: Erc20Token) -> TokenAmount: token_proxy = _make_token_proxy(w3=w3, token=token) amount = Wei(token_proxy.functions.balanceOf(account.address).call()) return TokenAmount(amount, token)
def setUp(self): self.one_eth = EthereumAmount(1) self.one_rdn = TokenAmount(1, Erc20Token.find_by_ticker("RDN")) self.one_gwei = EthereumAmount(Wei(10 ** 9)) self.almost_one_eth = EthereumAmount("0.875") self.some_wei = EthereumAmount(Wei(50_000))
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 try_unlock(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") transaction_receipt = exchange.buy_tokens( account, token_amount, costs) wait_for_transaction(w3, transaction_receipt) 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) total_service_token_balance = get_total_token_owned( 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) if total_service_token_balance < required.service_token: raise ExchangeError("Exchange was not successful") elif service_token_balance.as_wei > 0: self._send_status_update( f"Making deposit of {service_token_balance.formatted} to the User Deposit Contract" ) self._send_status_update(f"This might take a few minutes") transaction_receipt = deposit_service_tokens( w3=w3, account=account, token=service_token, amount=service_token_balance.as_wei, ) wait_for_transaction(w3, transaction_receipt) service_token_deposited = get_token_deposit( w3=w3, account=account, token=service_token) self._send_status_update( f"Total amount deposited at UDC: {service_token_deposited.formatted}" ) if transfer_token_balance < required.transfer_token: redirect_url = self.reverse_url( "swap", configuration_file.file_name, transfer_token.ticker) next_page = "Moving on to exchanging DAI ..." else: redirect_url = self.reverse_url( "launch", configuration_file.file_name) next_page = "You are ready to launch Raiden! ..." self._send_summary( ["Congratulations! Swap Successful!", next_page], icon=token_ticker) time.sleep(5) self._send_redirect(redirect_url) 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)) redirect_url = self.reverse_url("swap", configuration_file.file_name, token_ticker) next_page = f"Try again to exchange {token_ticker}..." self._send_summary(["Transaction failed", str(exc), next_page], icon="error") time.sleep(5) self._send_redirect(redirect_url)
def test_cannot_buy_without_eth(test_account, provide_liquidity, uniswap): with pytest.raises(ValueError): uniswap.buy_tokens(test_account, TokenAmount(1, WIZ_TOKEN))
def test_cannot_buy_zero_tokens(funded_account, provide_liquidity, uniswap): with pytest.raises(ExchangeError): uniswap.buy_tokens(funded_account, TokenAmount(0, WIZ_TOKEN))
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))
def setUp(self): super().setUp() self.rdn_amount = TokenAmount(75, self._get_RDN())
def token_balance(w3, account, token): return (TokenAmount(0, token) if token.ticker == settings.transfer_token.ticker else TokenAmount( 10, token))
def setUp(self): self.one_eth = EthereumAmount(1) self.two_eth = EthereumAmount(2) self.one_rdn = TokenAmount(1, Erc20Token.find_by_ticker("RDN", "mainnet")) self.one_wiz = TokenAmount(1, Erc20Token.find_by_ticker("WIZ", "goerli"))
def test_cannot_buy_on_unsupported_network(test_account, kyber): with pytest.raises(ExchangeError): kyber.buy_tokens(test_account, TokenAmount(10, KNC_TOKEN))
def test_cannot_buy_zero_tokens(test_account, patch_kyber_support, kyber): with pytest.raises(ExchangeError): kyber.buy_tokens(test_account, TokenAmount(0, KNC_TOKEN))
def get_ethereum_balance(self, w3) -> TokenAmount: return TokenAmount(Wei(w3.eth.getBalance(self.address)), ETH)
def test_cannot_buy_without_enough_liquidity(test_account, patch_kyber_support, kyber): with pytest.raises(ExchangeError): kyber.buy_tokens(test_account, TokenAmount(10000000, KNC_TOKEN))