# Develop and API introduction
# Naming Rules
In the process of generating the abi interface file, will base32 encode the ACTION name and TABLE name, so there are restrictions on the naming rules.
ACTION, TABLE range: [1 ~ 5 a ~ z]
ACTION, TABLE length: no more than 13
class name range: [a ~ z 0 ~ 9]
class name length: unlimited
# Built-in type
The contract provides built-in types in addition to the basic types suppoerted by c++ syntax.
# contract_asset
contract_asset class contains two fields
int64_t amount;
uint64_t asset_id;
Use the is_amount_within_range
member method to verify that the contract_asset amount is valid or not
bool is_amount_within_range() const {
return -max_amount <= amount && amount <= max_amount;
}
Note
In the contract_asset type, where amount
represents the number of assets and it uses large members to store in the chain . For example, the accuracy of GXC assets is 5,1 GXC actually needs to be expressed as 1 * 100000 = 100000, that means, amount is 100000.
The asset_id indicates the instance_id of the asset (for example, if the asset id is 1.3.1, its instance_id is 1).
You can learn the use of the contract_asset
type refer to the Bank
contract.
# signature
the definition of signature class
struct signature {
uint8_t data[65];
};
example:
void verify(checksum256 hash, std::string pub_key, signature sig)
{
print(pub_key, "\n");
assert_recover_key(&hash, &sig, pub_key.c_str(), pub_key.length());
}
# public_key
public_key class
struct public_key {
char data[33];
};
# checksum256
checksum256 class
struct ALIGNED(checksum256) {
uint8_t hash[32];
};
example:
void issue(const std::string& question, const checksum256& hashed_answer)
{
uint64_t owner = get_trx_sender();
records.emplace(owner, [&](auto &p) {
p.issuer = owner;
p.question = question;
p.hashed_answer = hashed_answer;
});
}
# checksum160
checksum160 class
struct ALIGNED(checksum160) {
uint8_t hash[20];
};
# checksum512
checksum512 class
struct ALIGNED(checksum512) {
uint8_t hash[64];
};
# block_id_type
block_id_type class
typedef struct checksum160 block_id_type;
# Built-in API
When you develop smart contracts on GXChain, you can call the built-in API to implement richer features.
belong | api name | description |
---|---|---|
<graphenelib/action.h> | current_receiver | Returns the instance_id of the current contract account( the last digit of the account id) |
<graphenelib/action.h> | get_action_asset_id | Returns the asset instance_id sent to the contract by this call( the last digit of the asset id) |
<graphenelib/action.h> | get_action_asset_amount | Returns the number of assets sent to the contract by this call |
<graphenelib/asset.h> | withdraw_asset | Transfer assets from the current contract account to the other account |
<graphenelib/asset.h> | get_balance | Get the balance of an asset in the account on the chain |
<graphenelib/crypto.h> | sha1 | Obtain the encrypted data of the sha1 algorithm |
<graphenelib/crypto.h> | sha256 | Obtain the encrypted data of the sha256 algorithm |
<graphenelib/crypto.h> | sha512 | Obtain the encrypted data of the sha512 algorithm |
<graphenelib/crypto.h> | ripemd160 | Obtain the encrypted data of the ripemd160 algorithm |
<graphenelib/crypto.h> | assert_recover_key | Verify that the given signature and hash can recover the public key |
<graphenelib/global.h> | get_head_block_num | Get the head block number |
<graphenelib/global.h> | get_head_block_id | Get the head block hash |
<graphenelib/global.h> | get_block_id_for_num | Get the specified block hash |
<graphenelib/global.h> | get_head_block_time | Get the time of the block, return the value in seconds |
<graphenelib/global.h> | get_trx_sender | Get the instance_id of the account that called the contract |
<graphenelib/global.h> | get_trx_origin | Get the instance_id of the original account |
<graphenelib/global.h> | get_account_id | Get the instance_id of the account by the account name |
<graphenelib/global.h> | get_account_name_by_id | Obtain an account name by the account instance id |
<graphenelib/global.h> | get_asset_id | Get the instance_id of the asset by the asset name |
<graphenelib/global.h> | read_transaction | Read serialized data of the current transaction |
<graphenelib/global.h> | transaction_size | Get the length of the current transaction data after it has been serialized |
<graphenelib/global.h> | expiration | Get expiration time of the transaction |
<graphenelib/global.h> | tapos_block_num | Returns the block number referenced by the transaction |
<graphenelib/global.h> | tapos_block_prefix | Return the block ID referenced by the transaction (the second 32-bit number) |
<graphenelib/action.h> | read_action_data | Read data of the current action |
<graphenelib/action.h> | action_data_size | Returns the number of bytes needed to get the current action data |
<graphenelib/action.h> | send_inline | Inline execute action |
<graphenelib/action.hpp> | unpack_action_data | Deserialize the current action data into a defined action object |
<graphenelib/system.h> | graphene_assert | If the assertion fails, interrupt the execution of this contract and roll all states |
<graphenelib/system.h> | graphene_assert_message | If the assertion fails, interrupt the execution of this contract and roll all states |
<graphenelib/system.h> | Print log |
# current_receiver
Function: uint64_t current_receiver()
Head file: <graphenelib/action.h>
Description: Returns the instance_id of the current contract account( the last digit of the account id)
Return value: Instance_id
Example:
// @abi action
void examcurr(){
uint64_t ins_id = current_receiver();
print("current contract account id: ", ins_id, "\n");
}
# get_action_asset_id
Function: uint64_t get_action_asset_id()
Head file: <graphenelib/action.h>
Description: Return the asset instance_id sent to the contract by this call
Return value: Returns 0 means the action has no attached assets, otherwise returns instance_id
Example:
// @abi action
void examgetast(){
uint64_t ast_id = get_action_asset_id();
print("call action asset id: ",ast_id, "\n");
}
# get_action_asset_amount
Function: int64_t get_action_asset_amount()
Head file: <graphenelib/action.h>
Description: Returns the number of assets sent to the contract by this call
Return value: Returns 0 means the contract has no accompanying assets, otherwise means the number of assets attached and the quantity needs to be divided by 100,000
Example:
//get_action_asset_amount
// @abi action
void examgetamo(){
int64_t amount = get_action_asset_amount();
print("call action asset amount: ",amount,"\n");
}
# inline_transfer
Function:void inline_transfer(uint64_t from, uint64_t to, uint64_t asset_id, int64_t amount, const char* data, uint32_t length)
Head file:<graphenelib/asset.h>
Description: Transfer the assets of the current contract to an external account
Params:
Params | Type | Description |
---|---|---|
from | uint64_t | Must be _self |
to | uint64_t | The instance_id of the receiver account |
asset_id | uint64_t | Asset id used to transfer |
amount | int64_t | Transfer amount, this number contains the accuracy of the asset, for example, if you want to transfer 1 GXC, you should write 100000 |
data | const char* | The first address of the data string(memo) |
length | uint32_t | Length of the data string |
Example:
// @abi action
void examwith(uint64_t from,uint64_t to, uint64_t asset_id, int64_t amount){
std::string memo = "withdraw";
inline_transfer(from,to,asset_id,amount, memo.c_str(), memo.size());
print("inline_transfer example\n");
}
# withdraw_asset
Function: void withdraw_asset(uint64_t from, uint64_t to, uint64_t asset_id, int64_t amount)
Head file: <graphenelib/asset.h>
Description: Transfer assets from the current contract account to the other account
Params:
Params | Type | Description |
---|---|---|
from | uint64_t | Must be _self |
to | uint64_t | The instance_id of the receiver account |
asset_id | uint64_t | Asset id used to transfer |
amount | int64_t | Transfer amount, this number contains the accuracy of the asset, for example, if you want to transfer 1 GXC, you should write 100000 |
Example:
// @abi action
void examwith(uint64_t from,uint64_t to, uint64_t asset_id, int64_t amount){
withdraw_asset(from,to,asset_id,amount);
print("withdraw_asset example\n");
}
# get_balance
Function: int64_t get_balance(int64_t account, int64_t asset_id)
Head file: <graphenelib/asset.h>
Description: Get the balance of an asset in the account on the chain
Return value: The balance
Params:
Params | Type | Description |
---|---|---|
account | int64_t | Account's instance_id |
asset_id | int64_t | Asset's instance_id |
Example:
// @abi action
void examgetbl(int64_t account, int64_t asset_id){
int64_t balance = get_balance(account, asset_id);
print("account balance: ",balance,"\n");
}
# sha1
Function: void sha1(const char *data, uint32_t length, checksum160 *hash)
Head file: <graphenelib/crypto.h>
Description: Obtain the encrypted data of the sha1 algorithm
Params:
Params | Type | Description |
---|---|---|
data | const char* | Data start address |
length | uint32_t | Data length |
hash | checksum160* | Hash value |
Example:
// @abi action
void examsha1(std::string data){
checksum160 hash;
sha1(data.c_str(),data.length(),&hash);
printhex(hash.hash,20);
print("\n");
}
# sha256
Function: void sha256(const char *data, uint32_t length, checksum256 * hash)
Head file: <graphenelib/crypto.h>
Description: Obtain the encrypted data of the sha256 algorithm
Params:
Params | Type | Description |
---|---|---|
data | const char* | Data start address |
length | uint32_t | Data length |
hash | checksum256* | Hash value |
Example:
// @abi action
void examsha25(std::string data){
checksum256 hash;
sha256(data.c_str(),data.length(),&hash);
printhex(hash.hash,32);
print("\n");
}
# sha512
Function: void sha512(const char *data, uint32_t length, checksum512 * hash)
Head file: <graphenelib/crypto.h>
Description: Obtain the encrypted data of the sha512 algorithm
Params:
Params | Type | Description |
---|---|---|
data | const char* | Data start address |
length | uint32_t | Data length |
hash | checksum512* | Hash value |
Example:
// @abi action
void examsha512(std::string data){
checksum512 hash;
sha512(data.c_str(),data.length(),&hash);
printhex(hash.hash,64);
print("\n");
}
# ripemd160
Function: void ripemd160(const char *data, uint32_t length, checksum160 * hash)
Head file: <graphenelib/crypto.h>
Description: Obtain the encrypted data of the ripemd160 algorithm
Params:
Params | Type | Description |
---|---|---|
data | const char* | Data start address |
length | uint32_t | Data length |
hash | checksum160* | Hash value |
Example:
// @abi action
void examripemd(std::string data){
checksum160 hash;
ripemd160(data.c_str(),data.length(),&hash);
printhex(hash.hash,20);
print("\n");
}
# assert_recover_key
Function: void assert_recover_key(const checksum256 *digest,const signature *sig, const char *pub, uint32_t publen);
Head file: <graphenelib/crypto.h>
Description: Verify that the given signature and hash can recover the public key
Params:
Params | Type | Description |
---|---|---|
data | const checksum256* | Sha256 hash |
sig | const signature* | Signature value of the string |
pub | const char* | Public key(in hexadecimal format) |
publen | uint32_t | Plulic key length |
Example:
// @abi action
void examrecover(checksum256 dig,signature sig,std::string pkey)
{
assert_recover_key(&dig,&sig,pkey.c_str(), pkey.length());
}
# get_head_block_num
Function: int64_t get_head_block_num()
Head file: <graphenelib/global.h>
Description: Return the latest block number
Return value: The head block number
Example:
// @abi action
void examgetnum(){
int64_t head_num = get_head_block_num();
print("head block num: ",head_num, "\n");
}
# get_head_block_id
Function: void get_head_block_id(checksum160* hash)
Head file: <graphenelib/global.h>
Description: Get the head block hash
Params:
Params | Type | Description |
---|---|---|
hash | checksum160* | The head block hash value |
Example:
// @abi action
void examgetid(){
checksum160 block_hash;
get_head_block_id(&block_hash);
printhex(block_hash.hash,20);
print("\n");
}
# get_block_id_for_num
Function: void get_block_id_for_num(checksum160* hash, uint32_t block_num)
Head file: <graphenelib/global.h>
Description: Get the specified block hash
Params:
Params | Type | Description |
---|---|---|
hash | checksum160* | The specified block hash |
block_num | uint32_t | The specified block num |
Example:
// @abi action
void examidnum(){
checksum160 block_hash;
get_block_id_for_num(&block_hash,1); //get the hash of first block
printhex(block_hash.hash,20);
print("\n");
}
# get_head_block_time
Function: int64_t get_head_block_time()
Head file: <graphenelib/global.h>
Description: Get the time of the latest block, return the value in seconds
Return Value: The time of the latest block
Example:
// @abi action
void examgettime(){
int64_t head_time;
head_time = get_head_block_time();
print("head block time: ",head_time,"\n");
}
# get_trx_sender
Function: uint64_t get_trx_sender()
Head file: <graphenelib/global.h>
Description: Get the instance_id of the account that called the contract
Return Value: Instance_id of the account
Example:
// @abi action
void examgettrx(){
uint64_t sender_id;
sender_id = get_trx_sender();
print("call action instance id: ",sender_id, "\n");
}
# get_trx_origin
Function: uint64_t get_trx_origin()
Head file: <graphenelib/global.h>
Description: Get the instance_id of the original account
Return Value: Original account instance_id
Example:
// @abi action
void examgetori(){
uint64_t origin_id;
origin_id = get_trx_origin();
print("original instance id: ",origin_id, "\n");
}
# get_account_id
Function: int64_t get_account_id(const char * data, uint32_t length)
Head file: <graphenelib/global.h>
Description: Get the instance_id of the account by the account name
Return Value: Returns the instance_id of the account if the account exists, or -1 if the account does not exist
Params:
Params | Type | Description |
---|---|---|
data | const char* | Account name( such as nathan ) |
length | uint32_t | Account name length( such as the length of nathan is 6) |
Example:
// @abi action
void examgetacid(std::string data){
int64_t acc_id;
acc_id = get_account_id(data.c_str(), data.length());
print("account id: ",acc_id, "\n");
}
# get_account_name_by_id
Function: int64_t get_account_name_by_id(array_ptr<char> data, size_t buffer_size, int64_t account_id)
Head file: <graphenelib/global.h>
Description: Obtain an account name by the account instance id
Return Value: Returns 0 if the account exists, or -1 if the account does not exist
Params:
Params | Type | Description |
---|---|---|
data | const char* | Account name |
length | uint32_t | Account name length |
account_id | int64_t | Account instance_id |
Example:
// @abi action
void examgetname(int64_t accid){
char data[65]={0};
int64_t result;
result = get_account_name_by_id(data,65,accid);
print(static_cast<const char*>data,"\n");
}
# get_asset_id
Function: int64_t get_asset_id(const char * data, uint32_t length)
Head file: <graphenelib/global.h>
Description: Get the instance_id of the asset by the asset name
Return Value: Returns the instance_id of the asset if the asset exists, or -1 if the asset does not exist
Params:
Params | Type | Description |
---|---|---|
data | const char* | Asset name |
length | uint32_t | The length of asset name |
Example:
// @abi action
void examassid(std::string data){
int64_t assid;
assid = get_asset_id(data.c_str(),data.length());
print("asset id: ",assid, "\n");
}
# read_transaction
Function: int read_transaction(char* dst, uint32_t dst_size)
Head file: <graphenelib/global.h>
Description: Extract serialized data of the current transaction to the param dst
Return Value: When dst_size is 0, it returns the number of bytes required for reading; when dst_size is not 0, it returns the number of bytes actually read (The minimum value of dst_size is also the minimum value of transaction size )
Params:
Params | Type | Description |
---|---|---|
dst | char* | The pointer to receive the read buffer data |
dst_size | uint32_t | The read data size |
Example:
// @abi action
void examreadtrx(){
int dwsize;
dwsize =transaction_size();
char* pBuffer = new char[dwsize];
uint32_t size = read_transaction(pBuffer,dwsize);
delete[] pBuffer;
}
# transaction_size
Function: int transaction_size()
Head file: <graphenelib/global.h>
Description: Get the length of the data of the current transaction which has been serialized
Return Value: The length of the data
Example:
// @abi action
void examtrxsize(){
int dwsize;
dwsize =transaction_size();
print("the size of the serialize trx: ",dwsize,"\n");
}
# expiration
Function: uint64_t expiration()
Head file: <graphenelib/global.h>
Description: Get transaction expiration time
Return Value: The transaction expiration time
Example:
// @abi action
void exampira(){
uint64_t timenum = expiration();
print("the expiration time: ", timenum, "\n");
}
# tapos_block_num
Function: int tapos_block_num()
Head file: <graphenelib/global.h>
Description: Returns the block number referenced by the transaction
Return Value: The block number
Example:
// @abi action
void examtapnum(){
uint64_t tapos_num;
tapos_num = tapos_block_num();
print("ref block num: ",tapos_num, "\n");
}
# tapos_block_prefix
Function: uint64_t tapos_block_prefix()
Head file: <graphenelib/global.h>
Description: Get the block ID of the transaction reference (the second 32-bit number)
Return Value: Return the block ID of the transaction reference (the second 32-bit number)
Example:
// @abi action
void examtappre(){
uint64_t tapos_prefix;
tapos_prefix = tapos_block_prefix();
print("ref block id: ",tapos_prefix, "\n");
}
# read_action_data
Function: uint32_t read_action_data( void* msg, uint32_t len )
Head file: <graphenelib/action.h>
Description: Read the data of the current action
Return Value: Return the number of bytes of data actually read. If len is 0, the number of bytes required for reading will be returned. Params:
Params | Type | Description |
---|---|---|
msg | void* | The pointer to receive the read buffer data |
len | uint32_t | Buffer size |
Example:
// @abi action
void examract(uint64_t num,std::string number){
auto size = action_data_size();
char *buffer = static_cast<char*>(malloc(size));
read_action_data((void*)buffer,size);
printhex(buffer,size);
}
# action_data_size
Function: uint32_t action_data_size()
Head file: <graphenelib/action.h>
Description: Returns the number of bytes required to read the current action data
Return Value: The number of bytes
Example:
// @abi action
void examrasize(uint64_t num,std::string number){
auto size = action_data_size();
print("size: ", size, "\n");
}
# send_inline
Function: void send_inline(char *serialized_action, size_t size)
Head file: <graphenelib/action.h>
Description: Inline execute action( Generally, by constructing an action and inlining the action through its send member method, its internal implementation is send_inline,Inline action)
# unpack_action_data
Function: T unpack_action_data()
Head file: <graphenelib/action.hpp>
Description: Deserialize the current action data into a defined action object
Return Value: Action object
Example:
struct myaction {
uint64_t num;
std::string name;
GRAPHENE_SERIALIZE(myaction,(num)(name))
};
// @abi action
void examupact(uint64_t num,std::string name){
auto my = unpack_action_data<myaction>();
print(my.name, "\n");
}
# graphene_assert
Function: void graphene_assert(uint32_t test, const char* msg)
Head file: <graphenelib/system.h>
Description: If the assertion fails, interrupt the execution of this contract and roll all states
Params:
Params | Type | Description |
---|---|---|
test | uint32_t | Assertion |
msg | const char* | Message |
Example:
// @abi action
void examassert(){
uint64_t number=1;
graphene_assert(number == 1, "wrong!");
}
# graphene_assert_message
Function: void graphene_assert_message(uint32_t test, const char* msg, uint32_t msg_len)
Head file: <graphenelib/system.h>
Description: If the assertion fails, interrupt the execution of this contract and roll all states
Params:
Params | Type | Description |
---|---|---|
test | uint32_t | Assertion |
msg | const char* | Message |
msg_len | uint32_t | Message size |
Example:
// @abi action
void examassmsg(){
uint64_t number=1;
std::string msg = "wrong!!!";
graphene_assert_message(number == 1, msg.c_str(),msg.length());
}
Function: void print(const char* ptr)
Head file: <graphenelib/system.h>
Description: Print log(Please click Contract Debug)
Params:
Params | Type | Description |
---|---|---|
ptr | const char* | Log |
Example:
// @abi action
void examprint(){
print("example example example!!!\n");
}
# example
The sample contract for api usage has been deployed to the test network, which can be tested by the IDE client, click to view contract source,contract name is apitest3
# Inline action
# Description
GXChain supports one contract to call another contract and supports payment accounts to set ram fees. For examples :User --> contract_A --> contract_B
, for contract contract_B
, User
is the original caller, and contract_A
is sender.
However, cross-contract calls are limited by the number of layers: no more than three contracts in call chain. Such as : User -> contract_A -> contract_B -> contract_C
, if the number of call levels is greater than 3, the execution will be terminated. If the call chain forms a loop, it will also terminate execution.
The ram resource used in the contract, the payment account can be set to the following four identities:
ram_fee payer | Description |
---|---|
0 | Contract account itself (same as _slef) |
_self | Contract account itself (same as 0) |
sender | Contract call account |
original | The account originally calls the contract , which is used as the starting call account in the cross-contract call. |
# Example
contract_A --> contract_B
, contract_A
contract calls contract_B
contract.
- Construct an action in
contract_A
containingcontract_B
account id/account name, action name, parameters, calling account (_self), additional assets. - Call the send method of the action to complete the cross-contract call.
#contract_b
···
void hi(std::string name,uint64_t number)
{
···
}
···
#contract_A
···
struct param {
std::string name;
uint64_t number;
};
void inlinecall(uint64_t con_b_id, std::string con_b_name){
param par{"hello", 100};
// 1: When constructing an action, use the contract account id
action contract_b_id(con_b_id, N(hi), std::move(par), _self, {1000, 1});
contract_b_id.send();
// 2: When constructing an action, use the contract account name
action contract_b_name(con_b_name, N(hi), std::move(par), _self, {1000,1});
contract_b_name.send();
}
···
# Multi-index table
# index
# Introduction to multi-index table
The data must be stored in units of C++ class instances, so the stored C++ classes must be defined in the contract. Each class has a table, similar to a single table in the sql database. The difference is that it has the following characteristics:
Support for multi index
Joint indexing is not supported
Only the primary key is unique
Index type only supports uint64_t type
If you want to use a string as an index, you must use the
uint64_t string_to_name(string str)
in the contract library to convert the string to uint64_t. The string length is limited to 12 characters and can only include ([az].[1 -5]) a total of 32 characters
For indexes other than the primary key, when there are multiple record index values, the acquired object is the earliest inserted record.
Support for additions and deletions
# example code
#include <graphenelib/contract.hpp>
#include <graphenelib/dispatcher.hpp>
#include <graphenelib/multi_index.hpp>
#include <graphenelib/print.hpp>
using namespace graphene;
class multindex : public contract
{
struct offer;
public:
multindex(uint64_t id)
: contract(id)
, offers(_self, _self)
{
}
//@abi action
void additem(uint64_t i1, uint64_t i2, std::string name)
{
uint64_t pk = offers.available_primary_key();
print("pk=", pk);
offers.emplace(0, [&](auto &o) {
o.id = pk;
o.idx1 = i1;
o.idx2 = i2;
o.stringidx = graphenelib::string_to_name(name.c_str());
});
}
//@abi action
void getbypk(uint64_t key)
{
auto it = offers.find(key);
if (it != offers.end()) {
dump_item(*it);
}
}
//@abi action
void getbyidx1(uint64_t key)
{
auto idx = offers.template get_index<N(idx1)>();
auto matched_offer_itr = idx.lower_bound(key);
if (matched_offer_itr != idx.end()) {
dump_item(*matched_offer_itr);
}
}
//@abi action
void getbyidx2(uint64_t key)
{
auto idx = offers.template get_index<N(idx2)>();
auto matched_offer_itr = idx.lower_bound(key);
if (matched_offer_itr != idx.end()) {
dump_item(*matched_offer_itr);
}
}
//@abi action
void getbystring(std::string key)
{
auto idx = offers.template get_index<N(stringidx)>();
auto matched_offer_itr = idx.lower_bound(N(key));
if (matched_offer_itr != idx.end()) {
dump_item(*matched_offer_itr);
}
}
private:
void dump_item(const offer &o)
{
print("offer.id:", o.id, "\n");
print("offer.idx1:", o.idx1, "\n");
print("offer.idx2:", o.idx2, "\n");
graphenelib::name n;
n.value = o.stringidx;
print("offer.stringidx:", n.to_string().c_str(), "\n");
}
private:
//@abi table offer i64
struct offer {
uint64_t id;
uint64_t idx1;
uint64_t idx2;
uint64_t stringidx;
uint64_t primary_key() const { return id; }
uint64_t by_index1() const { return idx1; }
uint64_t by_index2() const { return idx2; }
uint64_t by_stringidx() const {return stringidx; }
GRAPHENE_SERIALIZE(offer, (id)(idx1)(idx2)(stringidx))
};
typedef multi_index<N(offer), offer,
indexed_by<N(idx1), const_mem_fun<offer, uint64_t, &offer::by_index1>>,
indexed_by<N(idx2), const_mem_fun<offer, uint64_t, &offer::by_index2>>,
indexed_by<N(stringidx), const_mem_fun<offer, uint64_t, &offer::by_stringidx>>>
offer_index;
offer_index offers;
};
GRAPHENE_ABI(multindex, (additem)(getbypk)(getbyidx1)(getbyidx2)(getbystring))
# Define type
private:
//@abi table offer i64
struct offer {
uint64_t id;
uint64_t idx1;
uint64_t idx2;
uint64_t stringidx;
uint64_t primary_key() const { return id; }
uint64_t by_index1() const { return idx1; }
uint64_t by_index2() const { return idx2; }
uint64_t by_stringidx() const {return stringidx; }
GRAPHENE_SERIALIZE(offer, (id)(idx1)(idx2)(stringidx))
};
typedef multi_index<N(offer), offer,
indexed_by<N(idx1), const_mem_fun<offer, uint64_t, &offer::by_index1>>,
indexed_by<N(idx2), const_mem_fun<offer, uint64_t, &offer::by_index2>>,
indexed_by<N(stringidx), const_mem_fun<offer, uint64_t, &offer::by_stringidx>>> offer_index;
offer_index offers;
Must add comments //@abi table offer i64
offer
is table name, Can't exceed 12 characters and can only be [a-z] 1-5]
,and can only start with letters or .
.
i64
is an index type.
Struct offer{...} is a normal c++ class.
GRAPHENE_SERIALIZE(offer, (id)(idx1)(idx2)(stringidx))
is used to serialize the table such as GRAPHEN_SERIALIZE (typename, (field1)(field2)(field3)(field4)...)
.
uint64_t primary_key() const { return id; }
Specify a unique primary key, the function name and type of this code are fixed and cannot be changed.The other three functions are used to define the secondary index.
The definition of index
typedef multi_index<N(offer), offer,
indexed_by<N(idx1), const_mem_fun<offer, uint64_t, &offer::by_index1>>,
indexed_by<N(idx2), const_mem_fun<offer, uint64_t, &offer::by_index2>>,
indexed_by<N(stringidx), const_mem_fun<offer, uint64_t, &offer::by_stringidx>>> offer_index;
`offer` is consistent with the offer in '//@abi table offer i64', `offer` is used to specify the type name previously defined
indexed_by<N(idx1), const_mem_fun<offer, uint64_t, &offer::by_index1>>,
const_mem_fun<offer, uint64_t, &offer::by_index1>
// There are secondary indexes here and a table can define up to 16 secondary indexes.
// N(idx1) used to define the index name
In the contract constructor you need to use the contract's _self (contract id) to initialize an instance of a multi-index type.
multindex(uint64_t id)
: contract(id)
, offers(_self, _self)
{}
# Emplace
uint64_t pk = offers.available_primary_key();
print("pk=", pk);
offers.emplace(0, [&](auto &o) {
o.id = pk;
o.idx1 = i1;
o.idx2 = i2;
o.stringidx = graphenelib::string_to_name(name.c_str());
});
uint64_t pk = offers.available_primary_key()
The next legal primary key used to obtain the auto-increment primary key, or you can specify it yourself
offers.emplace(0, [&](auto &o) {
o.id = pk;
o.idx1 = i1;
o.idx2 = i2;
o.stringidx = graphenelib::string_to_name(name.c_str());
});
Inserting an object with a lambda expression to assign a value to the newly added object o
o.stringidx = graphenelib::string_to_name(name.c_str())
is a way that uses a string type to query.
# Erase
Please read Find first
To delete an object by table's iterator, you should call the find function to find the iterator of the object you want to delete.
offers.erase(it)
'it' is an iterator of the object which you want find.
# Find
auto idx = offers.template get_index<N(stringidx)>();
auto matched_offer_itr = idx.lower_bound(N(key));
if (matched_offer_itr != idx.end()) {
dump_item(*matched_offer_itr);
}
auto idx = offers.template get_index<N(stringidx)>()
Get the index of the offer table name as stringidx
, the offer table has 4 indexes, one is the primary key index, and the other 3 secondary indexs are idx1
, idx2
, stringidx
auto matched_offer_itr = idx.lower_bound(N(key))
Find the object of the string key by the primary key index and return its iterator matched_offer_itr
# Modify
Modify objects by their iterators and lambda expressions
offers.modify(it, 0, [&](auto &o) {
o.idx1 = 1000;
});