Smart Contracts

The EOS C++ SDK Developer Hub

Welcome to the EOS C++ SDK developer hub. You'll find comprehensive guides and documentation to help you start working with EOS C++ SDK as quickly as possible, as well as support if you get stuck. Let's jump right in!

Get Started    

Multi Index Table Usage Guide

Description

Below is a usage guide for the multi index table.

In the interest of being thorough and providing clarity, sections of the final .cpp file will be broken out and discussed in further detail. Note that the complete .cpp file can be found at the bottom of this page.

Glossary

  • code - Refers to an account_name where a contract has been published.
  • scope - An account_name that the data in question belongs to.
  • table_name - The name of the table that is stored in memory.

Code Break Down

Struct to be stored

The data to be stored in the multi index table is the limit_order struct. The functions primary_key(), get_expiration(), get_price() are used to return the table. The returned table will be sorted based on which function was called.

struct limit_order {
  uint64_t     id;
  uint128_t    price;
  uint64_t     expiration;
  account_name owner;

  auto primary_key() const { return id; }
  uint64_t get_expiration() const { return expiration; }
  uint128_t get_price() const { return price; }

  EOSLIB_SERIALIZE( limit_order, ( id )( price )( expiration )( owner ) )
};

Creating the multi index table

auto payer = ilm.get_account();
...

payer is the variable that holds the account who will be "billed" for adding elements to the multi index table and modifying elements already in the multi index table.

...
eosio::multi_index< N( orders ), limit_order, 
...

N( orders ) is the name of the multi index table and limit_orders is the data to be stored in the table.

...  
indexed_by< N( byexp ), const_mem_fun< limit_order, uint64_t, 
&limit_order::get_expiration> >,
...

indexed_by< N( byexp ), const_mem_fun< limit_order, uint64_t, &limit_order::get_expiration> > is the definition of a way the orders multi index table can be indexed. N( byexp ) is the name of this index. The const_mem_fun indicates the data type being retrieved, limit_order, the type of variable being sorted by, uint64_t, and the function that will be used get the variable, get_expiration.

...
  indexed_by< N( byprice ), const_mem_fun< limit_order, uint128_t, &limit_order::get_price> >
...

indexed_by< N( byprice ), const_mem_fun< limit_order, uint128_t, &limit_order::get_price> > is the definition of a way the orders multi index table can be indexed. N( byprice ) is the name of this index. The const_mem_fun indicates the data type being retrieved, limit_order, the type of variable being sorted by, uint128_t, and the function that will be used get the variable, get_price.

