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);
        }
Exemple #2
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);
        }
Exemple #3
0
        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);
        }
Exemple #4
0
        /// <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);
        }