/ Tools / BiblioTech / Commands / MintCommand.cs
MintCommand.cs
  1  using BiblioTech.Options;
  2  using CodexContractsPlugin;
  3  using GethPlugin;
  4  using Logging;
  5  using Utils;
  6  
  7  namespace BiblioTech.Commands
  8  {
  9      public class MintCommand : BaseGethCommand
 10      {
 11          private readonly UserOption optionalUser = new UserOption(
 12              description: "If set, mint tokens for this user. (Optional, admin-only)",
 13              isRequired: false);
 14          private readonly UserAssociateCommand userAssociateCommand;
 15          private readonly ILog log;
 16  
 17          public MintCommand(UserAssociateCommand userAssociateCommand)
 18          {
 19              this.userAssociateCommand = userAssociateCommand;
 20              log = Program.Log;
 21          }
 22  
 23          public override string Name => "mint";
 24          public override string StartingMessage => RandomBusyMessage.Get();
 25          public override string Description => "Transfer and/or mint some TestTokens and Eth to the user if their balance is low.";
 26          public override CommandOption[] Options => new[] { optionalUser };
 27  
 28          protected override async Task Execute(CommandContext context, IGethNode gethNode, ICodexContracts contracts)
 29          {
 30              var userId = GetUserFromCommand(optionalUser, context);
 31              var addr = Program.UserRepo.GetCurrentAddressForUser(userId);
 32              if (addr == null)
 33              {
 34                  await context.Followup($"No address has been set for this user. Please use '/{userAssociateCommand.Name}' to set it first.");
 35                  await Program.AdminChecker.SendInAdminChannel($"User {Mention(userId)} used '/{Name}' but address has not been set.");
 36                  return;
 37              }
 38  
 39              log.Debug($"Running mint command for {userId} with address {addr}...");
 40              var report = new List<string>();
 41  
 42              Transaction<Ether>? sentEth = null;
 43              Transaction<TestToken>? mintedTokens = null;
 44  
 45              await Task.Run(async () =>
 46              {
 47                  sentEth = ProcessEth(gethNode, addr, report);
 48                  mintedTokens = await ProcessTestTokens(contracts, addr, report);
 49              });
 50  
 51              var reportLine = string.Join(Environment.NewLine, report);
 52              Program.UserRepo.AddMintEventForUser(userId, addr, sentEth, mintedTokens);
 53              await Program.AdminChecker.SendInAdminChannel($"User {Mention(userId)} used '/{Name}' successfully. ({reportLine})");
 54  
 55              await context.Followup(reportLine);
 56          }
 57  
 58          private async Task<Transaction<TestToken>?> ProcessTestTokens(ICodexContracts contracts, EthAddress addr, List<string> report)
 59          {
 60              if (IsTestTokenBalanceOverLimit(contracts, addr))
 61              {
 62                  log.Debug("TestToken balance is over threshold.");
 63                  report.Add("TestToken balance over threshold. (No TestTokens sent or minted.)");
 64                  return null;
 65              }
 66  
 67              var sent = await TransferTestTokens(contracts, addr, report);
 68              var minted = await MintTestTokens(contracts, addr, report);
 69  
 70              return new Transaction<TestToken>(sent.Item1, minted.Item1, $"{sent.Item2},{minted.Item2}");
 71          }
 72  
 73          private Transaction<Ether>? ProcessEth(IGethNode gethNode, EthAddress addr, List<string> report)
 74          {
 75              if (IsEthBalanceOverLimit(gethNode, addr))
 76              {
 77                  log.Debug("Eth balance is over threshold.");
 78                  report.Add("Eth balance is over threshold. (No Eth sent.)");
 79                  return null;
 80              }
 81              var eth = Program.Config.SendEth.Eth();
 82              log.Debug($"Sending {eth}...");
 83              var transaction = gethNode.SendEth(addr, eth);
 84              report.Add($"Sent {eth} {FormatTransactionLink(transaction)}");
 85              return new Transaction<Ether>(eth, 0.Eth(), transaction);
 86          }
 87  
 88          private async Task<(TestToken, string)> MintTestTokens(ICodexContracts contracts, EthAddress addr, List<string> report)
 89          {
 90              var nothing = (0.TstWei(), string.Empty);
 91              if (Program.Config.MintTT < 1)
 92              {
 93                  log.Debug("Skip minting TST: configured amount is less than 1.");
 94                  return nothing;
 95              }
 96  
 97              try
 98              {
 99                  var tokens = Program.Config.MintTT.TstWei();
100                  log.Debug($"Minting {tokens}...");
101                  var transaction = contracts.MintTestTokens(addr, tokens);
102                  report.Add($"Minted {tokens} {FormatTransactionLink(transaction)}");
103                  return (tokens, transaction);
104              }
105              catch (Exception ex)
106              {
107                  report.Add("Minter: I'm sorry! Something went unexpectedly wrong. (Admins have been notified)");
108                  await Program.AdminChecker.SendInAdminChannel($"{nameof(MintCommand)} {nameof(MintTestTokens)} failed with: {ex}");
109              }
110              return nothing;
111          }
112  
113          private async Task<(TestToken, string)> TransferTestTokens(ICodexContracts contracts, EthAddress addr, List<string> report)
114          {
115              var nothing = (0.TstWei(), string.Empty);
116              if (Program.Config.SendTT < 1)
117              {
118                  log.Debug("Skip transferring TST: configured amount is less than 1.");
119                  return nothing;
120              }
121              if (Program.GethLink == null)
122              {
123                  log.Debug("Skip transferring TST: GethLink not available.");
124                  report.Add("Transaction operations are currently not available.");
125                  return nothing;
126              }
127  
128              try
129              {
130                  var current = contracts.GetTestTokenBalance(Program.GethLink.Node.CurrentAddress);
131                  var amount = Program.Config.SendTT.TstWei();
132                  if (current.TstWei <= amount.TstWei)
133                  {
134                      log.Debug($"Unable to transfer TST: Bot has: {current} - Transfer amount: {amount}");
135                      report.Add("Unable to send TestTokens: Bot doesn't have enough! (Admins have been notified)");
136                      await Program.AdminChecker.SendInAdminChannel($"{nameof(MintCommand)} failed: Bot has insufficient tokens.");
137                      return nothing;
138                  }
139  
140                  log.Debug($"Sending {amount}...");
141                  var transaction = contracts.TransferTestTokens(addr, amount);
142                  report.Add($"Transferred {amount} {FormatTransactionLink(transaction)}");
143                  return (amount, transaction);
144              }
145              catch (Exception ex)
146              {
147                  report.Add("Transfer: I'm sorry! Something went unexpectedly wrong. (Admins have been notified)");
148                  await Program.AdminChecker.SendInAdminChannel($"{nameof(MintCommand)} {nameof(TransferTestTokens)} failed with: {ex}");
149              }
150              return nothing;
151          }
152  
153          private bool IsEthBalanceOverLimit(IGethNode gethNode, EthAddress addr)
154          {
155              var eth = gethNode.GetEthBalance(addr);
156              return eth > Program.Config.SendEth.Eth();
157          }
158  
159          private bool IsTestTokenBalanceOverLimit(ICodexContracts contracts, EthAddress addr)
160          {
161              var testTokens = contracts.GetTestTokenBalance(addr);
162  
163              if (Program.Config.MintTT > 0 && testTokens > Program.Config.MintTT.TstWei()) return true;
164              if (Program.Config.SendTT > 0 && testTokens > Program.Config.SendTT.TstWei()) return true;
165  
166              return false;
167          }
168  
169          private string FormatTransactionLink(string transaction)
170          {
171              var url = Program.Config.TransactionLinkFormat.Replace("<ID>", transaction);
172              return $"- [View on block explorer](<{url}>){Environment.NewLine}Transaction ID - `{transaction}`";
173          }
174      }
175  }