Пример #1
0
 public NeutrinoApi(NeutrinoSettings neutrinoSettings, WavesHelper wavesHelper, Node node,
                    PrivateKeyAccount account)
 {
     _wavesHelper      = wavesHelper;
     _node             = node;
     _account          = account;
     _neutrinoSettings = neutrinoSettings;
 }
Пример #2
0
        public PacemakerService(WavesHelper wavesHelper, Node node, PrivateKeyAccount account,
                                NeutrinoSettings neutrinoSettings, LeasingSettings leasingSettings, long deficitOffset = 5)
        {
            _neutrinoApi = new NeutrinoApi(neutrinoSettings, wavesHelper, node, account);

            _wavesHelper      = wavesHelper;
            _neutrinoSettings = neutrinoSettings;
            _leasingSettings  = leasingSettings;
            _deficitOffset    = deficitOffset;
        }
Пример #3
0
        private static async Task Main(string[] args)
        {
            var configuration = new ConfigurationBuilder()
                                .SetBasePath(Directory.GetCurrentDirectory())
#if DEBUG
                                .AddJsonFile("appsettings.development.json", optional: true, reloadOnChange: true)
#elif RELEASE
                                .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
#endif
                                .AddCommandLine(args)
                                .Build();

            var settings = configuration.Get <Settings>();
            var seed     = args[0];

            var wavesHelper = new WavesHelper(settings.NodeUrl, settings.ChainId);
            var account     = PrivateKeyAccount.CreateFromSeed(seed, settings.ChainId);
            var node        = new Node(settings.NodeUrl, settings.ChainId);

            var commands = new List <Command>()
            {
                Command.Withdraw,
                Command.TransferToAuction,
                Command.ExecuteOrderLiquidation,
                Command.ExecuteOrderAuction,
                Command.RebalanceLeasing
            };

            var pacemakerService = new PacemakerService(wavesHelper, node, account, settings.NeutrinoSettings, settings.Leasing, settings.DeficitOffset);

            while (true)
            {
                foreach (var command in commands)
                {
                    Logger.Info($"Run command:{command}");
                    var sw = new Stopwatch();
                    sw.Start();

                    try
                    {
                        await RunCommand(pacemakerService, command);
                    }
                    catch (Exception ex)
                    {
                        Logger.Error(ex);
                    }

                    sw.Stop();
                    Logger.Info($"Commands completed in {sw.ElapsedMilliseconds/1000} seconds");
                }

                Logger.Info($"Wait {TimeSpan.FromSeconds(settings.TimeoutSec)} seconds");
                await Task.Delay(TimeSpan.FromSeconds(settings.TimeoutSec));
            }
        }
