Smart contract should have entry point void __apply()
. There is no other requirement for the rest of the code of the smart contract, though the approach for writing smart contract used in the examples is encouraged. There are several limitations to the C++ language in order to get small and portable bytecode
-
dynamic linked libraries and runtime loading of the dynamic library is
not supported
-
C++ and C standard libraries are not supported fully on all the C++
toolchains, only classes/functions defined as header only can be
included. As a consequence of that the following language features are
not supported
-
RTTI
-
exceptions
-
constructors of global variables are not called on the program
startup
-
Contract parameters can be retrieved by get_parameters variadic functions, should return false on error. Contract return value can be set using set_return_value variadic functions.
Several standard classes and functions are provided as a part of development environment:
- string, vector, hashmap
- cryptography functions
- classes/functions for accessing chain
- classes/functions for accessing persistent storage
Following smart contract written in C++ implements simple program for custom token.
#include "string.hpp"
#include "parameters.hpp"
#include "return_value.hpp"
#include "contract_base.hpp"
#include "db_types.hpp"
#include "db_hashmap.hpp"
namespace x86_64_contract
{
class contract : public contract_base
{
private:
string _name{"Contract"};
DB_STRING(_owner);
DB_UINT64(_total_supply);
DB_UINT64(_max_supply);
DB_HASHMAP(db_string, db_uint64, _balances);
public:
void constructor() override
{
_owner = get_origin_sender();
_total_supply = 0;
_max_supply = 1'000'000'000;
}
std::uint64_t total_supply() const
{
return _total_supply;
}
std::uint64_t balance_of(const string& account)
{
if(db_hashmap<db_string, db_string>::npos != _balances.find(account))
{
return _balances[account];
}
return 0;
}
bool mint(std::uint64_t amount)
{
if(get_origin_sender() == _owner && _total_supply + amount <= _max_supply)
{
_total_supply += amount;
_balances[_owner] += amount;
return true;
}
return false;
}
bool transfer(const string& from, const string& to, std::uint64_t amount)
{
if(get_origin_sender() == from && _balances[from] >= amount)
{
_balances[from] -= amount;
_balances[to] += amount;
return true;
}
return false;
}
};
extern "C" void __apply()
{
CONTRACT(c);
string function;
parameters::get_function_name(function);
if (function == "total_supply")
{
set_return_values(c.total_supply());
}
else if (function == "balance_of")
{
string account;
if(get_parameters(account))
set_return_values(c.balance_of(account));
}
else if (function == "mint")
{
std::uint64_t amount = 0;
if(get_parameters(amount))
set_return_values(c.mint(amount));
}
else if (function == "transfer")
{
string from, to;
std::uint64_t amount = 0;
if(get_parameters(from, to, amount))
set_return_values(c.transfer(from, to, amount));
}
}
}
This smart contract receives the name of the function to be invoked as a parameter, calls the corresponding function of the contract and returns the value back to the blockchain.
C++ smart contract should be compiled and linked into ELF executable.
gcc compiler flags
-masm=intel -w -nostdlib -fno-rtti -fno-exceptions -fno-unwind-tables -ffreestanding -no-pie -std=c++14 -mno-mmx -mno-sse3 -mno-sse4.1 -mno-sse4.2
gcc linker flags
-nostdlib -fno-rtti -fno-exceptions -fno-unwind-tables -ffreestanding
The 64 bit ELF executable generated by the C++ linker tool should be repackaged, uploaded and executed using the general flow for the x86-64 smart contracts described in the corresponding sections.