AELF Smart Contract Development Guidelines

Hard Requirements

Pick Meaningful Names

Meaningful names are critical to convey your design intention and help the readers understand the code. Please always try to pick suitable names for states, variables and method names. For example, changing the vague list name to distinctNewControllers makes the code easier to understand.

    public override Empty AddController(AddressList input)
    {
        ...
        var list = input.Data.Distinct().Except(State.Controller.Value.Data).ToList();
        ...

        return new Empty();
    }
    public override Empty AddController(AddressList input)
    {
        ...
        var distinctNewControllers = input.Data.Distinct().Except(State.Controller.Value.Data).ToList();
        ...

        return new Empty();
    }

Run Through Copilot or ChatGPT on Namings

If you aren't sure about certain English grammar related issues. Ask Copilot to fix them for you. For example, the following assert message User is not exist. is not following correct grammar. You can

Below is an example showing how you could prompt Copilot to fix such issues for you.

Use Copilot to fix English grammar issues

Besides string literals in assert messages, you could ask Copilot to suggest better names for your methods as well. This is citical as a good names help you communicate the intention of your code and hence improves your code readability. For example you can select your method and prompt Copilot like Help me pick a suitable name for the method. You can choose from the suggested names given by Copilot.

Write Small Methods

Separate Admin and User Interfaces

For example, the following is the proto definitions of a BingGameContract. Now it contains all the methods, messages and events as well as contract metadata such as namespace and state names. If you think about the usage of the interface, you will quickly realize that the information like the contract state name is not interested if it's used for calling this contract on the client side. You will also realize that the methods (actions and views) can be broken down into two categories (one for admins and one for normal users). If we understand this and follow the Interface Segregation Principle of the SOLID principles, we can do a better job by separating it into a few proto files.

A Naive Proto File

A Better Organization of Proto Files

The Main Contract

The main contract is only needed when you write your smart contract and the client side won't need it.

The Shared Interface Between the Contract and the Client

bingo-contract.proto will be needed both by the contract and the client side where interaction to this contract is needed.

The Interface Facing the Operator/Admin of the Game

bingo-contract-admin.proto will be needed both by the contract and the operator of the dApp and is not required by other applications facing the end users.

bingo-contract-admin.proto:

Avoid Using Context.SendVirtualInline Directly

Context.SendVirtualInline is an API provided by the context, however to write cleaner code, the method binding in RefereceContractState provides a VirtualSend utility method where we can supply a virtual address hash along with the request input to send an inline call directly. So there's no need to call Context.SendVirtualInline directly, we can do <Contract>.<Method>.VirtualSend(virtualHash, methodInput) instead.

For example, the Context.SendVirtualInline call in the following code can be replaced with State.TokenContract.Transfer.VirtualSend.

Anti-Pattern
Canonical Way of Method Call with Virtual Address

Suggested Patterns

Add Documentation to All Public Methods

Your public methods should all have clear explanation about the functionality and logics contained in it.

Tip: GitHub Copilot can be used to help you add a proper documentation for your method. Below is an example.

Follow Check-Effect-Interaction Pattern

Try to break down your method logics into the following 3 steps:

  1. Check: Validate the input data and required permissions

  2. Effects: Update contract states

  3. Interaction: Send inline calls to other contracts

Last updated