Пример #4
0
        private static async Task Main(string[] args)
        {
            var configuration = new ConfigurationBuilder()
                                .SetBasePath(Directory.GetCurrentDirectory())
#if DEBUG
                                .AddJsonFile("appsettings.development.json", optional: true, reloadOnChange: true)
#elif RELEASE
                                .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
#endif
                                .AddCommandLine(args)
                                .Build();

            var settings = configuration.Get <Settings>();
            var seed     = args[0];

            var wavesHelper     = new WavesHelper(settings.NodeUrl, settings.ChainId);
            var account         = PrivateKeyAccount.CreateFromSeed(seed, settings.ChainId);
            var node            = new Node(settings.NodeUrl, settings.ChainId);
            var contractPubKey  = Base58.Decode((settings.ContractPubKey));
            var contractAddress = AddressEncoding.GetAddressFromPublicKey(contractPubKey, settings.ChainId);

            while (true)
            {
                try
                {
                    var height = await wavesHelper.GetHeight();

                    Logger.Info("New height: " + height);
                    Logger.Info("Init");

                    var neutrinoContractData = AccountDataConverter.ToNeutrinoAccountData(
                        await wavesHelper.GetDataByAddress(contractAddress));
                    var controlContractData = AccountDataConverter.ToControlAccountData(
                        await wavesHelper.GetDataByAddress(neutrinoContractData.ControlContractAddress));
                    var auctionControlData = AccountDataConverter.ToAuctionAccountData(
                        await wavesHelper.GetDataByAddress(neutrinoContractData.AuctionContractAddress));
                    var liquidationControlData = AccountDataConverter.ToLiquidationAccountData(
                        await wavesHelper.GetDataByAddress(neutrinoContractData.LiquidationContractAddress));

                    var totalNeutrinoSupply = await wavesHelper.GetTotalSupply(neutrinoContractData.NeutrinoAssetId);

                    var neutrinoBalance = await wavesHelper.GetBalance(contractAddress, neutrinoContractData.NeutrinoAssetId);

                    var wavesBalance = await wavesHelper.GetBalance(contractAddress);

                    Logger.Info($"Price:{controlContractData.Price}");

                    var neutrinoContractBalance = await wavesHelper.GetDetailsBalance(contractAddress);

                    var addresses = new List <string>();
                    if (neutrinoContractData.BalanceLockNeutrinoByUser != null)
                    {
                        addresses.AddRange(neutrinoContractData.BalanceLockNeutrinoByUser.Where(x => x.Value > 0).Select(x => x.Key));
                    }
                    if (neutrinoContractData.BalanceLockWavesByUser != null)
                    {
                        addresses.AddRange(neutrinoContractData.BalanceLockWavesByUser?.Where(x => x.Value > 0).Select(x => x.Key));
                    }

                    long totalWithdraw = 0;
                    foreach (var address in addresses)
                    {
                        var withdrawBlock = neutrinoContractData.BalanceUnlockBlockByAddress.GetValueOrDefault(address);
                        if (height < withdrawBlock)
                        {
                            continue;
                        }

                        var indexes = controlContractData.PriceHeightByIndex.Where(x => x.Value >= withdrawBlock).ToList();

                        if (!indexes.Any())
                        {
                            continue;
                        }

                        var indexString            = indexes.Min(x => x.Key);
                        var index                  = Convert.ToInt64(indexString);
                        var heightByIndex          = controlContractData.PriceHeightByIndex[indexString];
                        var priceByHeight          = controlContractData.PriceByHeight[Convert.ToString(heightByIndex)];
                        var withdrawNeutrinoAmount = neutrinoContractData.BalanceLockNeutrinoByUser.GetValueOrDefault(address);

                        if (withdrawNeutrinoAmount > 0)
                        {
                            var availableBalance = neutrinoContractBalance.Available - totalWithdraw;
                            var wavesAmount      = CurrencyConvert.NeutrinoToWaves(withdrawNeutrinoAmount, priceByHeight);

                            if (wavesAmount > availableBalance)
                            {
                                if (!settings.Leasing.IsLeasingProvider)
                                {
                                    continue;
                                }

                                var totalLeasingCancelAmount = 0L;
                                var activeLeaseTxs           = await wavesHelper.GetActiveLease(settings.Leasing.NodeAddress);

                                foreach (var leasingTx in activeLeaseTxs.OrderBy(x => x.Amount))
                                {
                                    if (totalLeasingCancelAmount >= wavesAmount)
                                    {
                                        break;
                                    }

                                    totalLeasingCancelAmount += leasingTx.Amount;
                                    var cancelLease = new CancelLeasingTransaction(settings.ChainId, contractPubKey, leasingTx.Id, 0.005m);
                                    cancelLease.Sign(account);
                                    // shit code. Bug in wavesCs
                                    var json = JObject.Parse(cancelLease.GetJsonWithSignature().ToJson());
                                    json.Add("proofs", new JArray {
                                        cancelLease.Proofs.Take(Array.FindLastIndex(cancelLease.Proofs, p => p != null && p.Length > 0) + 1)
                                        .Select(p => p == null ? "" : p.ToBase58())
                                        .ToArray()
                                    });
                                    json.Add("version", 2);
                                    json.Add("chainId", settings.ChainId);
                                    var id = await wavesHelper.WaitTxAndGetId(await wavesHelper.Broadcast(json.ToString()));

                                    Logger.Info($"Cancel lease tx:{id} (LeaseId:{cancelLease.LeaseId})");
                                }
                            }

                            totalWithdraw += wavesAmount;
                        }

                        var withdrawTx = node.InvokeScript(account, contractAddress, "withdraw",
                                                           new List <object>()
                        {
                            address, index
                        });
                        var txId = await wavesHelper.WaitTxAndGetId(withdrawTx);

                        Logger.Info($"Withdraw tx id:{txId} (Address:{address})");
                    }

                    if (settings.Leasing.IsLeasingProvider)
                    {
                        var leasingAmountForOneTxInWavelet = settings.Leasing.LeasingAmountForOneTx * CurrencyConvert.Wavelet;

                        var minWaves         = Convert.ToInt64((neutrinoContractBalance.Regular - totalWithdraw) / 100 * (100 - settings.Leasing.LeasingSharePercent));
                        var availableBalance = neutrinoContractBalance.Available - totalWithdraw;
                        var neededAmount     = minWaves - availableBalance;
                        var activeLeaseTxs   = await wavesHelper.GetActiveLease(settings.Leasing.NodeAddress);

                        var totalLeasingCancelAmount = 0L;
                        if (neededAmount > leasingAmountForOneTxInWavelet)
                        {
                            foreach (var leasingTx in activeLeaseTxs.OrderBy(x => x.Amount).Where(x => x.Sender == contractAddress))
                            {
                                if (totalLeasingCancelAmount >= neededAmount)
                                {
                                    break;
                                }

                                totalLeasingCancelAmount += leasingTx.Amount;
                                var cancelLease = new CancelLeasingTransaction(settings.ChainId, contractPubKey,
                                                                               leasingTx.Id, 0.005m);
                                cancelLease.Sign(account);
                                // shit code. Bug in wavesCs
                                var json = JObject.Parse(cancelLease.GetJsonWithSignature().ToJson());
                                json.Add("proofs", new JArray
                                {
                                    cancelLease.Proofs
                                    .Take(Array.FindLastIndex(cancelLease.Proofs, p => p != null && p.Length > 0) +
                                          1)
                                    .Select(p => p == null ? "" : p.ToBase58())
                                    .ToArray()
                                });
                                json.Add("version", 2);
                                json.Add("chainId", settings.ChainId);
                                var id = await wavesHelper.WaitTxAndGetId(await wavesHelper.Broadcast(json.ToString()));

                                Logger.Info($"Cancel lease tx:{id} (LeaseId:{cancelLease.LeaseId})");
                            }
                        }

                        var expectedLeasingBalance = Convert.ToInt64((neutrinoContractBalance.Regular - totalWithdraw) / 100 * settings.Leasing.LeasingSharePercent);
                        var leasingBalance         = neutrinoContractBalance.Regular - neutrinoContractBalance.Available;
                        var neededLeaseTx          = expectedLeasingBalance - leasingBalance;


                        while (neededLeaseTx >= leasingAmountForOneTxInWavelet)
                        {
                            neededLeaseTx -= leasingAmountForOneTxInWavelet;
                            var leaseTx = new LeaseTransaction(settings.ChainId, contractPubKey, settings.Leasing.NodeAddress, settings.Leasing.LeasingAmountForOneTx, 0.005m);
                            leaseTx.Sign(account);
                            // shit code. Bug in wavesCs
                            var json = JObject.Parse(leaseTx.GetJsonWithSignature().ToJson());
                            json.Add("proofs", new JArray {
                                leaseTx.Proofs.Take(Array.FindLastIndex(leaseTx.Proofs, p => p != null && p.Length > 0) + 1)
                                .Select(p => p == null ? "" : p.ToBase58())
                                .ToArray()
                            });
                            json.Add("version", 2);
                            var id = await wavesHelper.WaitTxAndGetId(await wavesHelper.Broadcast(json.ToString()));

                            Logger.Info($"Lease tx:{id}");
                        }
                    }

                    var supply  = totalNeutrinoSupply - neutrinoBalance + neutrinoContractData.BalanceLockNeutrino;
                    var reserve = wavesBalance - neutrinoContractData.BalanceLockWaves;

                    Logger.Info($"Supply:{supply}");
                    Logger.Info($"Reserve:{reserve}");

                    var bondAuctionBalance = await wavesHelper.GetBalance(neutrinoContractData.AuctionContractAddress,
                                                                          neutrinoContractData.BondAssetId);

                    var deficit = CurrencyConvert.NeutrinoToBond(supply - CurrencyConvert.WavesToNeutrino(reserve, controlContractData.Price));
                    var liquidationContractBalance = CurrencyConvert.NeutrinoToBond(await wavesHelper.GetBalance(neutrinoContractData.LiquidationContractAddress, neutrinoContractData.NeutrinoAssetId));

                    Logger.Info($"Deficit:{deficit}");
                    Logger.Info($"BondBalance:{bondAuctionBalance}");

                    if ((deficit > 0 && deficit - bondAuctionBalance >= CurrencyConvert.NeutrinoToBond(supply) * DeficitOffset / 100) || (deficit * -1) - liquidationContractBalance > 0)
                    {
                        Logger.Info("Transfer to auction");
                        var generateBondTx = node.InvokeScript(account, contractAddress,
                                                               "transferToAuction", null);
                        var txId = await wavesHelper.WaitTxAndGetId(generateBondTx);

                        Logger.Info($"Transfer to auction tx id:{txId}");
                    }
                    var surplusWithoutLiquidation = CurrencyConvert.NeutrinoToBond(CurrencyConvert.WavesToNeutrino(reserve, controlContractData.Price) - supply - CurrencyConvert.BondToNeutrino(liquidationContractBalance));
                    if (surplusWithoutLiquidation > 0 && liquidationContractBalance > 0 && !string.IsNullOrEmpty(liquidationControlData.Orderbook))
                    {
                        Logger.Info("Execute order for liquidation");

                        var  orders       = liquidationControlData.Orderbook.Split("_");
                        long totalExecute = 0;
                        var  surplus      = Math.Abs(deficit);
                        foreach (var order in orders.Where(x => !string.IsNullOrEmpty(x)))
                        {
                            var total       = liquidationControlData.TotalByOrder[order];
                            var totalFilled = liquidationControlData.FilledTotalByOrder?.GetValueOrDefault(order) ?? 0;
                            var amount      = total - totalFilled;
                            if (totalExecute >= surplus)
                            {
                                break;
                            }

                            totalExecute += amount;

                            var exTx = node.InvokeScript(account, neutrinoContractData.LiquidationContractAddress, "liquidateBond",
                                                         null);
                            var txId = await wavesHelper.WaitTxAndGetId(exTx);

                            Logger.Info($"Execute order tx id:{txId}");
                        }
                    }
                    else if (surplusWithoutLiquidation <= 0 && liquidationContractBalance > 0)
                    {
                        var exTx = node.InvokeScript(account, neutrinoContractData.LiquidationContractAddress,
                                                     "liquidateBond",
                                                     null);
                        var txId = await wavesHelper.WaitTxAndGetId(exTx);

                        Logger.Info($"Execute order tx id:{txId}");
                    }
                    else if (deficit > 0 && bondAuctionBalance > 0 && !string.IsNullOrEmpty(auctionControlData.Orderbook))
                    {
                        Logger.Info("Execute order for auction");

                        var  orders       = auctionControlData.Orderbook.Split("_");
                        long totalExecute = 0;
                        foreach (var order in orders.Where(x => !string.IsNullOrEmpty(x)))
                        {
                            var total       = auctionControlData.TotalByOrder[order];
                            var totalFilled = auctionControlData.FilledTotalByOrder?.GetValueOrDefault(order) ?? 0;
                            var amount      = CurrencyConvert.NeutrinoToBond(total - totalFilled) * 100 / auctionControlData.PriceByOrder[order];

                            if (totalExecute >= deficit)
                            {
                                break;
                            }

                            totalExecute += amount;

                            var exTx = node.InvokeScript(account, neutrinoContractData.AuctionContractAddress,
                                                         "sellBond", null);
                            var exTxId = await wavesHelper.WaitTxAndGetId(exTx);

                            Logger.Info($"Execute order tx id:{exTxId}");
                        }
                    }
                }
                catch (Exception ex)
                {
                    Logger.Error(ex);
                }

                await Task.Delay(TimeSpan.FromSeconds(settings.TimeoutSec));
            }
        }
