This section of the guide provides instructions for sending actions to an external contract. The examples of the execution of instructions are shown in the contract, where the performed actions are kept.
Log into CONTRACTS_DIR
and create a directory there with the name abcounter
, as well as the file abcounter.cpp
.
cd CONTRACTS_DIR
mkdir abcounter
cd abcounter
touch abcounter.cpp
Open the abcounter.cpp
file and write the following program text into it:
#include <eosiolib/eosio.hpp>
using namespace eosio;
class [[eosio::contract]] abcounter : public eosio::contract {
private:
struct [[eosio::table]] counter {
name key;
uint64_t emplaced;
uint64_t modified;
uint64_t erased;
uint64_t primary_key() const {
return key.value;
}
};
using count_index = eosio::multi_index<"counter"_n, counter>;
public:
using contract::contract;
abcounter(name receiver, name code, datastream<const char*> ds):
contract(receiver, code, ds) {}
[[eosio::action]]
void count(name user, std::string type) {
require_auth( name("addressbook"));
count_index counts(name(_code), _code.value);
auto iterator = counts.find(user.value);
if (iterator == counts.end()) {
counts.emplace("addressbook"_n, [&]( auto& row ) {
row.key = user;
row.emplaced = (type == "emplace") ? 1 : 0;
row.modified = (type == "modify") ? 1 : 0;
row.erased = (type == "erase") ? 1 : 0;
});
}
else {
counts.modify(iterator, "addressbook"_n, [&]( auto& row ) {
if(type == "emplace") { row.emplaced += 1; }
if(type == "modify") { row.modified += 1; }
if(type == "erase") { row.erased += 1; }
});
}
}
};
EOSIO_DISPATCH( abcounter, (count));
The peculiarity of this text is that it has a restriction on calls to an action for the contract account. For restriction, use require_auth
for the addressbook
contract. Only the addressbook
contract account is authorized to perform require_auth
. The count
action is not sent by the user, but by the addressbook
contract.
require_auth( name("addressbook"));
cleos create account cyber abcounter YOUR_PUBLIC_KEY
eosio-cpp -o abcounter.wasm abcounter.cpp --abigen
cleos set contract abcounter CONTRACTS_DIR/abcounter
cd CONTRACTS_DIR/addressbook
Open the file addressbook.cpp
and create in it another helper named increment_counter
in the private part of the contract.
void increment_counter(name user, std::string type) {
action counter = action(
permission_level{get_self(),"active"_n},
"abcounter"_n,
"count"_n,
std::make_tuple(user, type)
);
counter.send();
}
The action body contains:
- выдача разрешения уровня
active
.Для разрешения функцияget_self()
возвращает текущий контрактaddressbook
; - the
abcounter
account name of the contract; - the
count
action to call; - data, username and line type.
Add calls to «set», «modify» and «erase» helpers.
//Emplace
increment_counter(user, "emplace");
//Modify
increment_counter(user, "modify");
//Erase
increment_counter(user, "erase");
As a result, the addressbook.cpp
file should look like this:
#include <eosiolib/eosio.hpp>
#include <eosiolib/print.hpp>
using namespace eosio;
class [[eosio::contract]] addressbook : public eosio::contract {
private:
struct [[eosio::table]] person {
name key;
std::string first_name;
std::string last_name;
uint64_t age;
std::string street;
std::string city;
std::string state;
uint64_t primary_key() const { return key.value; }
uint64_t get_secondary_1() const { return age;}
};
void send_summary(name user, std::string message) {
action(
permission_level{get_self(),"active"_n},
get_self(),
"notify"_n,
std::make_tuple(user, name{user}.to_string() + message)
).send();
};
void increment_counter(name user, std::string type) {
action counter = action(
permission_level{get_self(),"active"_n},
"abcounter"_n,
"count"_n,
std::make_tuple(user, type)
);
counter.send();
}
typedef eosio::multi_index<"person"_n, person,
indexed_by<"byage"_n, member<person, uint64_t, &person::get_secondary_1>>
> address_index;
public:
using contract::contract;
addressbook(name receiver, name code, datastream<const char*> ds): contract(receiver, code, ds) {}
[[eosio::action]]
void upsert(name user, std::string first_name, std::string last_name, uint64_t age, std::string street, std::string city, std::string state) {
require_auth(user);
address_index addresses(_code, _code.value);
auto iterator = addresses.find(user.value);
if( iterator == addresses.end() )
{
addresses.emplace(user, [&]( auto& row ) {
row.key = user;
row.first_name = first_name;
row.last_name = last_name;
row.age = age;
row.street = street;
row.city = city;
row.state = state;
send_summary(user, " successfully emplaced record to addressbook");
increment_counter(user, "emplace");
});
}
else {
std::string changes;
addresses.modify(iterator, user, [&]( auto& row ) {
row.key = user;
row.first_name = first_name;
row.last_name = last_name;
row.age = age;
row.street = street;
row.city = city;
row.state = state;
send_summary(user, " successfully modified record to addressbook");
increment_counter(user, "modify");
});
}
}
[[eosio::action]]
void erase(name user) {
require_auth(user);
address_index addresses(_code, _code.value);
auto iterator = addresses.find(user.value);
eosio_assert(iterator != addresses.end(), "Record does not exist");
addresses.erase(iterator);
send_summary(user, " successfully erased record from addressbook");
increment_counter(user, "erase");
}
[[eosio::action]]
void notify(name user, std::string msg) {
require_auth(get_self());
require_recipient(user);
}
};
EOSIO_DISPATCH( addressbook, (upsert)(notify)(erase));
Since the changes should not affect the ABI, you do not need to re-edit the ABI file (make sure that this file has not been updated).
eosio-cpp -o addressbook.wasm addressbook.cpp
cleos set contract addressbook CONTRACTS_DIR/addressbook
Before testing, make sure that the abcounter
and addressbook
contracts are set.
8.6.1 Verify that a notification is sent to the abcounter
contract, as well as a change in the entry in the addressbook
contract table by executing:
cleos push action addressbook upsert '["alice", "alice", "liddell", 19, "123 drink me way", "wonderland", "amsterdam"]' -p alice@active
The action is considered successful if the output contains the following information:
executed transaction: ...
# addressbook <= addressbook::upsert {"user":"alice","first_name":"alice","last_name":"liddell","street":"123 drink me way","city":"wonde...
# addressbook <= addressbook::notify {"user":"alice","msg":"alice successfully modified record in addressbook"}
# alice <= addressbook::notify {"user":"alice","msg":"alice successfully modified record in addressbook"}
# abcounter <= abcounter::count {"user":"alice","type":"modify"}
8.6.2 Check the appearance of a row in the table for the alice
user.
cleos get table abcounter abcounter counts --lower alice --limit 1
The action is considered successful if the result is the following:
{
"rows": [{
"key": "alice",
"emplaced": 1,
"modified": 0,
"erased": 0
}
],
"more": false
}
8.6.3 Check that the upsert
method modifies the record.
cleos push action addressbook upsert '["alice", "alice", "liddell", 21,"1 there we go", "wonderland", "amsterdam"]' -p alice@active
The actions are considered successfully completed if the resulting output contains the following information:
executed transaction: …
# addressbook <= addressbook::upsert {"user":"alice","first_name":"alice","last_name":"liddell","street":"1 coming down","city":"normalla...
# addressbook <= addressbook::notify {"user":"alice","msg":"alice successfully emplaced record to addressbook"}
>> Notified
# alice <= addressbook::notify {"user":"alice","msg":"alice successfully emplaced record to addressbook"}
# abcounter <= abcounter::count {"user":"alice","type":"emplace"}
warning: transaction executed locally, but may not be confirmed by the network yet ]
8.6.4 Verify the liquidation of the alice
user entry from the table by running:
cleos push action addressbook erase '["alice"]' -p alice@active
The action is considered to be successfully completed if the resulting output contains the following information:
executed transaction: ...
# addressbook <= addressbook::erase {"user":"alice"}
>> Erased
# addressbook <= addressbook::notify {"user":"alice","msg":"alice successfully erased record from addressbook"}
>> Notified
# alice <= addressbook::notify {"user":"alice","msg":"alice successfully erased record from addressbook"}
# abcounter <= abcounter::count {"user":"alice","type":"erase"}
warning: transaction executed locally, but may not be confirmed by the network yet ]
Toaster:addressbook sandwich$
8.6.5 Test the ability to manipulate abcounter
contract data by executing:
cleos push action abcounter count '["alice", "erase"]' -p alice@active
cleos get table abcounter abcounter counts --lower alice
The abcounter
contract table should contain the following information:
{
"rows": [{
"key": "alice",
"emplaced": 1,
"modified": 1,
"erased": 1
}
],
"more": false
}