aelf技术点解读 | 分红合约接口实现方案

本文详细介绍了aelf区块链平台上的分红合约设计,包括如何创建分红方案、管理子分红方案和受益人,以及分红的发放流程。分红合约通过统一的接口实现,允许节点和DApp按股份比例分配奖励。分红方案包含虚拟地址、受益人管理、代币分配策略等功能,同时支持子分红方案的级联。此外,文章还讨论了主链分红池的构建和维护,包括添加分红、配置子分红方案和受益人,以及在不同节点状态下的分红释放机制。
摘要由CSDN通过智能技术生成

Profit合约:统一的分红管理方案
概要
由于aelf主链采用DPoS共识机制,通过持有代币或者锁仓来获得权益是aelf治理模型中重要组成部分。这就产生了一个需求:实现一个能够统一管理分红的标准流程,并将其作为一个基础的智能合约。这个合约在创世区块中即部署于链上,其应用包括但不限于:生产节点在某一届任期结束时根据其区块生产数量(以此作为权重)获得相应奖励,选民通过节点竞选投票所质押ELF来分享相应的奖励,DApp合约允许用户通过抵押Token来分享合约盈利。

分红方案即代币分配策略:任何地址都可以成为分红方案(profit scheme)的管理者(manager)。每个管理者(manager)都可以为该分红方案添加受益人(beneficiary),并为每个受益人设定股份(shares)。之后,当分红项目创建者对其项目受益人发放(distribute)分红时,将按其对应的股份进行代币分配。每次分红结束后,分红方案的账期(period)即加一,根据具体分红数额会在该账期对应的虚拟地址(virtual address)上增加余额,持有股份的账户可以从中获得相应分红。分红的受益人不仅可以是账户地址,也可以是另一个分红方案,子分红方案所获分红可直接打进其总账(general ledger)。分红方案之间可以进行级联。

进一步阐述几个概念:

分红方案(profit scheme):通过分红合约创建出来的代币分配中心。

分红方案管理者(manager):管理分红方案的受益人及其相应股份。

分红受益人(beneficiary):aelf区块链上用来接收分红代币的账户地址。要注意的是,受益人的分红需要通过发送交易来获取(需填入对应分红方案的id,交易由自己或他人发送皆可)。

分红方案虚拟地址(virtual address):每个分红方案都会通过其唯一标识(scheme id)映射一个虚拟地址,这个地址仅用来释放分红,没有对应的公私钥对(公私钥对碰撞出来的概率可以忽略不计)。

子分红方案(sub profit item):这是一个相对概念,每个分红方案都可能成为子分红方案。子分红方案可持有其父分红方案股份,这样父分红方案在释放分红时,子分红方案的虚拟地址会获得相应代币。

获取分红(claim profits):作为一个普通用户,需要自行发送交易来获取自己应得的分红,这是为了避免注册的接收地址过多,释放分红的交易执行超时。

股份(shares):股份是每个分红受益人能够获取相应分红比例的证明,即某受益人分红数量 = 总分红 * 该受益人持有股份 / 总股份。

发放分红(distribute profits):将分红方案虚拟地址上的余额通过Bancor合约转化为ELF,并Transfer给分红接收地址的过程。

账期(period):账期时长由分红方案的管理者自行控制,发放分红后账期自行增一。

国库(Treasury):可能是aelf区块链中额度最大的分红方案,其主要管理者为Treasury合约,另有两个子分红方案相应的管理者为Election合约。其分红额度来源于区块生产奖励,当前每生产一个区块,分红额度即增加0.125个ELF,在本届任期结束的时候统一打入Treasury scheme总账,随后Treasury合约和Election合约负责维护七个分红方案。
方法解读
创建分红方案
顾名思义,该接口用来创建分红方案。接口如下:
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;
}
一个分红方案,包含以下属性:
一个唯一的虚拟地址,作为该分红方案的总账地址;
总股份;
尚未发放余额(可能移除);
当前账期期数;
子分红方案信息;
是否允许直接移除分红受益人(无视其可领取期数);
分红保留期数(过期即无法领取);
默认发放总账上某token对应的全部额度;
分红方案唯一标识;
延迟发放期数;
延迟发放分红所需缓存上的被推迟发放总股份;
管理者。
///
/// Create a Scheme of profit distribution.
/// At the first time, the scheme’s id is unknown,it may create by transaction id and createdSchemeIds;
///
///
///
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 schemeId;

}
子分红方案管理
用以添加和删除子分红方案。
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;
}
其中,添加子分红方案需要分别填入两个发生级联关系的分红方案的id,然后需输入子分红项目所占股份。而移除级联关系只需要分别输入两个分红方案的id即可。
///
/// Add a child to a existed scheme.
///
/// AddSubSchemeInput
///
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 Empty();

}
受益人管理
用以添加和删除分红受益人,为方便起见提供了批量管理接口。
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 = 2;
}
在添加分红受益人时,除了需要指定分红方案id、分红受益人地址和股份,还可以指定该受益人能够接收分红的最后账期期数,默认是一经添加,即能永久收到分红,直到分红方案管理者调用RemoveBeneficiary方法将其股份移除(如果该分红方案的can_remove_beneficiary_directly属性值为true,可以直接移除全部股份)。
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 = 
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
AELF智能合约支持多种语言,开发人员可以根据自己的需求选择最适合的语言进行开发。 AELF最终将支持多种语言,以便开发人员能够更轻松地扩展和定制系统。从引用中可以看到,AELF提供了易于使用的工具和框架来定制链和编写智能合约。 在AELF区块链开发指南中,引用提到了以太坊合约如何转换为AELF合约的例子。开发者可以参考这个教程,将Solidity语言翻译为C#语言,从而完成以太坊合约到AELF合约的转移。同时,在AELFGithub仓库中,引用提供了关于Quadratic Funding合约的示例代码,供开发者参考。 因此,AELF智能合约支持多种语言,开发人员可以根据自己的需求和喜好选择合适的语言进行开发。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [AElf:可扩展的云计算区块链平台](https://download.csdn.net/download/weixin_42099815/15061556)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [aelf区块链开发指南(一) | 如何将以太坊合约转为aelf合约?](https://blog.csdn.net/qukuailianaihao/article/details/122044917)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值