Пример #5
0
        private static async Task Main(string[] args)
        {
            var configuration = new ConfigurationBuilder()
                                .SetBasePath(Directory.GetCurrentDirectory())
#if DEBUG
                                .AddJsonFile("appsettings.development.json", optional: true, reloadOnChange: true)
#elif RELEASE
                                .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
#endif
                                .AddCommandLine(args)
                                .Build();

            var seed     = args[0];
            var settings = configuration.Get <Settings>();

            var nodeApi     = new Node(settings.NodeUrl, settings.ChainId);
            var account     = PrivateKeyAccount.CreateFromSeed(seed, settings.ChainId);
            var wavesHelper = new WavesHelper(settings.NodeUrl, settings.ChainId);

            Logger.Info("Start price oracle");
            while (true)
            {
                try
                {
                    var height = await wavesHelper.GetHeight();

                    Logger.Info($"Height:{height}");
                    var controlContractData = AccountDataConverter.ToControlAccountData(await wavesHelper.GetDataByAddress(settings.ContractAddress));

                    var newPrice = await GetPrice();

                    var oracleData = AccountDataConverter.ToOracleAccountData(await wavesHelper.GetDataByAddress(account.Address));
                    if (!oracleData.PriceByHeight?.ContainsKey(Convert.ToString(height)) ?? true)
                    {
                        var tx = nodeApi.PutData(account, new Dictionary <string, object>()
                        {
                            { "price_" + height, newPrice }
                        });
                        Logger.Info($"Tx set current price ({newPrice}): {(string) JObject.Parse(tx)["id"]}");
                        Logger.Debug(tx);
                    }

                    var oraclePriceCount = 0;
                    foreach (var oracle in controlContractData.Oracles.Split(","))
                    {
                        var anyOracleData = AccountDataConverter.ToOracleAccountData(await wavesHelper.GetDataByAddress(oracle));
                        if (anyOracleData.PriceByHeight?.ContainsKey(Convert.ToString(height)) ?? false)
                        {
                            oraclePriceCount++;
                        }
                    }

                    Logger.Debug("Oracle price count:" + oraclePriceCount);
                    if (oraclePriceCount >= controlContractData.BftCoefficientOracle && (!controlContractData.PriceByHeight?.ContainsKey(Convert.ToString(height)) ?? true))
                    {
                        var tx = nodeApi.InvokeScript(account, settings.ContractAddress, "finalizeCurrentPrice", null);
                        Logger.Info($"Tx finalize current price: {(string) JObject.Parse(tx)["id"]}");
                        Logger.Debug(tx);
                    }

                    Logger.Info($"Сurrent price:{controlContractData.Price}");
                    Logger.Info($"New price:{newPrice}");
                }
                catch (Exception ex)
                {
                    Logger.Error(ex);
                }

                Logger.Info($"Sleep");
                await Task.Delay(TimeSpan.FromSeconds(settings.TimeoutSec));
            }
        }