When it comes to indexing your tables, you have to be mindful of which data you'll need to access quickly/frequently within your contract. Less frequently accessed data can be handed using an off-chain indexer, or even a simple API call if you aren't storing tons of rows in your tables.
There are several options here, I'll outline the most common approaches below.
available_primary_key
This is the most straightforward of all. This method will just use the next available key, based on the highest value currently stored in your table. For example...
Let's say you have a table, and you are 100% certain that each user in the table will only need 1 row. You can handle this simply by using the user.value
struct [[eosio::table, eosio::contract(CONTRACT_NAME)]] balances {
eosio::name wallet;
eosio::asset balance;
uint64_t primary_key() const { return wallet.value; }
};
using balances_table = eosio::multi_index< "balances"_n, balances >;
ACTION someaction( const name& user ){
require_auth( user );
balances_table bal_t = balances_table( _self, _self.value );
auto itr = bal_t.require_find( user.value, "you do not have a balance" );
}
By uint128_t Of 2 Values
Let's say for example, you have an NFT market contract. All of the drop purchases are stored in a single table, under the same scope. Each drop can contain multiple orders, and each user can have orders for multiple drops. Using the drop ID, or the user's wallet as a primary index will not work in this case. You can solve this using the following method.
#define mix64to128(a, b) (uint128_t(a) << 64 | uint128_t(b))
struct [[eosio::table, eosio::contract(CONTRACT_NAME)]] orders {
uint64_t order_id;
eosio::name wallet;
uint64_t drop_id;
uint64_t primary_key() const { return order_id; }
uint128_t secondary_key() const { return mix64to128( wallet.value, drop_id ); }
};
using orders_table = eosio::multi_index<"orders"_n, orders,
eosio::indexed_by<"userdropmix"_n, eosio::const_mem_fun<orders, uint128_t, &orders::secondary_key>>
>;
ACTION findorder( const name& wallet, const uint64_t& drop_id ){
orders_table orders_t = orders_table( _self, _self.value );
auto orders_secondary = orders_t.get_index<"userdropmix"_n>();
uint128_t user_drop_key = mix64to128( wallet.value, drop_id );
auto itr = orders_secondary.require_find( user_drop_key, "no match found for this user + drop" );
}
By checksum256
Sometimes 128 bits is not enough to meet the needs for an index. For example, you may have a contract where you need to index by a combination of user, token and contract and be able to easily find that key. You can accomplish this by using a checksum256 hash, as demonstrated below.