Skip to content

Etch smart contracts

Smart contract code runs on the etch virtual machine.

All ledger nodes contain the etch VM and smart contract code.

The identity of a smart contract is calculated by performing a SHA256 hash on the contract code string as an initial step. Next, a further SHA256 hash is calculated from the previous result concatenated with a public key Address which represents the contract owner.

On the ledger, the etch VM stores the contract identity, the contract source code, and the data resources that are mapped by a data.json file.

With this information, the etch VM performs a modulo 16 calculation to decide where to store the data on the ledger, i.e. onto which shard.

Note

Coming soon: details on how developers may dictate the sharding storage design for a smart contract.

Smart contract structure

Smart contract functions are annotated depending on the activity they perform.

@init

The @init function defines a contract constructor that sets the state of the contract prior to any operations performed on it. It is called once and once only on contract initialisation/deployment.

The name of the @init function can be anything at all.

For example, the following function initialises a contract by creating a State type to represent the owner's account which then receives an initial supply of FET tokens.

This happens once and once only at contract deployment.

@init
function initialise(owner: Address)

    var INITIAL_SUPPLY = 100000000000u64;
    var account = State<UInt64>("owner");
    account.set(INITIAL_SUPPLY);

endfunction

function main()

    var owner = Address("2ifr5dSFRAnXexBMC3HYEVp3JHSuz7KBPXWDRBV4xdFrqGy6R9");
    initialise(owner);

endfunction

Remember

We use main() in the examples to allow for testing smart contract code outside of a ledger environment.

@action

The @action annotation signifies a function which performs a transaction.

You cannot create a smart contract in etch without an @action function and it is these functions that trigger the charging rules for data persistence fees.

The following function performs a transaction between two parties.

@action
function transfer(from: Address, to: Address, amount: UInt64)

    var from_balance = State<UInt64>(from); 
    from_balance.set(1000u64);
    var to_balance = State<UInt64>(to);
    to_balance.set(0u64);

    // check if all the conditions are valid
    if (from_balance.get() <= amount)
        panic("Argh!");
    endif

    from_balance.set(from_balance.get() - amount);
    to_balance.set(to_balance.get() + amount);

endfunction

function main()

    var owner = Address("2ifr5dSFRAnXexBMC3HYEVp3JHSuz7KBPXWDRBV4xdFrqGy6R9");
    var user = Address("2ifr5dSFRAnXexBMC3HYEVp3JHSuz7KBPXWDRBV4xdFrqGy6R9");
    transfer(owner, user, 100u64);

endfunction

In the worst case, the above function needs two shards for data storage.

@query

Query functions are read-only functions that allow you to view data residing on the ledger.

The following function queries the balance of an Address.

@query
function balance(address : Address) : UInt64

    var account = State<UInt64>(address);
    account.set(100u64);
    return account.get();

endfunction

function main()

    var owner = Address("2ifr5dSFRAnXexBMC3HYEVp3JHSuz7KBPXWDRBV4xdFrqGy6R9");
    var owner_balance = balance(owner);
    printLn(owner_balance);

endfunction

Smart contract addresses

etch smart contracts have a unique identification protocol for addressing on the Fetch.AI Ledger.

etch smart contract identifiers are a SHA256 hash of the contract source code which is then Base64 encoded and finally concatenated with the Base64 encoded owner's public key.

Using this identifier, smart contract functions are accessible with a subsequent . plus function name.

Data confirmation

If you run an etch contract in the simulator containing one or more State types and flag the compiler with -data and a filename, it will create a json file containing the details of the data that will be stored on the ledger.

./etch *filename* -data data.json

If we run one of the above code examples in this way, data.json may contain the following:

{"2ifr5dSFRAnXexBMC3HYEVp3JHSuz7KBPXWDRBV4xdFrqGy6R9": "8403000000000000"}

Utility functions

getBlockNumber() : returns the number of the current block in UInt64.

You need a node running to test this. As well as that, you can only get a result when the function is embedded within smart contract code in Python. See a coded example of getBlockNumber() with a running node here.