public async Task TestCashoutTokens() { var colorCoin = await _coinRepository.GetCoinByAddress(_tokenAdapterAddress); await CashinTokens(_externalTokenAddress, _clientTokenTransferAddress, new BigInteger(100), _tokenAdapterAddress, _clientA); var transferUser = await _transferContractService.GetTransferAddressUser(colorCoin.AdapterAddress, _clientTokenTransferAddress); var oldBalance = await _transferContractService.GetBalanceOnAdapter(colorCoin.AdapterAddress, _clientA); Assert.AreEqual(_clientA.ToLower(), transferUser); var guid = Guid.NewGuid(); var externalSign = await _exchangeService.GetSign(guid, _tokenAdapterAddress, _clientA, _clientA, oldBalance); var cashout = await _exchangeService.CashOut(guid, colorCoin.AdapterAddress, _clientA, _clientA, oldBalance, externalSign); while (await _transactionService.GetTransactionReceipt(cashout) == null) { await Task.Delay(100); } Assert.IsTrue(await _transactionService.IsTransactionExecuted(cashout, Constants.GasForCoinTransaction)); var currentBalance = await _transferContractService.GetBalanceOnAdapter(colorCoin.AdapterAddress, _clientA); var newBalance = await _ercService.GetBalanceForExternalTokenAsync(_clientA, _externalTokenAddress); Assert.IsTrue(oldBalance <= newBalance); Assert.IsTrue(currentBalance == 0); }
public async Task <string> IssueTokensAsync(string externalTokenAddress, string toAddress, BigInteger amount) { var externalToken = await _tokenRepository.GetAsync(externalTokenAddress); if (externalToken == null) { throw new ClientSideException(ExceptionType.WrongParams, $"External Token With address {externalTokenAddress} does not exist!"); } //0x2dcc0430f6b9a40fd54f7d99f8a73b7d2d84b1fa var initialSupply = BigInteger.Parse(externalToken.InitialSupply); if (initialSupply != 0) { BigInteger balance = await _ercInterfaceService.GetBalanceForExternalTokenAsync(_settings.EthereumMainAccount, externalTokenAddress); if (balance < amount) { string message = $"Can't issue more tokens. Current balance is {balance}, issue amount is {amount}."; await _logger.WriteInfoAsync("ExternalTokenService", "IssueTokens", "", message, DateTime.UtcNow); throw new ClientSideException(ExceptionType.WrongParams, message); } } string trHash = await _ercInterfaceService.Transfer(externalTokenAddress, _settings.EthereumMainAccount, toAddress, amount); await _logger.WriteInfoAsync("ExternalTokenService", "IssueTokens", "", $"Issued to address {toAddress} {amount.ToString()} tokens from {externalTokenAddress}", DateTime.UtcNow); return(trHash); }
public async Task <BigInteger> GetBalance(string transferContractAddress) { var transferContract = await _transferContractRepository.GetAsync(transferContractAddress); if (transferContract == null) { throw new ClientSideException(ExceptionType.WrongParams, $"Transfer contract with {transferContractAddress} address does not exist"); } BigInteger balance; if (!transferContract.ContainsEth) { balance = await _ercInterfaceService.GetBalanceForExternalTokenAsync(transferContract.ContractAddress, transferContract.ExternalTokenAddress); } else { balance = await _paymentService.GetAddressBalanceInWei(transferContract.ContractAddress); } return(balance); }
/// <param name="depositContractAddress"></param> /// <param name="erc20TokenAddress"></param> /// <param name="destinationAddress"></param> /// <returns>TransactionHash</returns> public async Task <string> RecievePaymentFromDepositContract(string depositContractAddress, string erc20TokenAddress, string destinationAddress) { var depositContract = await _contractRepository.GetByContractAddress(depositContractAddress); if (depositContract == null) { throw new ClientSideException(ExceptionType.WrongParams, $"DepositContractAddress {depositContractAddress} does not exist"); } var userWallet = await TransferWalletSharedService.GetUserTransferWalletAsync(_userTransferWalletRepository, depositContractAddress, erc20TokenAddress, depositContract.UserAddress); if (userWallet != null && !string.IsNullOrEmpty(userWallet.LastBalance)) { throw new ClientSideException(ExceptionType.TransferInProcessing, $"Transfer from {depositContractAddress} was started before wait for it to complete"); } var balance = await _ercInterfaceService.GetBalanceForExternalTokenAsync(depositContractAddress, erc20TokenAddress); if (balance == 0) { throw new ClientSideException(ExceptionType.NotEnoughFunds, $"No tokens detected at deposit address {depositContractAddress}"); } var guidStr = Guid.NewGuid().ToString(); var message = new Lykke.Service.EthereumCore.Core.Messages.LykkePay.LykkePayErc20TransferMessage() { OperationId = guidStr }; var existingOperation = await _operationsRepository.GetAsync(guidStr); if (existingOperation != null) { throw new ClientSideException(ExceptionType.EntityAlreadyExists, "Try again later"); } var transactionSenderAddress = _appSettings.LykkePay.LykkePayAddress; var estimationResult = await Erc20SharedService.EstimateDepositTransferAsync(_web3, _settings.Erc20DepositContract.Abi, transactionSenderAddress, depositContractAddress, erc20TokenAddress, destinationAddress); if (!estimationResult) { throw new ClientSideException(ExceptionType.WrongDestination, $"Can't estimate transfer {depositContractAddress}, {erc20TokenAddress}, {destinationAddress}"); } await _operationsRepository.SaveAsync(new HotWalletOperation() { Amount = balance, FromAddress = depositContractAddress, OperationId = guidStr, OperationType = HotWalletOperationType.Cashin, ToAddress = destinationAddress, TokenAddress = erc20TokenAddress }); await _transferQueue.PutRawMessageAsync(Newtonsoft.Json.JsonConvert.SerializeObject(message)); await TransferWalletSharedService.UpdateUserTransferWalletAsync(_userTransferWalletRepository, depositContractAddress, erc20TokenAddress, depositContract.UserAddress, balance.ToString()); return(guidStr); }
public async Task TransferToCoinContract(Erc20DepositContractTransaction contractTransferTr) { try { var userAddress = contractTransferTr.UserAddress; if (string.IsNullOrEmpty(userAddress) || userAddress == Constants.EmptyEthereumAddress) { await UpdateUserTransferWallet(contractTransferTr); await _logger.WriteInfoAsync("TransferContractTransactionService", "TransferToCoinContract", "", $"Can't cashin: there is no user assigned to the transfer contract {contractTransferTr.ContractAddress}", DateTime.UtcNow); return; } var tokenAddress = contractTransferTr.TokenAddress; var contractAddress = await _erc20DepositContractService.GetContractAddress(contractTransferTr.UserAddress); var balance = await _ercInterfaceService.GetBalanceForExternalTokenAsync(contractTransferTr.ContractAddress, contractTransferTr.TokenAddress); if (balance == 0) { await UpdateUserTransferWallet(contractTransferTr); await _logger.WriteInfoAsync("TransferContractTransactionService", "TransferToCoinContract", "", $"Can't cashin: there is no funds on the transfer contract {contractTransferTr.ContractAddress}", DateTime.UtcNow); return; } var opId = $"HotWalletCashin-{Guid.NewGuid().ToString()}"; string transactionHash = null; try { transactionHash = await _hotWalletService.StartCashinAsync(new HotWalletOperation() { Amount = balance, FromAddress = contractAddress, OperationId = opId, ToAddress = _hotWalletAddress, TokenAddress = tokenAddress, OperationType = HotWalletOperationType.Cashin, }); } catch (Exception exc) { await UpdateUserTransferWallet(contractTransferTr); return; } await _userPaymentHistoryRepository.SaveAsync(new UserPaymentHistory() { Amount = balance.ToString(), ToAddress = contractAddress, AdapterAddress = $"HotWallet-Token-{tokenAddress}", CreatedDate = DateTime.UtcNow, Note = $"Cashin from erc20 deposit contract {contractAddress}", TransactionHash = transactionHash, UserAddress = contractTransferTr.UserAddress }); //await UpdateUserTransferWallet(contractTransferTr); await _logger.WriteInfoAsync(nameof(Erc20DepositTransactionService), nameof(TransferToCoinContract), "", $"Transfered {balance} from erc 20 deposit contract to {_hotWalletAddress} by transaction {transactionHash}. " + $"Receiver = {userAddress}"); } catch (Exception e) { await _logger.WriteErrorAsync(nameof(Erc20DepositTransactionService), nameof(TransferToCoinContract), $"{contractTransferTr.ContractAddress} - erc20 - {contractTransferTr.TokenAddress} - {contractTransferTr.Amount}", e); throw; } }
public async Task Execute() { IList <Erc20Token> erc20Tokens = null; try { var tradeableAssets = await _assetsService.AssetGetAllAsync(); var supportedTokenAssets = tradeableAssets.Where(asset => asset.Type == Lykke.Service.Assets.Client.Models.AssetType.Erc20Token && asset.IsTradable); var assetIds = supportedTokenAssets.Select(x => x.Id); var tradableTokens = await _assetsService.Erc20TokenGetBySpecificationAsync(new Lykke.Service.Assets.Client.Models.Erc20TokenSpecification() { Ids = assetIds.ToList(), }); erc20Tokens = tradableTokens?.Items; } catch (Exception exc) { await _logger.WriteErrorAsync(nameof(Erc20DepositMonitoringContracts), nameof(Execute), "Assets Service unavailable", exc, DateTime.UtcNow); return; } if (erc20Tokens != null && !erc20Tokens.Any()) { await _logger.WriteWarningAsync(nameof(Erc20DepositMonitoringContracts), nameof(Execute), "", "No tokens available for trade", DateTime.UtcNow); return; } await _erc20DepositContractService.ProcessAllAsync(async (item) => { try { //Check that deposit contract assigned to user if (!string.IsNullOrEmpty(item.UserAddress)) { // null - means we ask for all balances on current address var tokenBalances = await _erc20BalanceService.GetBalancesForAddress(item.ContractAddress, new string[0]); if (tokenBalances != null) { foreach (var tokenBalance in tokenBalances) { string tokenAddress = tokenBalance.Erc20TokenAddress?.ToLower(); string formattedAddress = _userTransferWalletRepository.FormatAddressForErc20(item.ContractAddress, tokenAddress); IUserTransferWallet wallet = await _userTransferWalletRepository.GetUserContractAsync(item.UserAddress, formattedAddress); if (wallet == null || string.IsNullOrEmpty(wallet.LastBalance) || wallet.LastBalance == "0") { BigInteger balance = await _ercInterfaceService.GetBalanceForExternalTokenAsync(item.ContractAddress, tokenAddress); if (balance > 0) { await _userTransferWalletRepository.ReplaceAsync(new UserTransferWallet() { LastBalance = balance.ToString(), TransferContractAddress = formattedAddress, UserAddress = item.UserAddress, UpdateDate = DateTime.UtcNow }); await _erc20DepositTransactionService.PutContractTransferTransaction(new Erc20DepositContractTransaction() { Amount = balance.ToString(), UserAddress = item.UserAddress, TokenAddress = tokenAddress, ContractAddress = item.ContractAddress, CreateDt = DateTime.UtcNow, }); await _logger.WriteInfoAsync(nameof(Erc20DepositMonitoringContracts), nameof(Execute), "", $"Balance on transfer address - {item.ContractAddress} is {balance} (Tokens of {tokenBalance.Erc20TokenAddress})" + $" transfer belongs to user {item.UserAddress}", DateTime.UtcNow); } } } } } else { //TODO //Notify That deposit contract does not have a user; } } catch (Exception e) { await _logger.WriteErrorAsync(nameof(Erc20DepositMonitoringContracts), nameof(Execute), "", e, DateTime.UtcNow); } }); }
public async Task TransferToCoinContract(Erc20DepositContractTransaction contractTransferTr) { try { var tokenAddress = contractTransferTr.TokenAddress; var contractAddress = contractTransferTr.ContractAddress; var userAddress = contractTransferTr.UserAddress; if (string.IsNullOrEmpty(userAddress) || userAddress == Constants.EmptyEthereumAddress) { await UpdateUserTransferWallet(contractTransferTr); await _logger.WriteInfoAsync("TransferContractTransactionService", "TransferToCoinContract", "", $"Can't cashin: there is no user assigned to the transfer contract {contractTransferTr.ContractAddress}", DateTime.UtcNow); return; } if (string.IsNullOrEmpty(contractAddress) || contractAddress == Constants.EmptyEthereumAddress) { await UpdateUserTransferWallet(contractTransferTr); await _logger.WriteInfoAsync("TransferContractTransactionService", "TransferToCoinContract", "", $"Can't cashin: there is no contract address in message{contractTransferTr?.ToJson()}", DateTime.UtcNow); return; } var balance = await _ercInterfaceService.GetBalanceForExternalTokenAsync(contractAddress, contractTransferTr.TokenAddress); if (balance == 0) { await UpdateUserTransferWallet(contractTransferTr); await _logger.WriteInfoAsync("TransferContractTransactionService", "TransferToCoinContract", "", $"Can't cashin: there is no funds on the transfer contract {contractAddress}", DateTime.UtcNow); return; } var opId = $"HotWalletCashin-{Guid.NewGuid().ToString()}"; string transactionHash = null; try { transactionHash = await _hotWalletService.StartCashinAsync(new HotWalletOperation() { Amount = balance, FromAddress = contractAddress, OperationId = opId, ToAddress = _hotWalletAddress, TokenAddress = tokenAddress, OperationType = HotWalletOperationType.Cashin, }); await _cointTransactionQueue.PutRawMessageAsync(JsonConvert.SerializeObject(new CoinTransactionMessage() { TransactionHash = transactionHash })); } catch (ClientSideException clientSideExc) { var context = new { obj = contractTransferTr.ToJson(), exc = $"{clientSideExc.ExceptionType} {clientSideExc.Message} {clientSideExc.StackTrace}" }.ToJson(); await _logger.WriteInfoAsync(nameof(Erc20DepositTransactionService), nameof(TransferToCoinContract), $"{context}"); await UpdateUserTransferWallet(contractTransferTr); //Redirect issues to dedicated slack channel await _slackNotifier.ErrorAsync($"{nameof(Erc20DepositTransactionService)} can't start cashin {context}"); return; } catch (Exception exc) { await _logger.WriteErrorAsync(nameof(Erc20DepositTransactionService), nameof(TransferToCoinContract), $"{contractTransferTr.ToJson()}", exc); await UpdateUserTransferWallet(contractTransferTr); return; } await _userPaymentHistoryRepository.SaveAsync(new UserPaymentHistory() { Amount = balance.ToString(), ToAddress = contractAddress, AdapterAddress = $"HotWallet-Token-{tokenAddress}", CreatedDate = DateTime.UtcNow, Note = $"Cashin from erc20 deposit contract {contractAddress}", TransactionHash = transactionHash, UserAddress = contractTransferTr.UserAddress }); //await UpdateUserTransferWallet(contractTransferTr); await _logger.WriteInfoAsync(nameof(Erc20DepositTransactionService), nameof(TransferToCoinContract), "", $"Transfered {balance} from erc 20 deposit contract to {_hotWalletAddress} by transaction {transactionHash}. " + $"Receiver = {userAddress}"); } catch (Exception e) { await _logger.WriteErrorAsync(nameof(Erc20DepositTransactionService), nameof(TransferToCoinContract), $"{contractTransferTr.ContractAddress} - erc20 - {contractTransferTr.TokenAddress} - {contractTransferTr.Amount}", e); throw; } }
/// <param name="depositContractAddress"></param> /// <param name="erc20TokenAddress"></param> /// <param name="destinationAddress"></param> /// <returns>TransactionHash</returns> public async Task <string> RecievePaymentFromDepositContractAsync(string depositContractAddress, string erc20TokenAddress, string destinationAddress, string tokenAmount) { var depositContract = await _contractRepository.GetByContractAddress(depositContractAddress); if (depositContract == null) { throw new ClientSideException(ExceptionType.WrongParams, $"DepositContractAddress {depositContractAddress} does not exist"); } var amount = System.Numerics.BigInteger.Parse(tokenAmount); var balance = await _ercInterfaceService.GetBalanceForExternalTokenAsync(depositContractAddress, erc20TokenAddress); if (balance == 0 || amount > balance) { throw new ClientSideException(ExceptionType.NotEnoughFunds, $"Not enough tokens to proceed with withdrawal detected at deposit address {depositContractAddress}. " + $"Current balance: {balance}"); } var guidStr = Guid.NewGuid().ToString(); var message = new Lykke.Service.EthereumCore.Core.Messages.LykkePay.LykkePayErc20TransferMessage() { OperationId = guidStr }; var existingOperation = await _operationsRepository.GetAsync(guidStr); if (existingOperation != null) { throw new ClientSideException(ExceptionType.EntityAlreadyExists, "Try again later"); } var transactionSenderAddress = _appSettings.Airlines.AirlinesAddress; var estimationResult = await Erc223SharedService.EstimateDepositTransferAsync(_web3, _appSettings.EthereumCore.Erc223DepositContract.Abi, transactionSenderAddress, depositContractAddress, erc20TokenAddress, destinationAddress, amount); if (!estimationResult) { throw new ClientSideException(ExceptionType.WrongDestination, $"Can't estimate transfer {depositContractAddress}, {erc20TokenAddress}, {destinationAddress}"); } await _operationsRepository.SaveAsync(new HotWalletOperation() { Amount = amount, FromAddress = depositContractAddress, OperationId = guidStr, OperationType = HotWalletOperationType.Cashin, ToAddress = destinationAddress, TokenAddress = erc20TokenAddress }); await _transferQueue.PutRawMessageAsync(Newtonsoft.Json.JsonConvert.SerializeObject(message)); return(guidStr); }