Example #1
0
        private DistributedProfitsInfo UpdateDistributedProfits(DistributeProfitsInput input,
                                                                Address profitsReceivingVirtualAddress, long totalShares)
        {
            var balance = State.TokenContract.GetBalance.Call(new GetBalanceInput
            {
                Owner  = profitsReceivingVirtualAddress,
                Symbol = input.Symbol
            }).Balance;
            var distributedProfitsInformation = State.DistributedProfitsMap[profitsReceivingVirtualAddress];

            if (distributedProfitsInformation == null)
            {
                distributedProfitsInformation = new DistributedProfitsInfo
                {
                    TotalShares   = totalShares,
                    ProfitsAmount = { { input.Symbol, input.Amount.Add(balance) } },
                    IsReleased    = true
                };
            }
            else
            {
                // This means someone used `DistributeProfits` do donate to the specific account period of current profit item.
                distributedProfitsInformation.TotalShares = totalShares;
                distributedProfitsInformation.ProfitsAmount[input.Symbol] = balance.Add(input.Amount);
                distributedProfitsInformation.IsReleased = true;
            }

            State.DistributedProfitsMap[profitsReceivingVirtualAddress] = distributedProfitsInformation;
            return(distributedProfitsInformation);
        }
        public override Empty DistributeProfits(DistributeProfitsInput input)
        {
            var scheme = GetValidScheme(input.SchemeManager, true);

            Assert(Context.Sender == Context.GetContractAddressByName(SmartContractConstants.TokenContractSystemName) ||
                   Context.Sender == input.SchemeManager, "No permission to distribute profits.");
            State.ProfitContract.DistributeProfits.Send(new Profit.DistributeProfitsInput
            {
                SchemeId = scheme.SchemeId,
                Symbol   = input.Symbol ?? scheme.Symbol,
                Period   = scheme.Period
            });
            scheme.Period = scheme.Period.Add(1);
            State.TokenHolderProfitSchemes[input.SchemeManager] = scheme;
            return(new Empty());
        }
Example #3
0
        private void UpdateSubSchemeInformation(DistributeProfitsInput input, SchemeBeneficiaryShare subScheme,
                                                long amount)
        {
            var subItem = State.SchemeInfos[subScheme.SchemeId];

            if (subItem.UndistributedProfits.ContainsKey(input.Symbol))
            {
                subItem.UndistributedProfits[input.Symbol] =
                    subItem.UndistributedProfits[input.Symbol].Add(amount);
            }
            else
            {
                subItem.UndistributedProfits.Add(input.Symbol, amount);
            }

            State.SchemeInfos[subScheme.SchemeId] = subItem;
        }
Example #4
0
        private void PerformDistributeProfits(DistributeProfitsInput input, Scheme scheme, long totalShares,
                                              Address profitsReceivingVirtualAddress)
        {
            var remainAmount = input.Amount;

            remainAmount = DistributeProfitsForSubSchemes(input, scheme, totalShares, remainAmount);

            // Transfer remain amount to individuals' receiving profits address.
            if (remainAmount != 0)
            {
                State.TokenContract.TransferFrom.Send(new TransferFromInput
                {
                    From   = scheme.VirtualAddress,
                    To     = profitsReceivingVirtualAddress,
                    Amount = remainAmount,
                    Symbol = input.Symbol
                });
            }
        }
Example #5
0
        private long DistributeProfitsForSubSchemes(DistributeProfitsInput input, Scheme scheme, long totalShares,
                                                    long remainAmount)
        {
            Context.LogDebug(() => $"Sub schemes count: {scheme.SubSchemes.Count}");
            foreach (var subScheme in scheme.SubSchemes)
            {
                Context.LogDebug(() => $"Releasing {subScheme.SchemeId}");

                // General ledger of this sub profit item.
                var subItemVirtualAddress = Context.ConvertVirtualAddressToContractAddress(subScheme.SchemeId);

                var amount = subScheme.Shares.Mul(input.Amount).Div(totalShares);
                if (amount != 0)
                {
                    State.TokenContract.TransferFrom.Send(new TransferFromInput
                    {
                        From   = scheme.VirtualAddress,
                        To     = subItemVirtualAddress,
                        Amount = amount,
                        Symbol = input.Symbol
                    });
                }

                remainAmount = remainAmount.Sub(amount);

                UpdateSubSchemeInformation(input, subScheme, amount);

                // Update current_period of detail of sub profit item.
                var subItemDetail = State.ProfitDetailsMap[input.SchemeId][subItemVirtualAddress];
                foreach (var detail in subItemDetail.Details)
                {
                    detail.LastProfitPeriod = scheme.CurrentPeriod;
                }

                State.ProfitDetailsMap[input.SchemeId][subItemVirtualAddress] = subItemDetail;
            }

            return(remainAmount);
        }
