aelf技术点解读 | 分红合约接口实现方案
rpc CreateScheme (CreateSchemeInput) returns (aelf.Hash) { } ... message CreateSchemeInput { sint64 profit_receiving_due_period_count = 1; bool is_release_all_balance_every_time_by_default = 2; sint32 delay_distribute_period_count = 3; aelf.Address manager = 4; bool can_remove_beneficiary_directly = 5; } message Scheme { aelf.Address virtual_address = 1; sint64 total_shares = 2; map<string, sint64> undistributed_profits = 3;// token symbol -> amount sint64 current_period = 4; repeated SchemeBeneficiaryShare sub_schemes = 5; bool can_remove_beneficiary_directly = 6; sint64 profit_receiving_due_period_count = 7; bool is_release_all_balance_every_time_by_default = 8; aelf.Hash scheme_id = 9; sint32 delay_distribute_period_count = 10; map<sint64, sint64> cached_delay_total_shares = 11;// period -> total shares, max elements count should be delay_distribute_period_count aelf.Address manager = 12; } message SchemeBeneficiaryShare { aelf.Hash scheme_id = 1; sint64 shares = 2
/// <summary> /// Create a Scheme of profit distribution. /// At the first time, the scheme's id is unknown,it may create by transaction id and createdSchemeIds; /// </summary> /// <param name="input"></param> /// <returns></returns> public override Hash CreateScheme(CreateSchemeInput input) { ValidateContractState(State.TokenContract, SmartContractConstants.TokenContractSystemName); if (input.ProfitReceivingDuePeriodCount == 0) { // 为了避免分红合约State信息过多,设置一个过期时间。 input.ProfitReceivingDuePeriodCount = ProfitContractConstants.DefaultProfitReceivingDuePeriodCount; } var manager = input.Manager ?? Context.Sender; var schemeId = Context.TransactionId; // Why? Because one transaction may create many profit items via inline transactions. var createdSchemeIds = State.ManagingSchemeIds[manager]?.SchemeIds; if (createdSchemeIds != null && createdSchemeIds.Contains(schemeId)) { // So we choose this way to avoid profit id conflicts in aforementioned situation. schemeId = Hash.FromTwoHashes(schemeId, createdSchemeIds.Last()); } var scheme = GetNewScheme(input, schemeId, manager); State.SchemeInfos[schemeId] = scheme; var schemeIds = State.ManagingSchemeIds[scheme.Manager]; if (schemeIds == null) { schemeIds = new CreatedSchemeIds { SchemeIds = {schemeId} }; } else { schemeIds.SchemeIds.Add(schemeId); } State.ManagingSchemeIds[scheme.Manager] = schemeIds; Context.LogDebug(() => $"Created scheme {State.SchemeInfos[schemeId]}"); Context.Fire(new SchemeCreated { SchemeId = scheme.SchemeId, Manager = scheme.Manager, IsReleaseAllBalanceEveryTimeByDefault = scheme.IsReleaseAllBalanceEveryTimeByDefault, ProfitReceivingDuePeriodCount = scheme.ProfitReceivingDuePeriodCount, VirtualAddress = scheme.VirtualAddress }); return schem
rpc AddSubScheme (AddSubSchemeInput) returns (google.protobuf.Empty) { } rpc RemoveSubScheme (RemoveSubSchemeInput) returns (google.protobuf.Empty) { } ... message AddSubSchemeInput { aelf.Hash scheme_id = 1; aelf.Hash sub_scheme_id = 2; sint64 sub_scheme_shares = 3; } message RemoveSubSchemeInput { aelf.Hash scheme_id = 1; aelf.Hash sub_scheme_id = 2;
/// <summary> /// Add a child to a existed scheme. /// </summary> /// <param name="input">AddSubSchemeInput</param> /// <returns></returns> public override Empty AddSubScheme(AddSubSchemeInput input) { Assert(input.SchemeId != input.SubSchemeId, "Two schemes cannot be same."); Assert(input.SubSchemeShares > 0, "Shares of sub scheme should greater than 0."); var scheme = State.SchemeInfos[input.SchemeId]; Assert(scheme != null, "Scheme not found."); Assert(Context.Sender == scheme.Manager, "Only manager can add sub-scheme."); var subSchemeId = input.SubSchemeId; var subScheme = State.SchemeInfos[subSchemeId]; Assert(subScheme != null, "Sub scheme not found."); var subItemVirtualAddress = Context.ConvertVirtualAddressToContractAddress(subSchemeId); // Add profit details and total shares of the father scheme. AddBeneficiary(new AddBeneficiaryInput { SchemeId = input.SchemeId, BeneficiaryShare = new BeneficiaryShare { Beneficiary = subItemVirtualAddress, Shares = input.SubSchemeShares }, EndPeriod = long.MaxValue }); // Add a sub profit item. scheme.SubSchemes.Add(new SchemeBeneficiaryShare { SchemeId = input.SubSchemeId, Shares = input.SubSchemeShares }); State.SchemeInfos[input.SchemeId] = scheme; return new Empty(); } public override Empty RemoveSubScheme(RemoveSubSchemeInput input) { Assert(input.SchemeId != input.SubSchemeId, "Two schemes cannot be same."); var scheme = State.SchemeInfos[input.SchemeId]; Assert(scheme != null, "Scheme not found."); if (scheme == null) return new Empty(); Assert(Context.Sender == scheme.Manager, "Only manager can remove sub-scheme."); var subSchemeId = input.SubSchemeId; var subScheme = State.SchemeInfos[subSchemeId]; Assert(subScheme != null, "Sub scheme not found."); if (subScheme == null) return new Empty(); var subSchemeVirtualAddress = Context.ConvertVirtualAddressToContractAddress(subSchemeId); // Remove profit details State.ProfitDetailsMap[input.SchemeId][subSchemeVirtualAddress] = new ProfitDetails(); var shares = scheme.SubSchemes.Single(d => d.SchemeId == input.SubSchemeId); scheme.SubSchemes.Remove(shares); scheme.TotalShares = scheme.TotalShares.Sub(shares.Shares); State.SchemeInfos[input.SchemeId] = scheme; return new
rpc AddBeneficiary (AddBeneficiaryInput) returns (google.protobuf.Empty) { } rpc RemoveBeneficiary (RemoveBeneficiaryInput) returns (google.protobuf.Empty) { } rpc AddBeneficiaries (AddBeneficiariesInput) returns (google.protobuf.Empty) { } rpc RemoveBeneficiaries (RemoveBeneficiariesInput) returns (google.protobuf.Empty) { } ... message AddBeneficiaryInput { aelf.Hash scheme_id = 1; BeneficiaryShare beneficiary_share = 2; sint64 end_period = 3; } message RemoveBeneficiaryInput { aelf.Address beneficiary = 1; aelf.Hash scheme_id = 2; } message AddBeneficiariesInput { aelf.Hash scheme_id = 1; repeated BeneficiaryShare beneficiary_shares = 2; sint64 end_period = 4; } message RemoveBeneficiariesInput { repeated aelf.Address beneficiaries = 1; aelf.Hash scheme_id = 2; } message BeneficiaryShare { aelf.Address beneficiary = 1; sint64 shares =
public override Empty AddBeneficiary(AddBeneficiaryInput input) { AssertValidInput(input); if (input.BeneficiaryShare == null) return new Empty(); if (input.EndPeriod == 0) { // Which means this profit Beneficiary will never expired unless removed. input.EndPeriod = long.MaxValue; } var schemeId = input.SchemeId; var scheme = State.SchemeInfos[schemeId]; Assert(scheme != null, "Scheme not found."); if (scheme == null) return new Empty(); Assert( Context.Sender == scheme.Manager || Context.Sender == Context.GetContractAddressByName(SmartContractConstants.TokenHolderContractSystemName), "Only manager can add beneficiary."); Context.LogDebug(() => $"{input.SchemeId}.\n End Period: {input.EndPeriod}, Current Period: {scheme.CurrentPeriod}"); Assert(input.EndPeriod >= scheme.CurrentPeriod, "Invalid end period."); scheme.TotalShares = scheme.TotalShares.Add(input.BeneficiaryShare.Shares); State.SchemeInfos[schemeId] = scheme; var profitDetail = new ProfitDetail { StartPeriod = scheme.CurrentPeriod.Add(scheme.DelayDistributePeriodCount), EndPeriod = input.EndPeriod, Shares = input.BeneficiaryShare.Shares, }; var currentProfitDetails = State.ProfitDetailsMap[schemeId][input.BeneficiaryShare.Beneficiary]; if (currentProfitDetails == null) { currentProfitDetails = new ProfitDetails { Details = {profitDetail} }; } else { currentProfitDetails.Details.Add(profitDetail); } // Remove details too old. foreach (var detail in currentProfitDetails.Details.Where( d => d.EndPeriod != long.MaxValue && d.LastProfitPeriod >= d.EndPeriod && d.EndPeriod.Add(scheme.ProfitReceivingDuePeriodCount) < scheme.CurrentPeriod)) { currentProfitDetails.Details.Remove(detail); } State.ProfitDetailsMap[schemeId][input.BeneficiaryShare.Beneficiary] = currentProfitDetails; Context.LogDebug(() => $"Added {input.BeneficiaryShare.Shares} weights to scheme {input.SchemeId.ToHex()}: {profitDetail}"); return new Empty(); } public override Empty RemoveBeneficiary(RemoveBeneficiaryInput input) { Assert(input.SchemeId != null, "Invalid scheme id."); Assert(input.Beneficiary != null, "Invalid Beneficiary address."); var scheme = State.SchemeInfos[input.SchemeId]; Assert(scheme != null, "Scheme not found."); var currentDetail = State.ProfitDetailsMap[input.SchemeId][input.Beneficiary]; if (scheme == null || currentDetail == null) return new Empty(); Assert(Context.Sender == scheme.Manager || Context.Sender == Context.GetContractAddressByName(SmartContractConstants.TokenHolderContractSystemName), "Only manager can remove beneficiary."); var expiryDetails = scheme.CanRemoveBeneficiaryDirectly ? currentDetail.Details.ToList() : currentDetail.Details .Where(d => d.EndPeriod < scheme.CurrentPeriod && !d.IsWeightRemoved).ToList(); if (!expiryDetails.Any()) return new Empty(); var shares = expiryDetails.Sum(d => d.Shares); foreach (var expiryDetail in expiryDetails) { expiryDetail.IsWeightRemoved = true; if (expiryDetail.LastProfitPeriod >= scheme.CurrentPeriod) { currentDetail.Details.Remove(expiryDetail); } } // Clear old profit details. if (currentDetail.Details.Count != 0) { State.ProfitDetailsMap[input.SchemeId][input.Beneficiary] = currentDetail; } else { State.ProfitDetailsMap[input.SchemeId].Remove(input.Beneficiary); } scheme.TotalShares = scheme.TotalShares.Sub(shares); State.SchemeInfos[input.SchemeId] = scheme;
rpc ContributeProfits (ContributeProfitsInput) returns (google.protobuf.Empty) { } ... message ContributeProfitsInput { aelf.Hash scheme_id = 1; sint64 amount = 2; sint64 period = 3; string symbol = 4;
public override Empty ContributeProfits(ContributeProfitsInput input) { Assert(input.Symbol != null && input.Symbol.Any(), "Invalid token symbol."); Assert(input.Amount > 0, "Amount need to greater than 0."); 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. var virtualAddress = Context.ConvertVirtualAddressToContractAddress(input.SchemeId); if (input.Period == 0) { State.TokenContract.TransferFrom.Send(new TransferFromInput { From = Context.Sender, To = virtualAddress, Symbol = input.Symbol, Amount = input.Amount, Memo = $"Add {input.Amount} dividends." }); if (!scheme.UndistributedProfits.ContainsKey(input.Symbol)) { scheme.UndistributedProfits.Add(input.Symbol, input.Amount); } else { scheme.UndistributedProfits[input.Symbol] = scheme.UndistributedProfits[input.Symbol].Add(input.Amount); } State.SchemeInfos[input.SchemeId] = scheme; } else { var distributedPeriodProfitsVirtualAddress = GetDistributedPeriodProfitsVirtualAddress(virtualAddress, input.Period); var distributedProfitsInformation = State.DistributedProfitsMap[distributedPeriodProfitsVirtualAddress]; if (distributedProfitsInformation == null) { distributedProfitsInformation = new DistributedProfitsInfo { ProfitsAmount = {{input.Symbol, input.Amount}} }; } else { Assert(!distributedProfitsInformation.IsReleased, $"Scheme of period {input.Period} already released."); distributedProfitsInformation.ProfitsAmount[input.Symbol] = distributedProfitsInformation.ProfitsAmount[input.Symbol].Add(input.Amount); } State.TokenContract.TransferFrom.Send(new TransferFromInput { From = Context.Sender, To = distributedPeriodProfitsVirtualAddress, Symbol = input.Symbol, Amount = input.Amount, }); State.DistributedProfitsMap[distributedPeriodProfitsVirtualAddress] = distributedProfitsInformation; } return new Emp
rpc DistributeProfits (DistributeProfitsInput) returns (google.protobuf.Empty) { } ... message DistributeProfitsInput { aelf.Hash scheme_id = 1; sint64 period = 2; sint64 amount = 3; string symbol = 4;
/// <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 || Context.Sender == Context.GetContractAddressByName(SmartContractConstants.TokenHolderContractSystemName), "Only manager can distribute profits."); ValidateContractState(State.TokenContract, 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(); } 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(); } 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; } 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 = SafeCalculateProfits(subScheme.Shares, input.Amount, 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 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.Scheme
rpc ClaimProfits (ClaimProfitsInput) returns (google.protobuf.Empty) { } ... message ClaimProfitsInput { aelf.Hash scheme_id = 1; string symbol = 2; aelf.Address beneficiary = 3;
/// <summary> /// Gain the profit form SchemeId from Details.lastPeriod to scheme.currentPeriod - 1; /// </summary> /// <param name="input">ClaimProfitsInput</param> /// <returns></returns> public override Empty ClaimProfits(ClaimProfitsInput input) { 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."); var beneficiary = input.Beneficiary ?? Context.Sender; var profitDetails = State.ProfitDetailsMap[input.SchemeId][beneficiary]; Assert(profitDetails != null, "Profit details not found."); if (profitDetails == null || scheme == null) return new Empty(); // Just to avoid IDE warning. Context.LogDebug( () => $"{Context.Sender} is trying to profit {input.Symbol} from {input.SchemeId.ToHex()} for {beneficiary}."); var profitVirtualAddress = Context.ConvertVirtualAddressToContractAddress(input.SchemeId); var availableDetails = profitDetails.Details.Where(d => d.EndPeriod >= d.LastProfitPeriod).ToList(); var profitableDetails = availableDetails.Where(d => d.LastProfitPeriod < scheme.CurrentPeriod).ToList(); Context.LogDebug(() => $"Profitable details: {profitableDetails.Aggregate("\n", (profit1, profit2) => profit1.ToString() + "\n" + profit2)}"); // Only can get profit from last profit period to actual last period (profit.CurrentPeriod - 1), // because current period not released yet. for (var i = 0; i < Math.Min(ProfitContractConstants.ProfitReceivingLimitForEachTime, profitableDetails.Count); i++) { var profitDetail = profitableDetails[i]; if (profitDetail.LastProfitPeriod == 0) { // This detail never performed profit before. profitDetail.LastProfitPeriod = profitDetail.StartPeriod; } ProfitAllPeriods(scheme, input.Symbol, profitDetail, profitVirtualAddress, beneficiary); } State.ProfitDetailsMap[input.SchemeId][beneficiary] = new ProfitDetails {Details = {availableDetails}}; return new Empty(); } private long ProfitAllPeriods(Scheme scheme, string symbol, ProfitDetail profitDetail, Address profitVirtualAddress, Address beneficiary, bool isView = false) { var totalAmount = 0L; var lastProfitPeriod = profitDetail.LastProfitPeriod; for (var period = profitDetail.LastProfitPeriod; period <= (profitDetail.EndPeriod == long.MaxValue ? scheme.CurrentPeriod - 1 : Math.Min(scheme.CurrentPeriod - 1, profitDetail.EndPeriod)); period++) { var periodToPrint = period; var detailToPrint = profitDetail; var distributedPeriodProfitsVirtualAddress = GetDistributedPeriodProfitsVirtualAddress(profitVirtualAddress, period); var distributedProfitsInformation = State.DistributedProfitsMap[distributedPeriodProfitsVirtualAddress]; if (distributedProfitsInformation == null || distributedProfitsInformation.TotalShares == 0) { continue; } Context.LogDebug(() => $"Released profit information: {distributedProfitsInformation}"); var amount = SafeCalculateProfits(profitDetail.Shares, distributedProfitsInformation.ProfitsAmount[symbol], distributedProfitsInformation.TotalShares); if (!isView) { Context.LogDebug(() => $"{beneficiary} is profiting {amount} {symbol} tokens from {scheme.SchemeId.ToHex()} in period {periodToPrint}." + $"Sender's Shares: {detailToPrint.Shares}, total Shares: {distributedProfitsInformation.TotalShares}"); if (distributedProfitsInformation.IsReleased && amount > 0) { State.TokenContract.TransferFrom.Send(new TransferFromInput { From = distributedPeriodProfitsVirtualAddress, To = beneficiary, Symbol = symbol, Amount = amount }); } lastProfitPeriod = period + 1; } totalAmount = totalAmount.Add(amount); } profitDetail.LastProfitPeriod = lastProfitPeriod; return
rpc CreateScheme (CreateTokenHolderProfitSchemeInput) returns (google.protobuf.Empty) { } ... message CreateTokenHolderProfitSchemeInput { string symbol = 1; sint64 minimum_lock_minutes = 2; map<string, sint64> auto_distribute_threshold = 3;
public override Empty CreateScheme(CreateTokenHolderProfitSchemeInput input) { if (State.ProfitContract.Value == null) { State.ProfitContract.Value = Context.GetContractAddressByName(SmartContractConstants.ProfitContractSystemName); } State.ProfitContract.CreateScheme.Send(new CreateSchemeInput { Manager = Context.Sender, // 如果不指定发放分红额度,默认发放分红项目总账所有额度 IsReleaseAllBalanceEveryTimeByDefault = true, // 允许直接移除分红方案受益人 CanRemoveBeneficiaryDirectly = true }); State.TokenHolderProfitSchemes[Context.Sender] = new TokenHolderProfitScheme { Symbol = input.Symbol, // 最小锁仓时间 MinimumLockMinutes = input.MinimumLockMinutes, // 总账额度大于多少时自动发放 AutoDistributeThreshold = {input.AutoDistributeThreshold} }; return new Empty();
rpc AddBeneficiary (AddTokenHolderBeneficiaryInput) returns (google.protobuf.Empty) { } rpc RemoveBeneficiary (RemoveTokenHolderBeneficiaryInput) returns (google.protobuf.Empty) { } rpc ContributeProfits (ContributeProfitsInput) returns (google.protobuf.Empty) { } rpc DistributeProfits (DistributeProfitsInput) returns (google.protobuf.Empty) { } ... message AddTokenHolderBeneficiaryInput { aelf.Address beneficiary = 1; sint64 shares = 2; } message RemoveTokenHolderBeneficiaryInput { aelf.Address beneficiary = 1; sint64 amount = 2; } message ContributeProfitsInput { aelf.Address scheme_manager = 1; sint64 amount = 2; string symbol = 3; } message DistributeProfitsInput { aelf.Address scheme_manager = 1; string symbol = 2; } message TokenHolderProfitScheme { string symbol = 1; aelf.Hash scheme_id = 2; sint64 period = 3; sint64 minimum_lock_minutes = 4; map<string, sint64> auto_distribute_threshold =
public override Empty AddBeneficiary(AddTokenHolderBeneficiaryInput input) { var scheme = GetValidScheme(Context.Sender); var detail = State.ProfitContract.GetProfitDetails.Call(new GetProfitDetailsInput { SchemeId = scheme.SchemeId, Beneficiary = input.Beneficiary }); var shares = input.Shares; if (detail.Details.Any()) { // Only keep one detail. // 将之前的Shares移除 State.ProfitContract.RemoveBeneficiary.Send(new RemoveBeneficiaryInput { SchemeId = scheme.SchemeId, Beneficiary = input.Beneficiary }); shares.Add(detail.Details.Single().Shares); } // 添加更新后的Shares State.ProfitContract.AddBeneficiary.Send(new AddBeneficiaryInput { SchemeId = scheme.SchemeId, BeneficiaryShare = new BeneficiaryShare { Beneficiary = input.Beneficiary, Shares = shares } }); return new Empty(); } public override Empty RemoveBeneficiary(RemoveTokenHolderBeneficiaryInput input) { var scheme = GetValidScheme(Context.Sender); var detail = State.ProfitContract.GetProfitDetails.Call(new GetProfitDetailsInput { Beneficiary = input.Beneficiary, SchemeId = scheme.SchemeId }).Details.Single(); var lockedAmount = detail.Shares; State.ProfitContract.RemoveBeneficiary.Send(new RemoveBeneficiaryInput { SchemeId = scheme.SchemeId, Beneficiary = input.Beneficiary }); if (lockedAmount > input.Amount && input.Amount != 0) // If input.Amount == 0, means just remove this beneficiary. { State.ProfitContract.AddBeneficiary.Send(new AddBeneficiaryInput { SchemeId = scheme.SchemeId, BeneficiaryShare = new BeneficiaryShare { Beneficiary = input.Beneficiary, Shares = lockedAmount.Sub(input.Amount) } }); } return new Empty(); } public override Empty ContributeProfits(ContributeProfitsInput input) { var scheme = GetValidScheme(input.SchemeManager); if (State.TokenContract.Value == null) { State.TokenContract.Value = Context.GetContractAddressByName(SmartContractConstants.TokenContractSystemName); } State.TokenContract.TransferFrom.Send(new TransferFromInput { From = Context.Sender, To = Context.Self, Symbol = input.Symbol, Amount = input.Amount }); State.TokenContract.Approve.Send(new ApproveInput { Spender = State.ProfitContract.Value, Symbol = input.Symbol, Amount = input.Amount }); State.ProfitContract.ContributeProfits.Send(new Profit.ContributeProfitsInput { SchemeId = scheme.SchemeId, Symbol = input.Symbol, Amount = input.Amount }); return new Empty(); } 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 Emp
rpc RegisterForProfits (RegisterForProfitsInput) returns (google.protobuf.Empty) { } rpc Withdraw (aelf.Address) returns (google.protobuf.Empty) { } rpc ClaimProfits (ClaimProfitsInput) returns (google.protobuf.Empty) { } ... message RegisterForProfitsInput { aelf.Address scheme_manager = 1; sint64 amount = 2; } message ClaimProfitsInput { aelf.Address scheme_manager = 1; aelf.Address beneficiary = 2; string symbol = 3;
public override Empty RegisterForProfits(RegisterForProfitsInput input) { var scheme = GetValidScheme(input.SchemeManager); if (State.TokenContract.Value == null) { State.TokenContract.Value = Context.GetContractAddressByName(SmartContractConstants.TokenContractSystemName); } State.TokenContract.Lock.Send(new LockInput { LockId = Context.TransactionId, Symbol = scheme.Symbol, Address = Context.Sender, Amount = input.Amount, }); State.LockIds[input.SchemeManager][Context.Sender] = Context.TransactionId; State.LockTimestamp[Context.TransactionId] = Context.CurrentBlockTime; State.ProfitContract.AddBeneficiary.Send(new AddBeneficiaryInput { SchemeId = scheme.SchemeId, BeneficiaryShare = new BeneficiaryShare { Beneficiary = Context.Sender, Shares = input.Amount } }); // Check auto-distribute threshold. foreach (var threshold in scheme.AutoDistributeThreshold) { var originScheme = State.ProfitContract.GetScheme.Call(scheme.SchemeId); var balance = State.TokenContract.GetBalance.Call(new GetBalanceInput { Owner = originScheme.VirtualAddress, Symbol = threshold.Key }).Balance; if (balance < threshold.Value) continue; State.ProfitContract.DistributeProfits.Send(new Profit.DistributeProfitsInput { SchemeId = scheme.SchemeId, Symbol = threshold.Key, Period = scheme.Period.Add(1) }); scheme.Period = scheme.Period.Add(1); State.TokenHolderProfitSchemes[input.SchemeManager] = scheme; } return new Empty(); } public override Empty Withdraw(Address input) { var scheme = GetValidScheme(input); if (State.TokenContract.Value == null) { State.TokenContract.Value = Context.GetContractAddressByName(SmartContractConstants.TokenContractSystemName); } var amount = State.TokenContract.GetLockedAmount.Call(new GetLockedAmountInput { Address = Context.Sender, LockId = State.LockIds[input][Context.Sender], Symbol = scheme.Symbol }).Amount; var lockId = State.LockIds[input][Context.Sender]; Assert(State.LockTimestamp[lockId].AddMinutes(scheme.MinimumLockMinutes) < Context.CurrentBlockTime, "Cannot withdraw."); State.TokenContract.Unlock.Send(new UnlockInput { Address = Context.Sender, LockId = lockId, Amount = amount, Symbol = scheme.Symbol }); State.LockIds[input].Remove(Context.Sender); State.ProfitContract.RemoveBeneficiary.Send(new RemoveBeneficiaryInput { SchemeId = scheme.SchemeId, Beneficiary = Context.Sender }); return new Empty(); } public override Empty ClaimProfits(ClaimProfitsInput input) { var scheme = GetValidScheme(input.SchemeManager); var beneficiary = input.Beneficiary ?? Context.Sender; State.ProfitContract.ClaimProfits.Send(new Profit.ClaimProfitsInput { SchemeId = scheme.SchemeId, Beneficiary = beneficiary, Symbol = input.Symbol }); return new Empt
public override Empty InitialTreasuryContract(Empty input) { Assert(!State.Initialized.Value, "Already initialized."); State.ProfitContract.Value = Context.GetContractAddressByName(SmartContractConstants.ProfitContractSystemName); // 创建七个分红方案。 // Create profit items: `Treasury`, `CitizenWelfare`, `BackupSubsidy`, `MinerReward`, // `MinerBasicReward`, `MinerVotesWeightReward`, `ReElectedMinerReward` var profitItemNameList = new List<string> { "Treasury", "MinerReward", "Subsidy", "Welfare", "Basic Reward", "Votes Weight Reward", "Re-Election Reward" }; for (var i = 0; i < 7; i++) { var index = i; Context.LogDebug(() => profitItemNameList[index]); State.ProfitContract.CreateScheme.Send(new CreateSchemeInput { IsReleaseAllBalanceEveryTimeByDefault = true, // Distribution of Citizen Welfare will delay one period. DelayDistributePeriodCount = i == 3 ? 1 : 0, }); } State.Initialized.Value = true; return new Empty(); } public override Empty InitialMiningRewardProfitItem(Empty input) { Assert(State.TreasuryHash.Value == null, "Already initialized."); var managingSchemeIds = State.ProfitContract.GetManagingSchemeIds.Call(new GetManagingSchemeIdsInput { Manager = Context.Self }).SchemeIds; Assert(managingSchemeIds.Count == 7, "Incorrect schemes count."); State.TreasuryHash.Value = managingSchemeIds[0]; State.RewardHash.Value = managingSchemeIds[1]; State.SubsidyHash.Value = managingSchemeIds[2]; State.WelfareHash.Value = managingSchemeIds[3]; State.BasicRewardHash.Value = managingSchemeIds[4]; State.VotesWeightRewardHash.Value = managingSchemeIds[5]; State.ReElectionRewardHash.Value = managingSchemeIds[6]; var electionContractAddress = Context.GetContractAddressByName(SmartContractConstants.ElectionContractSystemName); if (electionContractAddress != null) { State.ProfitContract.ResetManager.Send(new ResetManagerInput { SchemeId = managingSchemeIds[2], NewManager = electionContractAddress }); State.ProfitContract.ResetManager.Send(new ResetManagerInput { SchemeId = managingSchemeIds[3], NewManager = electionContractAddress }); } BuildTreasury(); var treasuryVirtualAddress = Address.FromPublicKey(State.ProfitContract.Value.Value.Concat( managingSchemeIds[0].Value.ToByteArray().ComputeHash()).ToArray()); State.TreasuryVirtualAddress.Value = treasuryVirtualAddress; return new Empty(); } // 构建级联关系 private void BuildTreasury() { // Register `MinerReward` to `Treasury` State.ProfitContract.AddSubScheme.Send(new AddSubSchemeInput { SchemeId = State.TreasuryHash.Value, SubSchemeId = State.RewardHash.Value, SubSchemeShares = TreasuryContractConstants.MinerRewardWeight }); // Register `BackupSubsidy` to `Treasury` State.ProfitContract.AddSubScheme.Send(new AddSubSchemeInput { SchemeId = State.TreasuryHash.Value, SubSchemeId = State.SubsidyHash.Value, SubSchemeShares = TreasuryContractConstants.BackupSubsidyWeight }); // Register `CitizenWelfare` to `Treasury` State.ProfitContract.AddSubScheme.Send(new AddSubSchemeInput { SchemeId = State.TreasuryHash.Value, SubSchemeId = State.WelfareHash.Value, SubSchemeShares = TreasuryContractConstants.CitizenWelfareWeight }); // Register `MinerBasicReward` to `MinerReward` State.ProfitContract.AddSubScheme.Send(new AddSubSchemeInput { SchemeId = State.RewardHash.Value, SubSchemeId = State.BasicRewardHash.Value, SubSchemeShares = TreasuryContractConstants.BasicMinerRewardWeight }); // Register `MinerVotesWeightReward` to `MinerReward` State.ProfitContract.AddSubScheme.Send(new AddSubSchemeInput { SchemeId = State.RewardHash.Value, SubSchemeId = State.VotesWeightRewardHash.Value, SubSchemeShares = TreasuryContractConstants.VotesWeightRewardWeight }); // Register `ReElectionMinerReward` to `MinerReward` State.ProfitContract.AddSubScheme.Send(new AddSubSchemeInput { SchemeId = State.RewardHash.Value, SubSchemeId = State.ReElectionRewardHash.Value, SubSchemeShares = TreasuryContractConstants.ReElectionReward
rpc Donate (DonateInput) returns (google.protobuf.Empty) { } ... message DonateInput { string symbol = 1; sint64 amount = 2;
public override Empty Donate(DonateInput input) { Assert(input.Amount > 0, "Invalid amount of donating. Amount needs to be greater than 0."); if (State.TokenContract.Value == null) { State.TokenContract.Value = Context.GetContractAddressByName(SmartContractConstants.TokenContractSystemName); } if (State.TokenConverterContract.Value == null) { State.TokenConverterContract.Value = Context.GetContractAddressByName(SmartContractConstants.TokenConverterContractSystemName); } var isNativeSymbol = input.Symbol == Context.Variables.NativeSymbol; var connector = State.TokenConverterContract.GetConnector.Call(new TokenSymbol {Symbol = input.Symbol}); var canExchangeWithNativeSymbol = connector.RelatedSymbol != string.Empty; State.TokenContract.TransferFrom.Send(new TransferFromInput { From = Context.Sender, To = isNativeSymbol || !canExchangeWithNativeSymbol ? State.TreasuryVirtualAddress.Value : Context.Self, Symbol = input.Symbol, Amount = input.Amount, Memo = "Donate to treasury.", }); Context.Fire(new DonationReceived { From = Context.Sender, To = isNativeSymbol ? State.TreasuryVirtualAddress.Value : Context.Self, Symbol = input.Symbol, Amount = input.Amount, Memo = "Donate to treasury." }); if (input.Symbol != Context.Variables.NativeSymbol && canExchangeWithNativeSymbol) { ConvertToNativeToken(input.Symbol, input.Amount); } return new Empty
rpc DonateAll (DonateAllInput) returns (google.protobuf.Empty) { } ... message DonateAllInput { string symbol = 1;
- 使用一个inline交易调用DonateAll,参数填ELF。
private void ConvertToNativeToken(string symbol, long amount) { State.TokenConverterContract.Sell.Send(new SellInput { Symbol = symbol, Amount = amount }); Context.SendInline(Context.Self, nameof(DonateAll), new DonateAllInput { Symbol = Context.Variables.NativeSymbol }); } public override Empty DonateAll(DonateAllInput input) { if (State.TokenContract.Value == null) { State.TokenContract.Value = Context.GetContractAddressByName(SmartContractConstants.TokenContractSystemName); } var balance = State.TokenContract.GetBalance.Call(new GetBalanceInput { Symbol = input.Symbol, Owner = Context.Sender }).Balance; Donate(new DonateInput { Symbol = input.Symbol, Amount = balance }); return new Empty(
/// <summary> /// Call the Vote function of VoteContract to do a voting. /// </summary> /// <param name="input"></param> /// <returns></returns> public override Empty Vote(VoteMinerInput input) { // Check candidate information map instead of candidates. var targetInformation = State.CandidateInformationMap[input.CandidatePubkey]; AssertValidCandidateInformation(targetInformation); var lockSeconds = (input.EndTimestamp - Context.CurrentBlockTime).Seconds; AssertValidLockSeconds(lockSeconds); State.LockTimeMap[Context.TransactionId] = lockSeconds; // ... CallTokenContractLock(input.Amount); CallTokenContractIssue(input.Amount); CallVoteContractVote(input.Amount, input.CandidatePubkey); var votesWeight = GetVotesWeight(input.Amount, lockSeconds); CallProfitContractAddBeneficiary(votesWeight, lockSeconds); // ... return new Empty(); } private void CallProfitContractAddBeneficiary(long votesWeight, long lockSeconds) { State.ProfitContract.AddBeneficiary.Send(new AddBeneficiaryInput { SchemeId = State.WelfareHash.Value, BeneficiaryShare = new BeneficiaryShare { Beneficiary = Context.Sender, Shares = votesWeight }, EndPeriod = GetEndPeriod(lockSeconds)
/// <summary> /// Actually this method is for adding an option of the Voting Item. /// Thus the limitation of candidates will be limited by the capacity of voting options. /// </summary> /// <param name="input"></param> /// <returns></returns> public override Empty AnnounceElection(Empty input) { var recoveredPublicKey = Context.RecoverPublicKey(); AnnounceElection(recoveredPublicKey); var pubkey = recoveredPublicKey.ToHex(); LockCandidateNativeToken(); AddCandidateAsOption(pubkey); if (State.Candidates.Value.Value.Count <= GetValidationDataCenterCount()) { State.DataCentersRankingList.Value.DataCenters.Add(pubkey, 0); RegisterCandidateToSubsidyProfitScheme(); } return new Empty(); } private void RegisterCandidateToSubsidyProfitScheme() { if (State.ProfitContract.Value == null) { State.ProfitContract.Value = Context.GetContractAddressByName(SmartContractConstants.ProfitContractSystemName); } // Add 1 Shares for this candidate in subsidy profit item. State.ProfitContract.AddBeneficiary.Send(new AddBeneficiaryInput { SchemeId = State.SubsidyHash.Value, BeneficiaryShare = new BeneficiaryShare {Beneficiary = Context.Sender, Shares = 1}
private void UpdateTreasurySubItemsSharesBeforeDistribution(Round previousTermInformation) { var previousPreviousTermInformation = State.AEDPoSContract.GetPreviousTermInformation.Call(new SInt64Value { Value = previousTermInformation.TermNumber.Sub(1) }); UpdateBasicMinerRewardWeights(new List<Round> {previousPreviousTermInformation, previousTermInformation}); } /// <summary> /// Remove current total shares of Basic Reward, /// Add new shares for miners of next term. /// 1 share for each miner. /// </summary> /// <param name="previousTermInformation"></param> private void UpdateBasicMinerRewardWeights(IReadOnlyCollection<Round> previousTermInformation) { if (previousTermInformation.First().RealTimeMinersInformation != null) { State.ProfitContract.RemoveBeneficiaries.Send(new RemoveBeneficiariesInput { SchemeId = State.BasicRewardHash.Value, Beneficiaries = {previousTermInformation.First().RealTimeMinersInformation.Keys.Select(k => Address.FromPublicKey(ByteArrayHelper.HexStringToByteArray(k)))} }); } // Manage weights of `MinerBasicReward` State.ProfitContract.AddBeneficiaries.Send(new AddBeneficiariesInput { SchemeId = State.BasicRewardHash.Value, EndPeriod = previousTermInformation.Last().TermNumber, BeneficiaryShares = { previousTermInformation.Last().RealTimeMinersInformation.Values.Select(i => new BeneficiaryShare { Beneficiary = Address.FromPublicKey(ByteArrayHelper.HexStringToByteArray(i.Pubkey)), Shares = i.ProducedBlocks }) } });
private void UpdateTreasurySubItemsSharesAfterDistribution(Round previousTermInformation) { var victories = State.ElectionContract.GetVictories.Call(new Empty()).Value.Select(bs => bs.ToHex()) .ToList(); UpdateReElectionRewardWeights(previousTermInformation, victories); UpdateVotesWeightRewardWeights(previousTermInformation, victories); } /// <summary> /// Remove current total shares of Re-Election Reward, /// Add shares to re-elected miners based on their continual appointment count. /// </summary> /// <param name="previousTermInformation"></param> /// <param name="victories"></param> private void UpdateReElectionRewardWeights(Round previousTermInformation, ICollection<string> victories) { var previousMinerAddresses = previousTermInformation.RealTimeMinersInformation.Keys .Select(k => Address.FromPublicKey(ByteArrayHelper.HexStringToByteArray(k))).ToList(); var reElectionRewardProfitSubBeneficiaries = new RemoveBeneficiariesInput { SchemeId = State.ReElectionRewardHash.Value, Beneficiaries = {previousMinerAddresses} }; State.ProfitContract.RemoveBeneficiaries.Send(reElectionRewardProfitSubBeneficiaries); var minerReElectionInformation = State.MinerReElectionInformation.Value ?? InitialMinerReElectionInformation(previousTermInformation.RealTimeMinersInformation.Keys); AddBeneficiariesForReElectionScheme(previousTermInformation.TermNumber.Add(1), victories, minerReElectionInformation); var recordedMiners = minerReElectionInformation.Clone().ContinualAppointmentTimes.Keys; foreach (var miner in recordedMiners) { if (!victories.Contains(miner)) { minerReElectionInformation.ContinualAppointmentTimes.Remove(miner); } } State.MinerReElectionInformation.Value = minerReElectionInformation; } private void AddBeneficiariesForReElectionScheme(long endPeriod, IEnumerable<string> victories, MinerReElectionInformation minerReElectionInformation) { var reElectionProfitAddBeneficiaries = new AddBeneficiariesInput { SchemeId = State.ReElectionRewardHash.Value, EndPeriod = endPeriod }; foreach (var victory in victories) { if (minerReElectionInformation.ContinualAppointmentTimes.ContainsKey(victory)) { var minerAddress = Address.FromPublicKey(ByteArrayHelper.HexStringToByteArray(victory)); var continualAppointmentCount = minerReElectionInformation.ContinualAppointmentTimes[victory].Add(1); minerReElectionInformation.ContinualAppointmentTimes[victory] = continualAppointmentCount; reElectionProfitAddBeneficiaries.BeneficiaryShares.Add(new BeneficiaryShare { Beneficiary = minerAddress, Shares = Math.Min(continualAppointmentCount, TreasuryContractConstants.MaximumReElectionRewardShare) }); } else { minerReElectionInformation.ContinualAppointmentTimes.Add(victory, 0); } } if (reElectionProfitAddBeneficiaries.BeneficiaryShares.Any()) { State.ProfitContract.AddBeneficiaries.Send(reElectionProfitAddBeneficiaries); } } private MinerReElectionInformation InitialMinerReElectionInformation(ICollection<string> previousMiners) { var information = new MinerReElectionInformation(); foreach (var previousMiner in previousMiners) { information.ContinualAppointmentTimes.Add(previousMiner, 0); } return information; } /// <summary> /// Remove current total shares of Votes Weight Reward, /// Add shares to current miners based on votes they obtained. /// </summary> /// <param name="previousTermInformation"></param> /// <param name="victories"></param> private void UpdateVotesWeightRewardWeights(Round previousTermInformation, IEnumerable<string> victories) { var previousMinerAddresses = previousTermInformation.RealTimeMinersInformation.Keys .Select(k => Address.FromPublicKey(ByteArrayHelper.HexStringToByteArray(k))).ToList(); var votesWeightRewardProfitSubBeneficiaries = new RemoveBeneficiariesInput { SchemeId = State.VotesWeightRewardHash.Value, Beneficiaries = {previousMinerAddresses} }; State.ProfitContract.RemoveBeneficiaries.Send(votesWeightRewardProfitSubBeneficiaries); var votesWeightRewardProfitAddBeneficiaries = new AddBeneficiariesInput { SchemeId = State.VotesWeightRewardHash.Value, EndPeriod = previousTermInformation.TermNumber.Add(1) }; var dataCenterRankingList = State.ElectionContract.GetDataCenterRankingList.Call(new Empty()); foreach (var victory in victories) { var obtainedVotes = 0L; if (dataCenterRankingList.DataCenters.ContainsKey(victory)) { obtainedVotes = dataCenterRankingList.DataCenters[victory]; } var minerAddress = Address.FromPublicKey(ByteArrayHelper.HexStringToByteArray(victory)); if (obtainedVotes > 0) { votesWeightRewardProfitAddBeneficiaries.BeneficiaryShares.Add(new BeneficiaryShare { Beneficiary = minerAddress, Shares = obtainedVotes }); } } if (votesWeightRewardProfitAddBeneficiaries.BeneficiaryShares.Any()) { State.ProfitContract.AddBeneficiaries.Send(votesWeightRewardProfitAddBenefic
rpc Release (ReleaseInput) returns (google.protobuf.Empty) { } ... message ReleaseInput { sint64 term_number = 1;
public override Empty Release(ReleaseInput input) { MaybeLoadAEDPoSContractAddress(); Assert( Context.Sender == State.AEDPoSContract.Value, "Only aelf Consensus Contract can release profits from Treasury."); State.ProfitContract.DistributeProfits.Send(new DistributeProfitsInput { SchemeId = State.TreasuryHash.Value, Period = input.TermNumber, Symbol = Context.Variables.NativeSymbol }); MaybeLoadElectionContractAddress(); var previousTermInformation = State.AEDPoSContract.GetPreviousTermInformation.Call(new SInt64Value { Value = input.TermNumber }); UpdateTreasurySubItemsSharesBeforeDistribution(previousTermInformation); ReleaseTreasurySubProfitItems(input.TermNumber); UpdateTreasurySubItemsSharesAfterDistribution(previousTermInformation); return new Empty(); } private void ReleaseTreasurySubProfitItems(long termNumber) { State.ProfitContract.DistributeProfits.Send(new DistributeProfitsInput { SchemeId = State.RewardHash.Value, Period = termNumber, Symbol = Context.Variables.NativeSymbol }); State.ProfitContract.DistributeProfits.Send(new DistributeProfitsInput { SchemeId = State.BasicRewardHash.Value, Period = termNumber, Symbol = Context.Variables.NativeSymbol }); State.ProfitContract.DistributeProfits.Send(new DistributeProfitsInput { SchemeId = State.VotesWeightRewardHash.Value, Period = termNumber, Symbol = Context.Variables.NativeSymbol }); State.ProfitContract.DistributeProfits.Send(new DistributeProfitsInput { SchemeId = State.ReElectionRewardHash.Value, Period = termNumber, Symbol = Context.Variables.NativeSymbol })