The Dispatcher Macro & Apply

Every smart contract must provide an apply action handler. The apply action handler is a function that listens to all incoming actions and performs the desired behavior. In order to respond to a particular action, code is required to identify and respond to specific actions requests. apply uses the receiver, code, and action input parameters as filters to map to the desired functions that implement particular actions. The apply function can filter on the code parameter using something like the following:

if (code == name("contract_name").value) {
   // your handler to respond to particular code
}

Within a given code, one can respond to a particular action by filtering on the action parameter. This is normally used in conjunction with the code filter.

if (action ==  name("action_name").value) {
    //your handler to respond to a particular action
}

The EOSIO_DISPATCH macro

To simplify the work for contract developers, the EOSIO_DISPATCH macro encapsulates the lower level action mapping details of the apply function, enabling developers to focus on their application implementation.

#define EOSIO_DISPATCH( TYPE, MEMBERS ) \

A developer needs only to specify the code and action names from the contract in the macro, and all of the underlying C code mapping logic is generated by the macro. An example of use of the macro can be seen above, i.e., EOSIO_DISPATCH( hello, (hi) ) where hello and hi are values from the contract.

apply

apply is the action handler, it listens to all incoming actions and reacts according to the specifications within the function. The apply function requires two input parameters, code and action.

code filter

In order to respond to a particular action, structure the apply function as follows. You may also construct a response to general actions by omitting the code filter.

if (code ==  name("contract_name").value) {
    // your handler to respond to particular code
}

You can also define responses to respective actions in the code block.

action filter

To respond to a particular action, structure your apply function as follows. This is normally used in conjuction with the code filter.

if (action == name("action_name").value) {
    //your handler to respond to a particular action
}

wasm

Any program to be deployed to the EOSIO blockchain must be compiled into WASM format. This is the only format the blockchain accepts.

Once you have the CPP file ready, you can compile it into an WASM file (.wasm) using the eosio-cpp tool.

🚧

eosiocpp is deprecated from v1.2.0 and will be removed eventually. It will be replaced by eosio-cpp of eosio.cdt repository.

$ eosio-cpp -o ${contract}.wasm ${contract}.cpp

abi

The Application Binary Interface (ABI) is a JSON-based description on how to convert user actions between their JSON and Binary representations. The ABI also describes how to convert the database state to/from JSON. Once you have described your contract via an ABI then developers and users will be able to interact with your contract seamlessly via JSON.

The ABI file can be generated from the .cpp files using the eosio-cpp tool by passing --abigen argument.

$ eosio-cpp -o ${contract}.wasm ${contract}.cpp --abigen

The following is an example of what the skeleton contract ABI looks like:

{
  "types": [{
      "new_type_name": "name",
      "type": "name"
    }
  ],
  "structs": [{
      "name": "transfer",
      "base": "",
      "fields": {
        "from": "name",
        "to": "name",
        "quantity": "uint64"
      }
    },{
      "name": "account",
      "base": "",
      "fields": {
        "account": "name",
        "balance": "uint64"
      }
    }
  ],
  "actions": [{
      "action": "transfer",
      "type": "transfer"
    }
  ],
  "tables": [{
      "table": "account",
      "type": "account",
      "index_type": "i64",
      "key_names" : ["account"],
      "key_types" : ["name"]
    }
  ]
}

You will notice that this ABI defines an action transfer of type transfer. This tells EOSIO that when ${account}->transfer action is seen that the payload is of type transfer. The type transfer is defined in the structs array in the object with name set to transfer.

  "structs": [{
      "name": "transfer",
      "base": "",
      "fields": {
        "from": "name",
        "to": "name",
        "quantity": "uint64"
      }
    },{
...

The ABI has several fields, including from, to and quantity. These fields have the corresponding types name, and uint64. name is a built-in type used to represent base32 string as uint64. To see more about what built-in types are available, check here.

{
  "types": [{
      "new_type_name": "name",
      "type": "name"
    }
  ],
...

Inside the above types array we define a list of aliases for existing types. Here, we define name as an alias of account_name.