These docs are for v1.3.0. Click to read the latest docs for v1.6.

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 == N(${contract_name}) {
   // 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 == N(${action_name}) {
    //your handler to respond to a particular action
}

The EOSIO_ABI macro

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

#define EOSIO_ABI( TYPE, MEMBERS ) \
extern "C" { \
   void apply( uint64_t receiver, uint64_t code, uint64_t action ) { \
      auto self = receiver; \
      if( code == self ) { \
         TYPE thiscontract( self ); \
         switch( action ) { \
            EOSIO_API( TYPE, MEMBERS ) \
         } \
         /* does not allow destructor of thiscontract to run: eosio_exit(0); */ \
      } \
   } \
} \

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_ABI( hello, (hi) ) where hello and hi are values from the contract.

In this example you can see there is one function, apply. All it does is log the actions delivered and makes no other checks. Anyone can deliver any action at any time provided the block producers allow it. Absent any required signatures, the contract will be billed for the bandwidth consumed.

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 == N(${contract_name}) {
    // 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 == N(${action_name}) {
    //your handler to respond to a particular action
}

wast

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 a text version of WASM (.wast) using the eosiocpp tool.

🚧

eosiocpp is deprecated from v1.2.0 and will be removed in v1.3.0 . It will be replaced into eosio-cpp of eosio.wasmsdk repository.
Parameters and arguments could be changed accordingly.

$ eosio-cpp -o ${contract}.wast ${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 .hpp files using the eosio-cpp tool by passing --abigen argument.

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

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

{
  "types": [{
      "new_type_name": "account_name",
      "type": "name"
    }
  ],
  "structs": [{
      "name": "transfer",
      "base": "",
      "fields": {
        "from": "account_name",
        "to": "account_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": "account_name",
        "to": "account_name",
        "quantity": "uint64"
      }
    },{
...

The ABI has several fields, including from, to and quantity. These fields have the corresponding types account_name, and uint64. account_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": "account_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.