private async Task <bool> GetAccount(Message m) // verify that user is a part of the master chat { var text = m.Text?.Trim(); var user = m.From; var args = text.Split(" "); var cliArgs = CLIHelper.GetNamedArguments(args); var token = args.TryGetValueOrDefault(2)?.TrimStartSingle("$"); // $ATOM var baseName = token?.ToUpper(); if (token.IsNullOrEmpty()) { token = cliArgs.GetValueOrDefault("token"); } token = token?.ToLower(); var propsVar = Environment.GetEnvironmentVariable($"{token?.ToLower()}_PROPS"); TokenProps props; if (!propsVar.IsNullOrEmpty()) { props = propsVar.JsonDeserialize <TokenProps>(); } else { props = new TokenProps(); } props.name = token; var account = args.TryGetValueOrDefault(3)?.Trim()?.ToLower(); props.memo = cliArgs.GetValueOrDefault("memo") ?? ""; if (!props.memo.IsNullOrEmpty() && props.memo.Length > 256) { await _TBC.SendTextMessageAsync(text : $"*memo* can't have more then 256 characters, but was {props.memo.Length} characters.\nCheck description to see allowed parameters.", chatId : new ChatId(m.Chat.Id), replyToMessageId : m.MessageId, parseMode : Telegram.Bot.Types.Enums.ParseMode.Markdown); return(true); } if (props.index < 0 || props.index > 99999999) { props.index = (cliArgs.GetValueOrDefault("index")).ToIntOrDefault(BitcoinEx.GetCoinIndex(baseName)); } if (props.index < 0 || props.index > 99999999) // vlaidate coin index { await _TBC.SendTextMessageAsync(text : $"*index* flag `{props.index}` is invalid.\nCheck description to see allowed parameters.", chatId : new ChatId(m.Chat.Id), replyToMessageId : m.MessageId, parseMode : Telegram.Bot.Types.Enums.ParseMode.Markdown); return(true); } if (props.prefix.IsNullOrWhitespace()) { props.prefix = cliArgs.GetValueOrDefault("prefix"); } if (props.prefix.IsNullOrWhitespace()) // vlaidate address prefix { await _TBC.SendTextMessageAsync(text : $"*prefix* flag `{props.address ?? "undefined"}` is invalid.\nCheck description to see allowed parameters.", chatId : new ChatId(m.Chat.Id), replyToMessageId : m.MessageId, parseMode : Telegram.Bot.Types.Enums.ParseMode.Markdown); return(true); } var ua = await GetUserAccount(user.Id); var acc = new AsmodatStandard.Cryptography.Cosmos.Account(props.prefix, (uint)props.index); acc.InitializeWithMnemonic(ua.GetSecret()); var cosmosAdress = acc.CosmosAddress; if (account.EquailsAny(StringComparison.OrdinalIgnoreCase, "address", "account", "acc", "addr", "a", "deposit", "d", "adr", "adres", "adress", "addres")) { await _TBC.SendTextMessageAsync(chatId : m.Chat, $"{user.GetMarkDownUsername()} Public Address Is:\n`{cosmosAdress}`", replyToMessageId : m.MessageId, parseMode : Telegram.Bot.Types.Enums.ParseMode.Markdown); return(true); } else if (account.EquailsAny(StringComparison.OrdinalIgnoreCase, "balance")) { var lcd = cliArgs.GetValueOrDefault("lcd"); if (!lcd.IsNullOrWhitespace()) { props.lcd = lcd; } var client = new CosmosHub(lcd: props.lcd, timeoutSeconds: _cosmosHubClientTimeout); node_info nodeInfo; try { nodeInfo = await client.GetNodeInfo(); } catch { await _TBC.SendTextMessageAsync(text : $"*lcd* flag `{props.lcd ?? "undefined"}` is invalid or node can NOT be reached.\nCheck description to see allowed parameters.", chatId : new ChatId(m.Chat.Id), replyToMessageId : m.MessageId, parseMode : Telegram.Bot.Types.Enums.ParseMode.Markdown); return(true); } var network = cliArgs.GetValueOrDefault("network"); if (network.IsNullOrWhitespace()) { network = nodeInfo?.network; } if (network.IsNullOrWhitespace()) { network = props.network; } props.network = network; if (props.network.IsNullOrWhitespace() || props.network.Length <= 1) { await _TBC.SendTextMessageAsync(text : $"*network* flag `{props.network ?? "undefined"}` is invalid.\nCheck description to see allowed parameters.", chatId : new ChatId(m.Chat.Id), replyToMessageId : m.MessageId, parseMode : Telegram.Bot.Types.Enums.ParseMode.Markdown); return(true); } props.denom = cliArgs.GetValueOrDefault("denom") ?? props.denom ?? props.name.ToLower(); var fromAccountInfo = await client.GetAccount(account : cosmosAdress); var fromAccountBalance = fromAccountInfo?.coins?.FirstOrDefault(x => x?.denom?.ToLower() == props.denom.ToLower()); await _TBC.SendTextMessageAsync(chatId : m.Chat, $"Address: `{cosmosAdress}`\n" + $"Amount: `{fromAccountBalance?.amount ?? "0"} {fromAccountBalance?.denom ?? props.denom}`\n" + $"Network: `{props.network}`", replyToMessageId : m.MessageId, parseMode : Telegram.Bot.Types.Enums.ParseMode.Markdown); return(true); } else { await _TBC.SendTextMessageAsync(text : $"Command `{account ?? "undefined"}` is invalid.\nCheck description to see allowed parameters.", chatId : new ChatId(m.Chat.Id), replyToMessageId : m.MessageId, parseMode : Telegram.Bot.Types.Enums.ParseMode.Markdown); return(false); } }
private async Task TransactionProcessMessage(Message m) { if (!await this.CheckMasterChatMembership(m)) { return; } var chat = m.Chat; var from = m.From; var to = m.ReplyToMessage?.From; var text = (m.Text?.Trim() ?? "").Trim('\'', '"', '`', '*', '[', ']'); var args = text.Split(" "); var cliArgs = CLIHelper.GetNamedArguments(args); var toUsername = args.TryGetValueOrDefault(1, "").Trim(' ', '\'', '"', '*', '`', '[', ']'); var toAddress = (cliArgs.GetValueOrDefault("address") ?? args.TryGetValueOrDefault(3, "")).Trim(' ', '\'', '"', '*', '`', '[', ']'); if (!Bech32Ex.TryDecode(toAddress, out var hrp, out var addrBytes)) { toAddress = null; } if (!toAddress.IsNullOrWhitespace()) { to = null; } if (toAddress.IsNullOrWhitespace() && to == null && toUsername.StartsWith("@")) { to = await TryGetUserByUsername(toUsername.TrimStart("@")); } if (to == null && toAddress.IsNullOrWhitespace()) { await _TBC.SendTextMessageAsync(chatId : chat, $"Transaction can't be send.\n" + $"User @{toUsername ?? "null"} is not an active member of *{chat.Title}* group, `address` property is invalid or you responded to the old message that bot can't see.\n" + $"Try a 'reply to option', for example: Reply to -> `tip <amount> $<token_name>` rather then `tx @<username> <amount> $<token>` or specify `address` argument, e.g: `tx <amount> $<token> --address=<publicKey>`.", replyToMessageId : m.MessageId, parseMode : Telegram.Bot.Types.Enums.ParseMode.Markdown); return; } var token = (args.FirstOrDefault(x => x.StartsWith("$")).Trim() ?? cliArgs.GetValueOrDefault("token") ?? "").TrimStart("$"); var propsVar = Environment.GetEnvironmentVariable($"{token?.ToLower()}_PROPS"); TokenProps props; if (!propsVar.IsNullOrEmpty()) { props = propsVar.JsonDeserialize <TokenProps>(); } else { props = new TokenProps(); } props.denom = cliArgs.GetValueOrDefault("denom") ?? props.denom ?? token.ToLower(); props.amount = (args.FirstOrDefault(x => x.Trim().IsDigits()).Trim() ?? cliArgs.GetValueOrDefault("amount")).ToLongOrDefault(0); props.fees = cliArgs.GetValueOrDefault("fees", props.fees.ToString()).ToLongOrDefault(0); if (props.amount < 0 || props.fees < 0) { await _TBC.SendTextMessageAsync(chatId : chat, $"`amount` or `fees` or `token` were not specified.", replyToMessageId : m.MessageId, parseMode : ParseMode.Markdown); return; } string wallet = null; try { var account = await GetUserAccount(from); var acc = new AsmodatStandard.Cryptography.Cosmos.Account(props.prefix, (uint)props.index); acc.InitializeWithMnemonic(account.GetSecret()); var client = new CosmosHub(lcd: props.lcd, timeoutSeconds: _cosmosHubClientTimeout); var fromAccountInfo = await client.GetAccount(account : acc.CosmosAddress); var fromAccountBalance = fromAccountInfo?.coins?.FirstOrDefault(x => x?.denom?.ToLower() == props.denom.ToLower()); var fromBalance = (fromAccountBalance?.amount).ToBigIntOrDefault(0); wallet = $"Wallet: `{fromBalance} {props.denom}`"; } catch { } var optionsKeyboard = new InlineKeyboardMarkup(new[] { new [] // first row { InlineKeyboardButton.WithCallbackData("YES, SEND", $"{OptionKeys.txConfirm.ToString()} {from.Id} {to?.Id.ToString() ?? toAddress}"), InlineKeyboardButton.WithCallbackData("NO!, CANCEL", OptionKeys.txCancel.ToString()) }, }); string toConfirm = (to == null ? $"To: `{toAddress}`\n" : $"To: {to.GetMarkDownUsername()} (`{to.Id}`)\n"); await _TBC.SendTextMessageAsync(chatId : chat, $"*[ CONFIRM REQUEST {m.MessageId} ]*\n" + $"Action: `transfer`\n" + $"From: {from.GetMarkDownUsername()} (`{from.Id}`)\n" + toConfirm + $"Amount: `{props.amount} {props.denom}`\n" + $"Fees: `{props.fees} {props.denom}`\n" + wallet, replyToMessageId : m.MessageId, replyMarkup : optionsKeyboard, parseMode : ParseMode.Markdown); }
private async Task TransferProcessAcceptCallback(CallbackQuery c, long from, string to) { var text = c?.Message?.ReplyToMessage?.Text; var fromUser = c?.Message?.ReplyToMessage?.From; var toUser = c?.Message?.ReplyToMessage?.ReplyToMessage?.From; var chat = c.Message.Chat; var replyId = c?.Message?.ReplyToMessage?.MessageId ?? 0; if (c?.Message?.ReplyToMessage?.EditDate != null) { await _TBC.SendTextMessageAsync(chatId : chat, $"Transaction will *NOT* be processed, message was edited by the author.", replyToMessageId : replyId, parseMode : ParseMode.Markdown); return; } var args = text.Split(" "); var cliArgs = CLIHelper.GetNamedArguments(args); var props = await GetTokenTransferProps(chat, replyId, text, from, to); if (props == null) //failed to read properties { return; } var fromUA = await GetUserAccount(from, createNewAcount : false); var acc = new AsmodatStandard.Cryptography.Cosmos.Account(props.prefix, (uint)props.index); acc.InitializeWithMnemonic(fromUA.GetSecret()); Account fromAccountInfo = null; Token fromAccountBalance = null; var notEnoughFunds = false; var client = new CosmosHub(lcd: props.lcd, timeoutSeconds: _cosmosHubClientTimeout); var sequenceKey = $"{props.origin}-{props.network}"; BigInteger fromBalance = 0; try { await _txLocker.Lock(async() => { fromAccountInfo = await client.GetAccount(account: props.origin); fromAccountBalance = fromAccountInfo?.coins?.FirstOrDefault(x => x?.denom?.ToLower() == props.denom.ToLower()); fromBalance = (fromAccountBalance?.amount).ToBigIntOrDefault(0); if (fromBalance < (props.amount + props.fees)) { notEnoughFunds = true; return; } var sequence = fromAccountInfo.sequence.ToLongOrDefault(); var oldSeque = sequences.GetValueOrDefault(sequenceKey, -1); sequences[sequenceKey] = Math.Max(sequence, oldSeque + 1); fromAccountInfo.sequence = sequences[sequenceKey].ToString(); }); } catch (Exception ex) { _logger.Log($"[ERROR] => Filed to fetch '{props.denom ?? "undefined"}' balance of '{props.origin ?? "undefined"}' from '{props.lcd ?? "undefined"}': '{ex.JsonSerializeAsPrettyException(Newtonsoft.Json.Formatting.Indented)}'"); await _TBC.SendTextMessageAsync(text : $"Your account balance is `0 {props.denom}` or lcd '{props.lcd ?? "undefined"}' property is invalid.", chatId : chat, replyToMessageId : replyId, parseMode : ParseMode.Markdown); return; } if (notEnoughFunds) { await _TBC.SendTextMessageAsync(text : $"Transaction including fees requires `{props.amount + props.fees} {props.denom}` but your account balance is `{fromBalance} {props.denom}`", chatId : chat, replyToMessageId : replyId, parseMode : ParseMode.Markdown); return; } var doc = await client.CreateMsgSend( account : fromAccountInfo, to : props.address, amount : props.amount, denom : fromAccountBalance?.denom ?? props.denom, fees : props.fees, gas : props.gas, memo : props.memo ?? "Kira Interchain Wallet - Join us at https://t.me/kirainterex"); var tx = doc.CreateTx(acc); var txResponse = await client.PostTx(tx); var toUserId = to.ToIntOrDefault(0); if (toUser == null && toUserId != 0) { toUser = await _TBC.TryGetChatMember(chat, toUserId); } var fromUserName = $"{fromUser.GetMarkDownUsername()}"; var toUserName = $"{toUser?.GetMarkDownUsername() ?? $"`{props.address}`"}"; var sentAmount = $"{props.amount} {fromAccountBalance?.denom ?? props.denom}"; var statusMsg = ""; var debugLog = $"\nDebug Log: {txResponse?.raw_log ?? txResponse.error}"; var fromMsg = $"\nFrom: {fromUserName}"; var toMsg = $"\nTo: {toUserName}"; var amountMsg = $"\nAmount: `{sentAmount}`\n"; var networkMsg = $"\nNetwork Id: `{props.network ?? "undefined"}`"; var sequenceMsg = $"\nSequence: `{fromAccountInfo.sequence}`"; var hashMsg = $"\nTx Hash: `{txResponse?.txhash}`"; if (txResponse == null || txResponse.height.ToLongOrDefault(0) <= 0 || !txResponse.error.IsNullOrWhitespace()) { statusMsg = $"*Failed* 😢 Action ❌: `tx send`\n" + fromMsg + toMsg + amountMsg; debugLog = $"\nDebug Log: {txResponse?.raw_log}"; sequences[sequenceKey] = sequences.GetValueOrDefault(sequenceKey, -1) - 1; } else { statusMsg = $"*SUCCESS* 😄 {fromUserName} sent `{sentAmount}` 💸 to {toUserName} \n"; if (!text.Contains("--debug")) { debugLog = ""; sequenceMsg = ""; } } await _TBC.SendTextMessageAsync(chatId : chat, statusMsg + debugLog + networkMsg + sequenceMsg + hashMsg, replyToMessageId : replyId, parseMode : ParseMode.Markdown); }
private async Task FaucetProcessMessage(Message m) { var chat = m.Chat; var userId = m.From.Id; var text = (m.Text?.Trim() ?? "").Trim('\'', '"', '`', '*', '[', ']'); var args = text.Split(" "); var cliArgs = CLIHelper.GetNamedArguments(args); await _TBC.SendChatActionAsync(chat, ChatAction.Typing); //simulate keystrokes var token = args.TryGetValueOrDefault(2)?.TrimStartSingle("$"); // $ATOM if (token?.ToLower() == "kex" && Environment.GetEnvironmentVariable($"{token?.ToLower()}_PROPS").IsNullOrWhitespace()) { await _TBC.SendTextMessageAsync(chatId : chat, $"That one's comming 🔜 😉", replyToMessageId : m.MessageId, parseMode : ParseMode.Default); return; } if (!await this.CheckMasterChatMembership(m)) { return; } if (await GetDeposit(m)) { return; } var props = await GetTokenFaucetProps(m); if (props == null) //failed to read properties { return; } var acc = new AsmodatStandard.Cryptography.Cosmos.Account(props.prefix, (uint)props.index); acc.InitializeWithMnemonic(_mnemonic.Release()); var cosmosAdress = acc.CosmosAddress; TxResponse txResponse = null; Account accountInfo = null; Token accountBalance = null; long faucetTokenBalance = 0; var notEnoughFunds = false; var client = new CosmosHub(lcd: props.lcd, timeoutSeconds: _cosmosHubClientTimeout); if (props.address != cosmosAdress) { try { var userAccountInfo = await client.GetAccount(account : props.address); var userAccountBalance = userAccountInfo?.coins?.FirstOrDefault(x => x?.denom?.ToLower() == props.denom); var userBalance = (userAccountBalance?.amount ?? "0").ToBigIntOrDefault(0); if (userBalance >= props.amount) { await _TBC.SendTextMessageAsync(text : $"Your account balance exceeds `{props.amount} {props.denom}`", chatId : new ChatId(m.Chat.Id), replyToMessageId : m.MessageId, parseMode : Telegram.Bot.Types.Enums.ParseMode.Markdown); return; } props.amount = props.amount - userBalance; //only send difference up to max amount } catch (Exception ex) { _logger.Log($"[ERROR] => Filed to fetch '{props.denom ?? "undefined"}' balance of '{props.address ?? "undefined"}' from '{props.lcd ?? "undefined"}': '{ex.JsonSerializeAsPrettyException(Newtonsoft.Json.Formatting.Indented)}'"); } } var sequenceKey = $"{cosmosAdress}-{props.network}"; await _ssLocker.Lock(async() => { accountInfo = await client.GetAccount(account: cosmosAdress); accountBalance = accountInfo?.coins?.FirstOrDefault(x => x?.denom?.ToLower() == props.denom.ToLower()); faucetTokenBalance = (accountBalance?.amount).ToLongOrDefault(0); if (faucetTokenBalance < (props.amount + props.fees)) { notEnoughFunds = true; return; } var sequence = accountInfo.sequence.ToLongOrDefault(); var oldSeque = sequences.GetValueOrDefault(sequenceKey, -1); sequences[sequenceKey] = Math.Max(sequence, oldSeque + 1); accountInfo.sequence = sequences[sequenceKey].ToString(); }); if (notEnoughFunds) { await _TBC.SendTextMessageAsync(chatId : chat, $"Faucet does not have enough `{props.denom ?? "undefined"}` tokens ({faucetTokenBalance}) or coin index ({props.index}) is invalid.\n\n" + $"Network Id: `{props.network ?? "undefined"}`\n" + $"Faucet Public Address: `{cosmosAdress}`", replyToMessageId : m.MessageId, parseMode : Telegram.Bot.Types.Enums.ParseMode.Markdown); return; } var doc = await client.CreateMsgSend( account : accountInfo, to : props.address, amount : props.amount, denom : accountBalance?.denom ?? props.denom, fees : props.fees, gas : props.gas, memo : props.memo ?? "Kira Interchain Faucet - Join us at https://t.me/kirainterex"); var tx = doc.CreateTx(acc); txResponse = await client.PostTx(tx); var inviteLink = await GetMasterChatInviteLink(); string debugLog = null; if (text.Contains("--debug")) { debugLog = $"\nDebug Log: {txResponse?.raw_log}"; } if (txResponse == null || txResponse.height.ToLongOrDefault(0) <= 0 || !txResponse.error.IsNullOrWhitespace()) { await _TBC.SendTextMessageAsync(chatId : chat, $"*Failed* 😢 sending `{props.amount} {props.denom}` to {m.From.GetMarkDownUsername()} ❌\n" + $"Debug Log: `{txResponse?.raw_log ?? txResponse?.error}`\n" + $"Network Id: `{props?.network ?? "undefined"}`\n" + $"Sequence: `{accountInfo?.sequence}`\n" + $"Tx Hash: `{txResponse?.txhash}`", replyToMessageId : m.MessageId, parseMode : ParseMode.Markdown); sequences[sequenceKey] = sequences.GetValueOrDefault(sequenceKey, -1) - 1; } else { await _TBC.SendTextMessageAsync(chatId : chat, $"*SUCCESS* 😄 {inviteLink} sent you `{props.amount} {props.denom}` 💸\n" + $"{debugLog}\n" + $"Network Id: `{props.network ?? "undefined"}`\n" + $"Tx Hash: `{txResponse.txhash}`", replyToMessageId : m.MessageId, parseMode : ParseMode.Markdown); } }