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()); }
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; }
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 }); } }
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); }
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()); }
/// <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()); }
/// <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()); }