Exemple #1
0
 public async IAsyncEnumerable <ApiStakeEvent> ListStakes(
     [Required, FromRoute, EthereumAddress] string address,
     [Required, FromRoute] Symbol symbol)
 {
     await foreach (var stake in stakeService
                    .ListStakeEventsAsync(symbol, GetAddress(address))
                    .WithCancellation(scopedCancellationToken.Token))
     {
         yield return(MapStakeEvent(stake));
     }
 }
Exemple #2
0
        public async Task <IInvestment> GetInvestmentAsync(EthereumAddress address, Symbol symbol, CurrencyCode currencyCode)
        {
            if (symbol.IsFund())
            {
                var fund = await fundService.GetFundAsync(symbol, currencyCode);

                var tokenCount = await quantityProviderFactory
                                 .CreateProvider(fund.Token.Symbol.ToString())
                                 .GetContractBalanceAsync(fund.Token.ContractAddress, address, fund.Token.Decimals);

                var subInvestments = new List <ISubInvestment>();

                foreach (var asset in fund.Assets)
                {
                    var held = asset.Holding.ContractAddress.HasValue
                        ? await quantityProviderFactory
                               .CreateProvider(asset.Holding.Symbol)
                               .GetContractBalanceAsync(asset.Holding.ContractAddress.Value, address, asset.Holding.Decimals.Value)
                        : decimal.Zero;

                    if (held > decimal.Zero)
                    {
                        subInvestments.Add(new BusinessSubInvestment()
                        {
                            Holding     = asset.Holding,
                            Held        = held,
                            MarketValue = CurrencyConverter.Convert(asset.PricePerToken, currencyCode) * held
                        });
                    }
                }

                var stakeEvents = new List <IStakeEvent>();

                foreach (var stake in GetStakes())
                {
                    stakeEvents.AddRange(
                        await stakeService
                        .ListStakeEventsAsync(stake.Symbol, address, fund.Token.Symbol)
                        .ToListAsync(CancellationToken));
                }

                return(new BusinessInvestment()
                {
                    Fund = fund,
                    Held = tokenCount,
                    Legacy = tokenCount == default,
                    SubInvestments = subInvestments,
                    Stakes = stakeEvents
                });
Exemple #3
0
        private async Task SyncStakingPowerAsync(
            IFundService fundService,
            IStakeService stakeService,
            IStakingPowerRepository stakingRepository,
            IStakeSettings stake,
            DateTimeOffset startDate,
            DateTimeOffset endDate,
            CancellationToken cancellationToken)
        {
            var currentDate      = GetHourlyDate(startDate.UtcDateTime).UtcDateTime;
            var lastStakingPower = await stakingRepository.GetStakingPowerAsync(stake.ContractAddress, currentDate.AddHours(-1));

            var stakingEvents = await stakeService.ListStakeEventsAsync(stake.Symbol, startDate.UtcDateTime, endDate.UtcDateTime)
                                .ToListAsync(cancellationToken);

            var hourlyGroups = stakingEvents
                               .OrderBy(x => x.ConfirmedAt)
                               .GroupBy(x => GetHourlyDate(x.ConfirmedAt))
                               .ToList();

            do
            {
                try
                {
                    var fundPowers   = new List <DataStakingPowerFund>();
                    var hourlyEvents = hourlyGroups.SingleOrDefault(g => g.Key == currentDate)
                                       ?? Enumerable.Empty <IStakeEvent>();

                    foreach (var symbol in stake.FundMultipliers.Keys)
                    {
                        var fund = AppSettings.Funds
                                   .Cast <IFundSettings>()
                                   .Single(x => x.Symbol == symbol);

                        var events = lastStakingPower?.Breakdown.SingleOrDefault(x =>
                                                                                 x.ContractAddress.Equals(fund.ContractAddress.Address, StringComparison.OrdinalIgnoreCase))
                                     ?.Events.ToList()
                                     ?? new List <DataStakingEvent>();

                        events.AddRange(hourlyEvents
                                        .Where(e =>
                                               e.ContractAddress == fund.ContractAddress &&
                                               e.Type == StakeEventType.Lockup)
                                        .Select(e => new DataStakingEvent()
                        {
                            UserAddress  = e.UserAddress,
                            StakedAt     = e.ConfirmedAt,
                            Quantity     = e.Change,
                            ExpiresAt    = e.Lock.ExpiresAt,
                            TimeModifier = stake.TimeMultipliers
                                           .SingleOrDefault(tm =>
                                                            tm.RangeMin <= e.Lock.Duration.Days &&
                                                            tm.RangeMax >= e.Lock.Duration.Days)
                                           ?.Multiplier ?? 1
                        }));

                        foreach (var releaseEvent in hourlyEvents
                                 .Where(e =>
                                        e.ContractAddress == fund.ContractAddress &&
                                        e.Type != StakeEventType.Lockup))
                        {
                            var approximateQuantity = releaseEvent.Release.Quantity + (releaseEvent.Release.FeeQuantity ?? decimal.Zero);
                            var userStakes          = events
                                                      .Where(e => e.UserAddress.Equals(releaseEvent.UserAddress.Address, StringComparison.OrdinalIgnoreCase))
                                                      .ToList();

                            var lockUp = userStakes.Count > 0
                                ? userStakes.Count == 1
                                    ? userStakes.Single()
                                    : userStakes
                                         .OrderBy(x => x.StakedAt)
                                         .FirstOrDefault(e => Math.Abs(e.Quantity - approximateQuantity) <= Precision)
                                : null;

                            if (lockUp != null)
                            {
                                events.Remove(lockUp);
                            }
                        }

                        if (events.Any())
                        {
                            var prices = await fundService.ListPerformanceAsync(
                                symbol,
                                PriceMode.Raw,
                                currentDate.Date.AddDays(-1),
                                currentDate.Date.AddDays(2),
                                CurrencyCode.USD)
                                         .ToListAsync(cancellationToken);

                            var closestPrice = prices
                                               .OrderBy(i => Math.Abs(
                                                            new DateTimeOffset(i.Date, TimeSpan.Zero).ToUnixTimeSeconds() -
                                                            new DateTimeOffset(currentDate, TimeSpan.Zero).ToUnixTimeSeconds()))
                                               .FirstOrDefault()
                                               ?? throw new PermanentException($"No Price data could be found for date {currentDate}");

                            fundPowers.Add(new DataStakingPowerFund()
                            {
                                ContractAddress = fund.ContractAddress,
                                FundModifier    = stake.FundMultipliers[symbol],
                                PricePerToken   = closestPrice.NetAssetValuePerToken,
                                Events          = events
                            });
                        }
                    }

                    lastStakingPower = new DataStakingPower()
                    {
                        Address   = stake.ContractAddress,
                        Date      = currentDate.AddHours(1),
                        Power     = fundPowers.Sum(fp => fp.PricePerToken * fp.Events.Sum(fpe => fpe.Quantity * fpe.TimeModifier * fp.FundModifier)),
                        Breakdown = fundPowers,
                        Summary   = fundPowers
                                    .Select(fp => new DataStakingPowerSummary()
                        {
                            ContractAddress = fp.ContractAddress,
                            Power           = fp.PricePerToken * fp.Events.Sum(fpe => fpe.Quantity * fpe.TimeModifier * fp.FundModifier)
                        })
                                    .ToList()
                    };

                    await stakingRepository.UploadItemsAsync(lastStakingPower);
                }
                finally
                {
                    currentDate = currentDate.AddHours(1);
                }
            }while (!cancellationToken.IsCancellationRequested && currentDate < endDate.Round());
        }