orders( N( limitorders ), N( limitorders )

orders is the multi index table.

auto payer = ilm.get_account();

print("Creating multi index table 'orders'.\n");
eosio::multi_index< N( orders ), limit_order, 
  indexed_by< N( byexp ),   const_mem_fun< limit_order, uint64_t, &limit_order::get_expiration> >,
  indexed_by< N( byprice ), const_mem_fun< limit_order, uint128_t, &limit_order::get_price> >
    > orders( N( limitorders ), N( limitorders ) );

Adding to the multi index table

Below, two limit_orders are added to the orders table. Note that payer is the account that is being "billed" for the modification to the orders table.

orders.emplace( payer, [&]( auto& o ) {
  o.id = 1;
  o.expiration = 300;
  o.owner = N(dan);
});

auto order2 = orders.emplace( payer, [&]( auto& o ) {
  o.id = 2;
  o.expiration = 200;
  o.owner = N(thomas);
});

Sorted by primary key

By default, the orders table is sorted by primary key.

print("Items sorted by primary key:\n");
for( const auto& item : orders ) {
  print(" ID=", item.id, ", expiration=", item.expiration, ", owner=", name{item.owner}, "\n");
}

Sorted by secondary index - expiration

The orders table gets sorted by expiration and assigned to expidx.

auto expidx = orders.get_index<N(byexp)>();

print("Items sorted by expiration:\n");
for( const auto& item : expidx ) {
  print(" ID=", item.id, ", expiration=", item.expiration, ", owner=", name{item.owner}, "\n");
}

Sorted by secondary index - price

The orders table gets sorted by price and assigned to pridx.

auto pridx = orders.get_index<N(byprice)>();

print("Items sorted by price:\n");
for( const auto& item : pridx ) {
  print(" ID=", item.id, ", expiration=", item.expiration, ", owner=", name{item.owner}, "\n");
}

Modify an entry

Below, the entry with "ID=2" gets modified. Note that payer is the account that is being "billed" for the modification to the orders table.

print("Modifying expiration of order with ID=2 to 400.\n");
orders.modify( order2, payer, [&]( auto& o ) {
  o.expiration = 400;
});

Getting the lower bound

auto lower = expidx.lower_bound(100);

print("First order with an expiration of at least 100 has ID=", lower->id, " and expiration=", lower->get_expiration(), "\n");

Complete .cpp file

#include <eosiolib/eosio.hpp>
#include <eosiolib/dispatcher.hpp>
#include <eosiolib/multi_index.hpp>

using namespace eosio;

namespace limit_order_table {

    struct limit_order {
        uint64_t     id;
        uint128_t    price;
        uint64_t     expiration;
        account_name owner;

        auto primary_key() const { return id; }
        uint64_t get_expiration() const { return expiration; }
        uint128_t get_price() const { return price; }

        EOSLIB_SERIALIZE( limit_order, ( id )( price )( expiration )( owner ) )
    };

    class limit_order_table {
        public:

        ACTION( N( limitorders ), issue_limit_order ) {
            EOSLIB_SERIALIZE( issue_limit_order )
        };

        static void on( const issue_limit_order& ilm ) {
            auto payer = ilm.get_account();

            print("Creating multi index table 'orders'.\n");
            eosio::multi_index< N( orders ), limit_order, 
                indexed_by< N( byexp ),   const_mem_fun< limit_order, uint64_t, &limit_order::get_expiration> >,
                indexed_by< N( byprice ), const_mem_fun< limit_order, uint128_t, &limit_order::get_price> >
                > orders( N( limitorders ), N( limitorders ) );

            orders.emplace( payer, [&]( auto& o ) {
                o.id = 1;
                o.expiration = 300;
                o.owner = N(dan);
            });

            auto order2 = orders.emplace( payer, [&]( auto& o ) {
                o.id = 2;
                o.expiration = 200;
                o.owner = N(thomas);
            });

            print("Items sorted by primary key:\n");
            for( const auto& item : orders ) {
                print(" ID=", item.id, ", expiration=", item.expiration, ", owner=", name{item.owner}, "\n");
            }

            auto expidx = orders.get_index<N(byexp)>();

            print("Items sorted by expiration:\n");
            for( const auto& item : expidx ) {
                print(" ID=", item.id, ", expiration=", item.expiration, ", owner=", name{item.owner}, "\n");
            }

            auto pridx = orders.get_index<N(byprice)>();

            print("Items sorted by price:\n");
            for( const auto& item : pridx ) {
                print(" ID=", item.id, ", expiration=", item.expiration, ", owner=", name{item.owner}, "\n");
            }

            print("Modifying expiration of order with ID=2 to 400.\n");
            orders.modify( order2, payer, [&]( auto& o ) {
                o.expiration = 400;
            });

            auto lower = expidx.lower_bound(100);

            print("First order with an expiration of at least 100 has ID=", lower->id, " and expiration=", lower->get_expiration(), "\n");
   };

} /// limit_order_table

namespace limit_order_table {
   extern "C" {
      /// The apply method implements the dispatch of events to this contract
      void apply( uint64_t code, uint64_t action ) {
         require_auth( code );
         eosio_assert( eosio::dispatch< limit_order_table, limit_order_table::issue_limit_order >( code, action ), "Could not dispatch" );
      }
   }
}

Deleting a Table

Tables cannot be directly deleted, however, a table will delete itself automatically after all rows have been deleted.



Multi Index Table Usage Guide