Example #6
0
        private Empty BurnProfits(DistributeProfitsInput input, Scheme scheme, Address profitVirtualAddress,
                                  Address profitsReceivingVirtualAddress)
        {
            Context.LogDebug(() => "Entered BurnProfits.");
            scheme.CurrentPeriod = input.Period > 0 ? input.Period.Add(1) : scheme.CurrentPeriod;

            var balance = State.TokenContract.GetBalance.Call(new GetBalanceInput
            {
                Owner  = profitsReceivingVirtualAddress,
                Symbol = input.Symbol
            }).Balance;

            // Distribute profits to an address that no one can receive this amount of profits.
            if (input.Amount.Add(balance) == 0)
            {
                State.SchemeInfos[input.SchemeId] = scheme;
                State.DistributedProfitsMap[profitsReceivingVirtualAddress] = new DistributedProfitsInfo
                {
                    IsReleased = true
                };
                return(new Empty());
            }

            // Burn this amount of profits.
            if (input.Amount > 0)
            {
                State.TokenContract.TransferFrom.Send(new TransferFromInput
                {
                    From   = profitVirtualAddress,
                    To     = Context.Self,
                    Amount = input.Amount,
                    Symbol = input.Symbol
                });
            }

            if (balance > 0)
            {
                State.TokenContract.TransferFrom.Send(new TransferFromInput
                {
                    From   = profitsReceivingVirtualAddress,
                    To     = Context.Self,
                    Amount = balance,
                    Symbol = input.Symbol
                });
            }

            State.TokenContract.Burn.Send(new BurnInput
            {
                Amount = input.Amount.Add(balance),
                Symbol = input.Symbol
            });
            scheme.UndistributedProfits[input.Symbol] =
                scheme.UndistributedProfits[input.Symbol].Sub(input.Amount);
            State.SchemeInfos[input.SchemeId] = scheme;

            State.DistributedProfitsMap[profitsReceivingVirtualAddress] = new DistributedProfitsInfo
            {
                IsReleased    = true,
                ProfitsAmount = { { input.Symbol, input.Amount.Add(balance).Mul(-1) } }
            };
            return(new Empty());
        }
Example #7
0
        /// <summary>
        ///
        /// Will burn/destroy a certain amount of profits if `input.Period` is less than 0.
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public override Empty DistributeProfits(DistributeProfitsInput input)
        {
            Assert(input.Amount >= 0, "Amount must be greater than or equal to 0");

            Assert(input.Symbol != null && input.Symbol.Any(), "Invalid token symbol.");
            if (input.Symbol == null)
            {
                return(new Empty());                      // Just to avoid IDE warning.
            }
            var scheme = State.SchemeInfos[input.SchemeId];

            Assert(scheme != null, "Scheme not found.");
            if (scheme == null)
            {
                return(new Empty());                // Just to avoid IDE warning.
            }
            Assert(Context.Sender == scheme.Manager, "Only manager can distribute profits.");

            if (State.TokenContract.Value == null)
            {
                State.TokenContract.Value =
                    Context.GetContractAddressByName(SmartContractConstants.TokenContractSystemName);
            }

            var balance = State.TokenContract.GetBalance.Call(new GetBalanceInput
            {
                Owner  = scheme.VirtualAddress,
                Symbol = input.Symbol
            }).Balance;

            if (scheme.IsReleaseAllBalanceEveryTimeByDefault && input.Amount == 0)
            {
                // Distribute all from general ledger.
                Context.LogDebug(() =>
                                 $"Update distributing amount to {balance} because IsReleaseAllBalanceEveryTimeByDefault == true.");
                input.Amount = balance;
            }

            var totalShares = scheme.TotalShares;

            if (scheme.DelayDistributePeriodCount > 0)
            {
                scheme.CachedDelayTotalShares.Add(input.Period.Add(scheme.DelayDistributePeriodCount), totalShares);
                if (scheme.CachedDelayTotalShares.ContainsKey(input.Period))
                {
                    totalShares = scheme.CachedDelayTotalShares[input.Period];
                    scheme.CachedDelayTotalShares.Remove(input.Period);
                }
                else
                {
                    totalShares = 0;
                }
            }

            var releasingPeriod = scheme.CurrentPeriod;

            Assert(input.Period == releasingPeriod,
                   $"Invalid period. When release scheme {input.SchemeId.ToHex()} of period {input.Period}. Current period is {releasingPeriod}");

            var profitsReceivingVirtualAddress =
                GetDistributedPeriodProfitsVirtualAddress(scheme.VirtualAddress, releasingPeriod);

            if (input.Period < 0 || totalShares <= 0)
            {
                return(BurnProfits(input, scheme, scheme.VirtualAddress, profitsReceivingVirtualAddress));
            }

            Context.LogDebug(() => $"Receiving virtual address: {profitsReceivingVirtualAddress}");

            var distributedProfitInformation =
                UpdateDistributedProfits(input, profitsReceivingVirtualAddress, totalShares);

            Context.LogDebug(() =>
                             $"Distributed profit information of {input.SchemeId.ToHex()} in period {input.Period}, " +
                             $"total Shares {distributedProfitInformation.TotalShares}, total amount {distributedProfitInformation.ProfitsAmount} {input.Symbol}s");

            PerformDistributeProfits(input, scheme, totalShares, profitsReceivingVirtualAddress);

            scheme.CurrentPeriod = input.Period.Add(1);
            scheme.UndistributedProfits[input.Symbol] = balance.Sub(input.Amount);

            State.SchemeInfos[input.SchemeId] = scheme;

            return(new Empty());
        }
