Skip to content

Latest commit

 

History

History
596 lines (508 loc) · 22.6 KB

acs9.rst

File metadata and controls

596 lines (508 loc) · 22.6 KB

ACS9 - Contract profit dividend standard

On the AElf’s side chain, the contract needs to declare where its profits are going, and implement ACS9.

Interface

ACS9 contains an method which does not have to be implemented:

Methods

Method Name Request Type Response Type Description
TakeContractProfits acs9.TakeContractProfitsInput google.protobuf.Empty Used for the developer to collect the profits from the contract, and the profits will be distributed in this method.
GetProfitConfig google.protobuf.Empty acs9.ProfitConfig Query the config of profit.
GetProfitsAmount google.protobuf.Empty acs9.ProfitsMap Query the profits of the contract so far.

Types

acs9.ProfitConfig

Field Type Description Label
donation_parts_per_hundred int32 The portion of the profit that will be donated to the dividend pool each time the developer receives the profit.
profits_token_symbol_list string The profit token symbol list. repeated
staking_token_symbol string The token symbol that the user can lock them to claim the profit.

acs9.ProfitsMap

Field Type Description Label
value ProfitsMap.ValueEntry The profits, token symbol -> amount. repeated

acs9.ProfitsMap.ValueEntry

Field Type Description Label
key string
value int64

acs9.TakeContractProfitsInput

Field Type Description Label
symbol string The token symbol to take.
amount int64 The amount to take.

Implementation

Here we define a contract. The contract creates a token called APP at the time of initialization and uses the TokenHolder contract to create a token holder bonus scheme with the lock token is designated to APP.

The user will be given 10 APP when to sign up.

Users can purchase 1 APP with 1 ELF using method Deposit, and they can redeem the ELF using the method Withdraw.

When the user sends the Use transaction, the APP token is consumed.

Contract initialization is as follows:

public override Empty Initialize(InitializeInput input)
{
    State.TokenHolderContract.Value =
        Context.GetContractAddressByName(SmartContractConstants.TokenHolderContractSystemName);
    State.TokenContract.Value =
        Context.GetContractAddressByName(SmartContractConstants.TokenContractSystemName);
    State.DividendPoolContract.Value =
        Context.GetContractAddressByName(input.DividendPoolContractName.Value.ToBase64());
    State.Symbol.Value = input.Symbol == string.Empty ? "APP" : input.Symbol;
    State.ProfitReceiver.Value = input.ProfitReceiver;
    CreateToken(input.ProfitReceiver);
    // To test TokenHolder Contract.
    CreateTokenHolderProfitScheme();
    // To test ACS9 workflow.
    SetProfitConfig();
    State.ProfitReceiver.Value = input.ProfitReceiver;
    return new Empty();
}
private void CreateToken(Address profitReceiver, bool isLockWhiteListIncludingSelf = false)
{
    var lockWhiteList = new List<Address>
        {Context.GetContractAddressByName(SmartContractConstants.TokenHolderContractSystemName)};
    if (isLockWhiteListIncludingSelf)
        lockWhiteList.Add(Context.Self);
    State.TokenContract.Create.Send(new CreateInput
    {
        Symbol = State.Symbol.Value,
        TokenName = "DApp Token",
        Decimals = ACS9DemoContractConstants.Decimal,
        Issuer = Context.Self,
        IsBurnable = true,
        IsProfitable = true,
        TotalSupply = ACS9DemoContractConstants.TotalSupply,
        LockWhiteList =
        {
            lockWhiteList
        }
    });
    State.TokenContract.Issue.Send(new IssueInput
    {
        To = profitReceiver,
        Amount = ACS9DemoContractConstants.TotalSupply / 5,
        Symbol = State.Symbol.Value,
        Memo = "Issue token for profit receiver"
    });
}
private void CreateTokenHolderProfitScheme()
{
    State.TokenHolderContract.CreateScheme.Send(new CreateTokenHolderProfitSchemeInput
    {
        Symbol = State.Symbol.Value
    });
}
private void SetProfitConfig()
{
    State.ProfitConfig.Value = new ProfitConfig
    {
        DonationPartsPerHundred = 1,
        StakingTokenSymbol = "APP",
        ProfitsTokenSymbolList = {"ELF"}
    };
}

The State.symbol is a singleton of type string, state.Profitconfig is a singleton of type ProfitConfig, and state.profitreceiver is a singleton of type Address.

The user can use the SighUp method to register and get the bonus. Besides, it will create a archive for him:

Recharge and redemption:

In the implementation of Use, 1/3 profits are directly transferred into the token holder dividend scheme:

The implementation of this contract has been completed. Next, implement ACS9 to perfect the profit distribution:

Test

Since part of the profits from the ACS9 contract transfer to the Token contract and the other transfer to the dividend pool, a TokenHolder Stub and a contract implementing ACS10 Stub are required in the test. Accordingly, the contracts that implements ACS9 or ACS10 need to be deployed. Before the test begins, the contract implementing ACS9 can be initialized by interface IContractInitializationProvider, and sets the dividend pool’s name to the other contract’s name:

Prepare a user account:

Prepare some Stubs:

Then, transfer ELF to the user (TokenContractStub is the Stub of the initial bp who has much ELF) :

Have the user call SignUp to check if he/she has got 10 APP tokens:

Test the recharge method of the contract itself:

The user locks up 57 APP via the TokenHolder contract in order to obtain profits from the contract:

The Use method is invoked 10 times and 0.3 APP is consumed each time, and finally the user have 50 APP left:

Using the TakeContractProfits method, the developer attempts to withdraw 10 ELF as profits. The 10 ELF will be transferred to the developer in this method:

Next check the profit distribution results. The dividend pool should be allocated 0.1 ELF:

The user receives 1 ELF from the token holder dividend scheme:

Finally, let’s test the Withdraw method.