Dividend Pool
ACS10 - Dividend Pool Standard#
ACS10 facilitates the creation and management of dividend pools within a contract.
Interface#
To create a dividend pool, implement these optional interfaces:
Methods#
Method Name | Request Type | Response Type | Description |
---|---|---|---|
Donate | acs10.DonateInput | google.protobuf.Empty | Transfers tokens from the caller to the dividend pool. Converts non-native tokens to native tokens if required. |
Release | acs10.ReleaseInput | google.protobuf.Empty | Releases dividends based on the specified period number. |
SetSymbolList | acs10.SymbolList | google.protobuf.Empty | Sets the list of token symbols supported by the dividend pool. |
GetSymbolList | google.protobuf.Empty | acs10.SymbolList | Retrieves the list of token symbols supported by the dividend pool. |
GetUndistributedDividends | google.protobuf.Empty | acs10.Dividends | Queries the balance of undistributed tokens according to the symbol list. |
GetDividends | google.protobuf.Int64Value | acs10.Dividends | Queries dividend information based on the specified height. |
Types#
acs10.Dividends
Field | Type | Description | Label |
---|---|---|---|
value | Dividends.ValueEntry | The dividends, symbol -> amount. | repeated |
acs10.Dividends.ValueEntry
Field | Type | Description | Label |
---|---|---|---|
key | string | ||
value | int64 |
acs10.DonateInput
Field | Type | Description | Label |
---|---|---|---|
symbol | string | The token symbol to donate. | |
amount | int64 | The amount to donate. |
acs10.DonationReceived
Field | Type | Description | Label |
---|---|---|---|
from | aelf.Address | The address of donors. | |
pool_contract | aelf.Address | The address of dividend pool. | |
symbol | string | The token symbol Donated. | |
amount | int64 | The amount Donated. |
acs10.ReleaseInput
Field | Type | Description | Label |
---|---|---|---|
period_number | int64 | The period number to release. |
acs10.SymbolList
Field | Type | Description | Label |
---|---|---|---|
value | string | The token symbol list. | repeated |
aelf.Address
Field | Type | Description | Label |
---|---|---|---|
value | bytes |
aelf.BinaryMerkleTree
Field | Type | Description | Label |
---|---|---|---|
nodes | Hash | The leaf nodes. | |
root | Hash | The root node hash. | repeated |
leaf_count | int32 | The count of leaf node. |
aelf.Hash
Field | Type | Description | Label |
---|---|---|---|
value | bytes |
aelf.LogEvent
Field | Type | Description | Label |
---|---|---|---|
address | Address | The contract address. | |
name | string | The name of the log event. | |
indexed | bytes | The indexed data, used to calculate bloom. | repeated |
non_indexed | bytes | The non indexed data. | repeated |
aelf.MerklePath
Field | Type | Description | Label |
---|---|---|---|
merkle_path_nodes | MerklePathNode | The merkle path nodes. | repeated |
aelf.MerklePathNode
Field | Type | Description | Label |
---|---|---|---|
hash | Hash | The node hash. | |
is_left_child_node | bool | Whether it is a left child node. |
aelf.SInt32Value
Field | Type | Description | Label |
---|---|---|---|
value | sint32 |
aelf.SInt64Value
Field | Type | Description | Label |
---|---|---|---|
value | sint64 |
aelf.ScopedStatePath
Field | Type | Description | Label |
---|---|---|---|
address | Address | The scope address. | |
path | StatePath | The path of contract state. |
aelf.SmartContractRegistration
Field | Type | Description | Label |
---|---|---|---|
category | sint32 | The category of contract code (0: C#). | |
code | bytes | The byte array of the contract code. | |
code_hash | Hash | The hash of the contract code. | |
is_system_contract | bool | Whether it is a system contract. | |
version | int32 | The version of the current contract. |
aelf.StatePath
Field | Type | Description | Label |
---|---|---|---|
parts | string | The partial path of the state path. | repeated |
aelf.Transaction
Field | Type | Description | Label |
---|---|---|---|
from | Address | The address of the sender of the transaction. | |
to | Address | The address of the contract when calling a contract. | |
ref_block_number | int64 | The height of the referenced block hash. | |
ref_block_prefix | bytes | The first four bytes of the referenced block hash. | |
method_name | string | The name of a method in the smart contract at the To address. | |
params | bytes | The parameters to pass to the smart contract method. | |
signature | bytes | When signing a transaction, subset of fields: from/to, target method, parameter, reference block number, prefix. |
aelf.TransactionExecutingStateSet
Field | Type | Description | Label |
---|---|---|---|
writes | TransactionExecutingStateSet.WritesEntry | The changed states. | repeated |
reads | TransactionExecutingStateSet.ReadsEntry | The read states. | repeated |
deletes | TransactionExecutingStateSet.DeletesEntry | The deleted states. | repeated |
aelf.TransactionExecutingStateSet.DeletesEntry
Field | Type | Description | Label |
---|---|---|---|
key | string | ||
value | bool |
aelf.TransactionExecutingStateSet.ReadsEntry
Field | Type | Description | Label |
---|---|---|---|
key | string | ||
value | bool |
aelf.TransactionExecutingStateSet.WritesEntry
Field | Type | Description | Label |
---|---|---|---|
key | string | ||
value | bytes |
aelf.TransactionResult
Field | Type | Description | Label |
---|---|---|---|
transaction_id | Hash | The transaction id. | |
status | TransactionResultStatus | The transaction result status. | |
logs | LogEvent | The log events. | repeated |
bloom | bytes | Bloom filter for transaction logs. | repeated |
return_value | bytes | The return value of the transaction execution. | |
block_number | int64 | The height of the block that packages the transaction. | |
block_hash | Hash | The hash of the block that packages the transaction. | |
error | string | Failed execution error message. |
aelf.TransactionResultStatus
Name | Number | Description |
---|---|---|
NOT_EXISTED | The execution result of the transaction does not exist. | |
PENDING | 1 | The transaction is in the transaction pool waiting to be packaged. |
FAILED | 2 | Transaction execution failed. |
MINED | 3 | The transaction was successfully executed and packaged into a block. |
CONFLICT | 4 | When executed in parallel, there are conflicts with other transactions. |
PENDING_VALIDATION | 5 | The transaction is waiting for validation. |
NODE_VALIDATION_FAILED | 6 | Transaction validation failed. |
Usage#
ACS10 provides a standardized interface for dividend pools, independent of aelf chain interactions.
Implementation#
1State.ProfitContract.Value =2Context.GetContractAddressByName(SmartContractConstants.ProfitContractSystemName);3var schemeToken = HashHelper.ComputeFrom(Context.Self);4State.ProfitContract.CreateScheme.Send(new CreateSchemeInput5{6Manager = Context.Self,7CanRemoveBeneficiaryDirectly = true,8IsReleaseAllBalanceEveryTimeByDefault = true,9Token = schemeToken10});11State.ProfitSchemeId.Value = Context.GenerateId(State.ProfitContract.Value, schemeToken);
1State.TokenHolderContract.Value =2Context.GetContractAddressByName(SmartContractConstants.TokenHolderContractSystemName);3State.TokenHolderContract.CreateScheme.Send(new CreateTokenHolderProfitSchemeInput4{5Symbol = Context.Variables.NativeSymbol,6MinimumLockMinutes = input.MinimumLockMinutes7});8return new Empty();
1public override Empty Donate(DonateInput input)2{3State.TokenContract.TransferFrom.Send(new TransferFromInput4{5From = Context.Sender,6Symbol = input.Symbol,7Amount = input.Amount,8To = Context.Self9});10State.TokenContract.Approve.Send(new ApproveInput11{12Symbol = input.Symbol,13Amount = input.Amount,14Spender = State.TokenHolderContract.Value15});16State.TokenHolderContract.ContributeProfits.Send(new ContributeProfitsInput17{18SchemeManager = Context.Self,19Symbol = input.Symbol,20Amount = input.Amount21});22Context.Fire(new DonationReceived23{24From = Context.Sender,25Symbol = input.Symbol,26Amount = input.Amount,27PoolContract = Context.Self28});29var currentReceivedDividends = State.ReceivedDividends[Context.CurrentHeight];30if (currentReceivedDividends != null && currentReceivedDividends.Value.ContainsKey(input.Symbol))31{32currentReceivedDividends.Value[input.Symbol] =33currentReceivedDividends.Value[input.Symbol].Add(input.Amount);34}35else36{37currentReceivedDividends = new Dividends38{39Value =40{41{42input.Symbol, input.Amount43}44}45};46}47State.ReceivedDividends[Context.CurrentHeight] = currentReceivedDividends;48Context.LogDebug(() => string.Format("Contributed {0} {1}s to side chain dividends pool.", input.Amount, input.Symbol));49return new Empty();50}
1public override Empty Release(ReleaseInput input)2{3State.TokenHolderContract.DistributeProfits.Send(new DistributeProfitsInput4{5SchemeManager = Context.Self6});7return new Empty();8}
1public override SymbolList GetSymbolList(Empty input)2{3return new SymbolList4{5Value =6{7GetDividendPoolScheme().ReceivedTokenSymbols8}9};10}11private Scheme GetDividendPoolScheme()12{13if (State.DividendPoolSchemeId.Value == null)14{15var tokenHolderScheme = State.TokenHolderContract.GetScheme.Call(Context.Self);16State.DividendPoolSchemeId.Value = tokenHolderScheme.SchemeId;17}18return Context.Call<Scheme>(19Context.GetContractAddressByName(SmartContractConstants.ProfitContractSystemName),20nameof(ProfitContractContainer.ProfitContractReferenceState.GetScheme),21State.DividendPoolSchemeId.Value);22}
1public override Dividends GetUndistributedDividends(Empty input)2{3var scheme = GetDividendPoolScheme();4return new Dividends5{6Value =7{8scheme.ReceivedTokenSymbols.Select(s => State.TokenContract.GetBalance.Call(new GetBalanceInput9{10Owner = scheme.VirtualAddress,11Symbol = s12})).ToDictionary(b => b.Symbol, b => b.Balance)13}14};15}
Test#
Testing includes sending Donate, Release transactions, and querying operations. Example:
1const long amount = 10_00000000;2var keyPair = SampleECKeyPairs.KeyPairs[0];3var address = Address.FromPublicKey(keyPair.PublicKey);4var acs10DemoContractStub =5GetTester<ACS10DemoContractContainer.ACS10DemoContractStub>(DAppContractAddress, keyPair);6var tokenContractStub =7GetTester<TokenContractContainer.TokenContractStub>(TokenContractAddress, keyPair);8var tokenHolderContractStub =9GetTester<TokenHolderContractContainer.TokenHolderContractStub>(TokenHolderContractAddress,10keyPair);
1await tokenContractStub.Approve.SendAsync(new ApproveInput2{3Spender = TokenHolderContractAddress,4Symbol = "ELF",5Amount = long.MaxValue6});7await tokenContractStub.Approve.SendAsync(new ApproveInput8{9Spender = DAppContractAddress,10Symbol = "ELF",11Amount = long.MaxValue12});
1await tokenHolderContractStub.RegisterForProfits.SendAsync(new RegisterForProfitsInput2{3SchemeManager = DAppContractAddress,4Amount = amount5});
1await acs10DemoContractStub.Donate.SendAsync(new DonateInput2{3Symbol = "ELF",4Amount = amount5});
1// Check undistributed dividends before releasing.2{3var undistributedDividends =4await acs10DemoContractStub.GetUndistributedDividends.CallAsync(new Empty());5undistributedDividends.Value["ELF"].ShouldBe(amount);6}7var blockchainService = Application.ServiceProvider.GetRequiredService<IBlockchainService>();8var currentBlockHeight = (await blockchainService.GetChainAsync()).BestChainHeight;9var dividends =10await acs10DemoContractStub.GetDividends.CallAsync(new Int64Value {Value = currentBlockHeight});11dividends.Value["ELF"].ShouldBe(amount);
1await acs10DemoContractStub.Release.SendAsync(new ReleaseInput2{3PeriodNumber = 14});5// Check undistributed dividends after releasing.6{7var undistributedDividends =8await acs10DemoContractStub.GetUndistributedDividends.CallAsync(new Empty());9undistributedDividends.Value["ELF"].ShouldBe(0);10}
1var balanceBeforeClaimForProfits = await tokenContractStub.GetBalance.CallAsync(new GetBalanceInput2{3Owner = address,4Symbol = "ELF"5});6await tokenHolderContractStub.ClaimProfits.SendAsync(new ClaimProfitsInput7{8SchemeManager = DAppContractAddress,9Beneficiary = address10});11var balanceAfterClaimForProfits = await tokenContractStub.GetBalance.CallAsync(new GetBalanceInput12{13Owner = address,14Symbol = "ELF"15});16balanceAfterClaimForProfits.Balance.ShouldBe(balanceBeforeClaimForProfits.Balance + amount);
Example#
Implementing ACS10 facilitates building dividend pools on main and side chains.
Edited on: 15 July 2024 05:41:01 GMT+0