Smart Contract Development on EOS - Part 2
What happened last time ...
In the previous section of series - Getting Ready for Smart Contract Development on EOS, we took a deep dive in setting up the correct environment needed for interacting with a blockchain infrastructure managed by the EOS.io software. We learned how to spin up a developer sandbox environment through pulling official pre-built development images through docker. We also learned how to properly set up a single blockchain, create the necessary accounts, keys, and wallets in order to deploy contracts into the blockchain.
In this article ...
In this section part 2 section of the series, we'll continue to explore how accounts and wallets interact with smart contracts that are deployed on the blockchain. We'll first get a taste of smart contract development by simply constructing a "Hello World" contract and deploying it under the sandbox environment which we have created in the previous part of the tutorial.
explore how to deploy a simple token contract on the Eos blockchain, then we will proceed to issue the tokens and calling functions defined within the smart contract
Before we start ...
Following the wallet set up that we have done in the last article in Getting Ready for Smart Contract Development on EOS, we should first get the more clearer sense of how wallet, keys, and accounts interact within the Eos ecosystem. Cleos - which is the command-line tool that acts as an interface between nodeos - the actual node daemon hosting the blockchain and keosd - the component responsible for managing keys in the wallet. The Eos blockchain hosted by nodeos will only recognize accounts as an identity that's associated with each individual entity acting and transacting on the blockchain, and these accounts are connected by the cryptographic identity of the private key that we are accustomed to in other blockchain platforms. These private keys act as the credential to prove to the Eos blockchain that an individual is indeed the owner of the account. The wallet itself, however, doesn't directly interface with the Eos blockchain, it's only responsibility is to manage and keep the private key from being exposed. In the sandbox developer environment that we have created in part 1 to prepare yourself for smart contract development, the sandbox Eos blockchain will actually generate a block producing account called eosio. The specific private key of this eosio account can be found within the config.ini file. Without importing the private key to any one of our active wallet, we won't be able to gain access to the eosio account. Since in Eos, account generation can only be done by an existing account, we won't be able to generate additional accounts, signing transaction and deploying smart contracts; thus, we have imported the key in one of our generated wallet of "producer", in practice, the key can be imported into any of the active wallet or even multiple wallets.
Generating the Hello World Contract.
Let's first create a "hello world" contract by creating a folder call "hello" and change the directory into the folder,
mkdir hello && cd hello
For the simplicity of this tutorial, we shall create a pre-written smart contract that the eos developer tutorial has kindly porvided,
#include <eosiolib/eosio.hpp>
#include <eosiolib/print.hpp>
using namespace eosio;
class hello : public eosio::contract {
public:
using contract::contract;
/// @abi action
void hi( account_name user ) {
print( "Hello, ", name{user} );
}
};
EOSIO_ABI( hello, (hi) )
We will dive deeply in terms of writing our own smart contract in our future tutorials. The "hello world" smart contract encompasses a simple function which upon calling with a parameter that defines the account name through a signed transaction, will return the string "Hello, " and the account name.
For the next step, we need to compile the hello.cpp into a Web Assembly(WASM) file .wast and abi code that is necessary for the deployment of the smart contract through a CLI tool called eosiocpp that is packaged into the Eos dev docker image. In order to use eosiocpp, we need to access the one of Docker container that we have created and execute the commands from within.
First, place the "hello" folder which we just have created into "keosd-data-volume" - the shared folder mount point between the docker container keosd and the host system. Note, since the folder itself is created by docker we will be needing superuser privilege when moving files into it.
sudo mv hello keosd-data-volume/
Get into the bash of the keosd container by,
docker container exec -it keosd bash
Change directory to where the "hello" folder is located within the container.
cd /opt/eosio/bin/data-dir/hello
Note that /opt/eosio/bin/data-dir path is actually configured in the yaml file that brought up the containers.
Generate web assembly and abi through the compiler.
eosiocpp -o hello.wast hello.cpp &&
eosiocpp -g hello.abi hello.cpp
Note there may be warnings about no Ricardian clauses found which can safely ignore
Deploying the Hello World Contract.
Let's create a contract account called "hello.code" to deploy a smart contract. Creating a separate account for the purpose of the deployment is preferred since other participants will access the contract that we've created through the reference of the contract account.
cleos create account eosio hello.code ${owner public key} ${active public key}
Note the corresponding private key to the owner public key and active public key needs to be in an unlocked wallet in order to sign of transaction with the account that we have created.
The owner public key and active public defines the permission level that the account is associated with. By default each account will have two native named permission which is owner
and active
. The owner
permission level have the greatest authority of the account and will be used in cases where greatest authority is required such as changing the ownership of the account. The active
permission, on the other hand, is more commonly used for signing transactions associated with the account. Custom permission level can also be implemented to extend controls of the account.
Let's deploy our "hello world" contract and sign off the transaction with the active
permission of our newly generated contract account hello.code
.
# if we are inside the docker container under the /opt/eosio/bin/data-dir path
cleos -u http://nodeosd:8888 set contract hello.code ./hello -p hello.code@active
# if we are in the host environment
cleos set contract hello.code /opt/eosio/bin/data-dir path/hello -p hello.code@active
Note since we are deploying our contract within the keosd
docker container environment, we need to specify the location that our nodeos is running on within the network with -u http://nodeosd:8888
.
After our contact has been successfully deployed, let's try to invoke the contract with our user
account. Call the hi
function specified in the smart contract by referring to the contract account hello.code
cleos -u http://nodeosd:8888 push action hello.code hi '["user"]' -p user@active
executed transaction: 4c10c1426c16b1656e802f3302677594731b380b18a44851d38e8b5275072857 244 bytes 1000 cycles
# hello.code <= hello.code::hi {"user":"user"}
If you have attach the to log output of the nodeos docker container, you should see output similar to the following,
1025500ms thread-0 producer_plugin.cpp:944 produce_block ] Produced block 00004de945a23f63... #19945 @ 2018-05-25T19:17:05.500 signed by eosio [trxs: 0, lib: 19944, confirmed: 0]
1025830ms thread-0 apply_context.cpp:28 print_debug ]
[(hello.code,hi)->hello.code]: CONSOLE OUTPUT BEGIN =====================
Hello, user
[(hello.code,hi)->hello.code]: CONSOLE OUTPUT END =====================
1026000ms thread-0 producer_plugin.cpp:944 produce_block ] Produced block 00004deaebee2dc5... #19946 @ 2018-05-25T19:17:06.000 signed by eosio [trxs: 1, lib: 19945, confirmed: 0]
Deploying a token contract
Now that we have successfully deployed our first "hello world" contract, let's try to deploy another token contract that can be used to issuing your own tokens on the Eos platform.
Again, let's start by creating a token contract account that's responsible for managing the token contract.
cleos create account eosio eosio.token ${owner public key} ${active public key}
We proceed to deploy the pre-compiled contract eosio.token
found under the /contracts
folder within the keosd docker container
We can peek at the functions that are defined within the contract by looking at the .hpp
file.
contracts/eosio.token/eosio.token.hpp:
void create( account_name issuer,
asset maximum_supply );
void issue( account_name to, asset quantity, string memo );
void transfer( account_name from,
account_name to,
asset quantity,
string memo );
The concise way to call the create
function to create a new token:
cleos push action eosio.token create '[ "eosio", "1000000000.0000 SYS"]' \
-p eosio.token@active
executed transaction: 0e49a421f6e75f4c5e09dd738a02d3f51bd18a0cf31894f68d335cd70d9c0e12 120 bytes 1000 cycles
# eosio.token <= eosio.token::create {"issuer":"eosio","maximum_supply":"1000000000.0000 SYS"}
The more verbose way:
cleos push action eosio.token create '[ "eosio", "1000000000.0000 SYS"]' \
-p eosio.token@active
executed transaction: 0e49a421f6e75f4c5e09dd738a02d3f51bd18a0cf31894f68d335cd70d9c0e12 120 bytes 1000 cycles
# eosio.token <= eosio.token::create {"issuer":"eosio","maximum_supply":"1000000000.0000 SYS"}
Either way, the command has created a new token SYS with a precision of 4 decimals and a maximum supply of 1000000000.0000 SYS.
Issue Tokens and Transfering them.
The following command will issue 100.0000 SYS to the account user
:
cleos push action eosio.token issue '[ "user", "100.0000 SYS", "memo" ]' \
-p eosio@active
The output should look similar to the following indicating the successful transaction.
executed transaction: 822a607a9196112831ecc2dc14ffb1722634f1749f3ac18b73ffacd41160b019 268 bytes 1000 cycles
# eosio.token <= eosio.token::issue {"to":"user","quantity":"100.0000 SYS","memo":"memo"}
>> issue
# eosio.token <= eosio.token::transfer {"from":"eosio","to":"user","quantity":"100.0000 SYS","memo":"memo"}
>> transfer
# eosio <= eosio.token::transfer {"from":"eosio","to":"user","quantity":"100.0000 SYS","memo":"memo"}
# user <= eosio.token::transfer {"from":"eosio","to":"user","quantity":"100.0000 SYS","memo":"memo"}
Since now, the user
account have some amount of SYS token, the user
account can invoke the transfer
function to transfer some tokens to another account such as test
:
cleos push action eosio.token transfer \
'[ "user", "tester", "25.0000 SYS", "m" ]' -p user@active
Notice that this time the transaction is actually signed by the user account rather than the token contract account.
The output should look similar to the following indicating the successful transaction.
executed transaction: 06d0a99652c11637230d08a207520bf38066b8817ef7cafaab2f0344aafd7018 268 bytes 1000 cycles
# eosio.token <= eosio.token::transfer {"from":"user","to":"tester","quantity":"25.0000 SYS","memo":"m"}
>> transfer
# user <= eosio.token::transfer {"from":"user","to":"tester","quantity":"25.0000 SYS","memo":"m"}
# tester <= eosio.token::transfer {"from":"user","to":"tester","quantity":"25.0000 SYS","memo":"m"}
Moving Forward
Now that we have learned how to deploy and interact with smart contracts, it's essential to start defining our own contract logic that we see fit for the use cases of our Dapp. In the next part of our series, we'll dive deeper into the details of how to exactly write a smart contract. Stay tuned ...The output should look something as the following indicating the successful transaction.
Contact/About Us
If you are an advanced blockchain user, feel free to use any of those tools that you are comfortable with.
If you like what we do and believe in EOS9CAT, vote for eosninecatbp! Waiting for your support. Have a question, send an email to us or visit our website.
FOLLOW US on Facebook, Telegram, Medium, SteemIt, Github, Meetup E0S9CAT, Reddit, Twitter, and LinkedIn.