Example #8
0
        /// <summary>
        ///
        /// Will burn/destroy a certain amount of profits if `input.Period` is less than 0.
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public override Empty DistributeProfits(DistributeProfitsInput input)
        {
            if (input.AmountsMap.Any())
            {
                Assert(input.AmountsMap.All(a => !string.IsNullOrEmpty(a.Key)), "Invalid token symbol.");
            }

            var scheme = State.SchemeInfos[input.SchemeId];

            Assert(scheme != null, "Scheme not found.");

            // ReSharper disable once PossibleNullReferenceException
            Assert(Context.Sender == scheme.Manager || Context.Sender ==
                   Context.GetContractAddressByName(SmartContractConstants.TokenHolderContractSystemName),
                   "Only manager can distribute profits.");

            ValidateContractState(State.TokenContract, SmartContractConstants.TokenContractSystemName);

            var profitsMap = new Dictionary <string, long>();

            if (input.AmountsMap.Any())
            {
                foreach (var amount in input.AmountsMap)
                {
                    var actualAmount = amount.Value == 0
                        ? State.TokenContract.GetBalance.Call(new GetBalanceInput
                    {
                        Owner  = scheme.VirtualAddress,
                        Symbol = amount.Key
                    }).Balance
                        : amount.Value;
                    profitsMap.Add(amount.Key, actualAmount);
                }
            }
            else
            {
                if (scheme.IsReleaseAllBalanceEveryTimeByDefault && scheme.ReceivedTokenSymbols.Any())
                {
                    // Prepare to distribute all from general ledger.
                    foreach (var symbol in scheme.ReceivedTokenSymbols)
                    {
                        var balance = State.TokenContract.GetBalance.Call(new GetBalanceInput
                        {
                            Owner  = scheme.VirtualAddress,
                            Symbol = symbol
                        }).Balance;
                        profitsMap.Add(symbol, balance);
                    }
                }
            }

            var totalShares = scheme.TotalShares;

            if (scheme.DelayDistributePeriodCount > 0)
            {
                scheme.CachedDelayTotalShares.Add(input.Period.Add(scheme.DelayDistributePeriodCount), totalShares);
                if (scheme.CachedDelayTotalShares.ContainsKey(input.Period))
                {
                    totalShares = scheme.CachedDelayTotalShares[input.Period];
                    scheme.CachedDelayTotalShares.Remove(input.Period);
                }
                else
                {
                    totalShares = 0;
                }
            }

            var releasingPeriod = scheme.CurrentPeriod;

            Assert(input.Period == releasingPeriod,
                   $"Invalid period. When release scheme {input.SchemeId.ToHex()} of period {input.Period}. Current period is {releasingPeriod}");

            var profitsReceivingVirtualAddress =
                GetDistributedPeriodProfitsVirtualAddress(scheme.VirtualAddress, releasingPeriod);

            if (input.Period < 0 || totalShares <= 0)
            {
                return(BurnProfits(input.Period, profitsMap, scheme, profitsReceivingVirtualAddress));
            }

            Context.LogDebug(() => $"Receiving virtual address: {profitsReceivingVirtualAddress}");

            UpdateDistributedProfits(profitsMap, profitsReceivingVirtualAddress, totalShares);

            PerformDistributeProfits(profitsMap, scheme, totalShares, profitsReceivingVirtualAddress);

            scheme.CurrentPeriod = input.Period.Add(1);

            State.SchemeInfos[input.SchemeId] = scheme;

            return(new Empty());
        }