├── README.md ├── basics.md ├── deep.md └── setup.md /README.md: -------------------------------------------------------------------------------- 1 | # EOSIO / eosjs / Scatter Tutorials 2 | 3 | - [Setting up a virtual machine and EOSIO for smart contract development](https://github.com/nsjames/Scatter-Tutorials/blob/master/setup.md) 4 | - [Creating your first Smart Contract, understanding the structure and persisting things to the blockchain](https://github.com/nsjames/Scatter-Tutorials/blob/master/basics.md) 5 | - [Diving deeper into EOSIO Smart Contracts: Typedefs, Assertions and Singletons](https://github.com/nsjames/Scatter-Tutorials/blob/master/deep.md) 6 | -------------------------------------------------------------------------------- /basics.md: -------------------------------------------------------------------------------- 1 | # Creating your first Smart Contract 2 | 3 | ## 1) Changing some settings to listen on all interfaces 4 | - `sudo nano ~/.local/share/eosio/nodeos/config/config.ini` 5 | - Change the `http-server-address` to `0.0.0.0:8888` 6 | - `sudo nano ~/.bash_aliases` 7 | - Add a `#` in front of the `cleos` alias we set up before, we will no longer use it 8 | - restart `nodeos` 9 | 10 | ## 2) Setting up the `comptract.sh` script 11 | - `cd ~` 12 | - `mkdir scripts` 13 | - `cd scripts` 14 | - `nano comptract.sh` 15 | 16 | ``` 17 | #!/bin/bash 18 | 19 | if [[ $# -ne 2 ]]; then 20 | echo "USAGE: comptract.sh from within the directory" 21 | exit 1 22 | fi 23 | 24 | ACCOUNT=$1 25 | CONTRACT=$2 26 | 27 | eosiocpp -o ${CONTRACT}.wast ${CONTRACT}.cpp && 28 | eosiocpp -g ${CONTRACT}.abi ${CONTRACT}.cpp && 29 | cleos set contract ${ACCOUNT} ../${CONTRACT} 30 | ``` 31 | 32 | - `chmod +x comptract.sh` 33 | - Add `export PATH=$PATH:~/scripts` to the end of your `~/.profile` file 34 | - `export PATH=$PATH:~/scripts` 35 | 36 | ## 3) Create a new account for the `basics` contract 37 | - `cleos create account eosio basics ` 38 | 39 | 40 | ## 4) Creating the basics contract 41 | - Make a new directory inside of your `shared_contracts` folder called `tutorials` and then another inside of that called `basics` 42 | - Create a new cpp file called `basics.cpp` 43 | 44 | ``` 45 | #include 46 | #include 47 | 48 | using namespace eosio; 49 | using namespace std; 50 | 51 | class basics : public contract { 52 | using contract::contract; 53 | 54 | public: 55 | basics( account_name self ) : 56 | contract(self), 57 | _statuses(_self, _self){} 58 | 59 | // @abi action 60 | void test( name sender, string status ){ 61 | require_auth(sender); 62 | 63 | auto iter = _statuses.find(sender); 64 | if(iter == _statuses.end()) _statuses.emplace( sender, [&]( auto& row) { 65 | row.sender = sender; 66 | row.status = status; 67 | }); 68 | else _statuses.modify( iter, 0, [&]( auto& row) { 69 | row.status = status; 70 | }); 71 | } 72 | 73 | private: 74 | 75 | // @abi table 76 | struct statuses { 77 | name sender; 78 | string status; 79 | 80 | name primary_key() const { return sender; } 81 | EOSLIB_SERIALIZE( statuses, (sender)(status) ) 82 | }; 83 | 84 | multi_index _statuses; 85 | 86 | 87 | }; 88 | 89 | EOSIO_ABI( basics, (test) ) 90 | ``` 91 | 92 | ## 5) Compiling and testing 93 | - `cd /media/sf_shared_contracts/tutorials/basics/` 94 | - `comptract.sh basics basics` 95 | - `cleos push action basics test '["asdf"]' -p eosio` 96 | - `cleos push action basics test '["eosio", "Hello World"]' -p eosio` 97 | -------------------------------------------------------------------------------- /deep.md: -------------------------------------------------------------------------------- 1 | # Diving deeper into EOSIO Smart Contracts: Typedefs, Assertions and Singletons 2 | 3 | Here's some topics we'll be covering 4 | * typedefs 5 | * Assertions 6 | * Singletons 7 | 8 | 9 | ## Typedefs 10 | 11 | Typedefs are a way to abstract away types to something more friendly. 12 | 13 | Consider the following: 14 | ``` 15 | uint64_t varname = 1; 16 | id varname = 1; 17 | ``` 18 | 19 | It's far easier to read and discern what the second variable is even though they are exactly the same. 20 | It's trivial to do this, let's look at a simple example: 21 | 22 | ``` 23 | typedef uint64_t id; 24 | ``` 25 | 26 | When we put something like this in our contract we can now use `id` instead of `uint64_t` to keep our code clean and succint. 27 | However, this is only the tip of the iceberg. 28 | 29 | ``` 30 | // @abi table 31 | struct users { 32 | account_name user; 33 | id index; 34 | 35 | account_name primary_key(){ return user; } 36 | EOSLIB_SERIALIZE( users, (user)(index) ) 37 | }; 38 | typedef multi_index user_ids_table; 39 | ``` 40 | 41 | When we define a table in EOSIO we are actually defining a typedef which abstracts away 42 | ` multi_index(uint64_t code, uint64_t scope) ` 43 | into something more friendly like `user_ids_table` so that when we want to use it we can simply do something like 44 | ``` 45 | user_ids_table table(_self, _self); 46 | ``` 47 | where `_self` is a reference to the contract itself. 48 | 49 | 50 | ## Assertions 51 | 52 | Assertions are our way of validating conditional logic within a contract and reverting if conditions are not met as well as 53 | returning an error message about the failure. 54 | 55 | ``` 56 | eosio_assert(1 == 1, "One is not equal to one."); 57 | eosio_assert(1 == 2, "One is not equal to two."); 58 | ``` 59 | 60 | You can use any type of boolean logic within the first parameter to validate conditions. 61 | 62 | There are a few helpers available to do specific things like assert authorization and permissions. 63 | ``` 64 | require_auth(someAccountName); 65 | require_auth2(someAccountName, N(owner OR active)); 66 | ``` 67 | 68 | ## Singletons 69 | 70 | 71 | [Singletons](https://github.com/EOSIO/eos/blob/master/contracts/eosiolib/singleton.hpp) 72 | are a great way of storing configuration data and single reference mappings based on scope/account. 73 | 74 | 75 | Let's look at a way of storing a configuration struct into a singleton which has an 76 | `account_name application` 77 | which must be used to sign every action request along with the sender's signature. 78 | 79 | ``` 80 | struct config { 81 | account_name application; 82 | 83 | EOSLIB_SERIALIZE( config, (application) ) 84 | }; 85 | 86 | typedef singleton configs; 87 | ``` 88 | 89 | We've created a singleton called `configs` to hold the `config` struct. To fill this singleton we're going to also 90 | create an action called `own` which will require authorization from the contract account ( owner ) as well as the sender which 91 | will become the `application`. 92 | 93 | **If singleton `scope` parameters are omitted they default to `_self`.** 94 | 95 | ``` 96 | void own( account_name application ){ 97 | require_auth(_self); 98 | require_auth(application); 99 | 100 | eosio_assert(!configs::exists(), "Configuration already exists"); 101 | configs::set(config{application}); 102 | } 103 | ``` 104 | 105 | Now we can use this singleton elsewhere to easily grab the configs from the blockchain without having to iterate through 106 | tables by specifying the scope which is then used as the index. 107 | 108 | ``` 109 | // Remember, no specified scope defaults to _self 110 | configs::get().application 111 | ``` 112 | 113 | So how can we make use of this when dealing with actual mappings? 114 | Well lets put together some of the concepts we've learned so far and make an atomic integer mapping to a user's account 115 | which can only be created using an `owner` authority permission and the application key. 116 | 117 | ``` 118 | #include 119 | #include 120 | #include 121 | 122 | using namespace eosio; 123 | 124 | class deep : contract { 125 | using contract::contract; 126 | 127 | public: 128 | deep( account_name self ) : contract(self){} 129 | 130 | void setapp( account_name application ){ 131 | require_auth(_self); 132 | require_auth(application); 133 | 134 | eosio_assert(!configs::exists(), "Configuration already exists"); 135 | configs::set(config{application}); 136 | } 137 | 138 | void setacc( account_name user ){ 139 | require_auth2(user, N(owner)); 140 | require_auth(configs::get().application); 141 | eosio_assert(!ids::exists(user), "User already has an ID"); 142 | ids::set(nextId(), user); 143 | } 144 | 145 | void getacc( account_name user ){ 146 | eosio_assert(ids::exists(user), "User does not have an ID"); 147 | print("User's ID is: ", ids::get(user)); 148 | } 149 | 150 | void removeacc( account_name user ){ 151 | require_auth(user); 152 | require_auth(configs::get().application); 153 | eosio_assert(ids::exists(user), "User does not have an ID"); 154 | ids::remove(user); 155 | } 156 | 157 | private: 158 | typedef uint64_t id; 159 | 160 | 161 | struct config { 162 | account_name application; 163 | 164 | EOSLIB_SERIALIZE( config, (application) ) 165 | }; 166 | 167 | typedef singleton configs; 168 | 169 | 170 | typedef singleton ids; 171 | typedef singleton lastId; 172 | 173 | id nextId(){ 174 | id lid = lastId::exists() ? lastId::get()+1 : 0; 175 | lastId::set(lid); 176 | return lid; 177 | 178 | } 179 | }; 180 | 181 | EOSIO_ABI( platform, (setapp)(setacc)(getacc)(removeacc) ) 182 | ``` 183 | 184 | 185 | -------------------------------------------------------------------------------- /setup.md: -------------------------------------------------------------------------------- 1 | # Setup EOSIO Virtual Machine 2 | 3 | ## 1) Getting VirtualBox 4 | - https://www.virtualbox.org/wiki/Downloads 5 | 6 | ## 2) Getting Ubuntu 16.04 7 | - https://www.ubuntu.com/download/alternative-downloads 8 | 9 | ## 3) Setting up the Virtual Machine 10 | - Minimum requirements: at least 8096 ( 8GB ) RAM to spare and 30-50GB disk space 11 | 12 | - Create new Virtual Machine 13 | - Select Linux as the OS 14 | - Select Ubuntu as the distribution 15 | - Double click the new Virtual Machine 16 | - Load the Ubuntu 16.04 disk as the startup disk and click "Start" 17 | - Select both "Download Updates" and "Install third party" for best coverage. 18 | - Select "Erase disk and continue". 19 | - Select timezone and fill out user data 20 | - Select your language and click "Install Ubuntu" 21 | - Let it finish installing and restart 22 | - Press enter when prompted "Please remove installation medium" 23 | - Open a Terminal window ( ctrl+alt+t ) 24 | - `sudo apt update` 25 | - `sudo apt upgrade` 26 | - `sudo apt-get install virtualbox-guest-dkms` 27 | - `sudo adduser vboxsf` 28 | - Close the Virtual Machine and make sure to "Power Off", not just to exit and save it's state. 29 | 30 | ## 4) Setting up host connection 31 | - Go back to VirtualBox Manager 32 | - Right click your new virtual box 33 | - Click Settings 34 | - Click Network 35 | - Select "Bridged Adapter" from the Attached To dropdown 36 | 37 | ## 5) Setting up shared folder 38 | - While still in the Settings panel click "Shared Folders" 39 | - Click the little plus sign folder icon on the right to add a shared folder 40 | - Create a folder for contracts somewhere on your local machine called "shared_contracts" and select it for the Folder Path 41 | - Select "Auto-Mount" and make sure "Read Only" is NOT selected 42 | 43 | ## 6) Finishing Basic Setup 44 | - Save your settings. 45 | - Restart the Virtual Machine. You can see your shared folder in "Files" now with "sf_" in front of it. 46 | * This is where you will be putting your contracts so that you can work on them in Windows but use them on Ubuntu 47 | without having to copy paste them between host/guest machines. 48 | - Click the Devices menu from the top again, click "Shared Clipboard" and select "Bidirectional" 49 | * This will allow you to copy/paste between guest/host and make it easier to follow these tutorials 50 | - Open a Terminal window ( ctrl+alt+t ) 51 | - `sudo apt install git` 52 | - `ifconfig | grep "inet addr" | grep Bcast` 53 | - This should show you an IP ( something like 192.168.56.101 ). Grab it and save it somewhere. 54 | 55 | ## 7) Setting up EOSIO 56 | ( Refer to the EOSIO wiki for changes to this flow. ) 57 | - Open a Terminal window ( ctrl+alt+t ) 58 | - `cd ~` 59 | - `sudo apt install git` 60 | - `git clone https://github.com/EOSIO/eos --recursive` 61 | - `cd eos` 62 | - `./eosio_build.sh` 63 | * If prompted to install dependencies type 1 and click enter to install them. 64 | - `cd build` 65 | - `sudo make install` 66 | 67 | ## 8) Testing EOSIO setup 68 | - Create a new empty document on your desktop to save some information for later ( I'll refer to it as "eostext" from now on ) 69 | - Open a Terminal window ( ctrl+alt+t ) 70 | - nodeos 71 | - Press "ctrl+c" to stop nodeos now that it has created a config.ini file. 72 | - Let's find the genesis file using `sudo find / -name genesis.json` 73 | - It should come up with a few options, save the one that has "/eosio/nodeos/config" in it to your eostext file, for instance "/home/nsjames/.local/share/eosio/nodeos/config/genesis.json" 74 | - `sudo nano ~/.local/share/eosio/nodeos/config/config.ini` 75 | - Towards the bottom of the config file there should be a private key starting with "5", save it in your eostext file. 76 | - Edit the config.ini file, adding/updating the following settings to the defaults already in place: 77 | - You can press "ctrl+w" to find things inside of nano. 78 | ``` 79 | # Find and change ( This will be the IP you saved before, something like 192.168.56.101 ) 80 | http-server-address = 192.168.56.101:8888 81 | 82 | # Fill this with the genesis path you save in your eostext file before. 83 | genesis-json = /path/to/eos/source/genesis.json 84 | 85 | # Find and switch to true 86 | enable-stale-production = true 87 | 88 | # Find, remove "#", and change to "eosio" 89 | producer-name = eosio 90 | 91 | # Copy the following plugins to the bottom of the file 92 | plugin = eosio::producer_plugin 93 | plugin = eosio::wallet_api_plugin 94 | plugin = eosio::chain_api_plugin 95 | plugin = eosio::http_plugin 96 | plugin = eosio::account_history_api_plugin 97 | ``` 98 | 99 | - Press "ctrl+x", then "y" and press enter to save the config file. 100 | - Open a new Terminal Window ( ctrl+alt+t ) 101 | - Run "nodeos" and let is keep running in it's own window. 102 | 103 | - Setup an alias for cleos now that we're using a different IP 104 | - `sudo nano ~/.bash_aliases` 105 | - Add the following line to the end of that file ( replace IPs with the one you entered into the config.ini ): 106 | `alias cleos='cleos -H 192.168.56.101 -p 8888 --wallet-host 192.168.56.101 --wallet-port 8888'` 107 | - `source ~/.bashrc` 108 | 109 | 110 | ## 9) Creating a wallet 111 | - `cleos wallet create` 112 | - This will create a wallet and return a password for you. Save it in your eostext file. 113 | - Create two sets of keypairs using `cleos create key`, Save each keypair in your eostext file and label the first "Owner Key" and the second "Active Key" 114 | - Import the private keys you just created. The private key from the config.ini should already be inside your wallet 115 | - `cleos wallet import ` 116 | - `cleos wallet keys` 117 | - Make sure there are 3 private keys in your wallet, if it has 2 import the key from the config.ini 118 | 119 | ## 10) Creating an account 120 | - `cd ~/eos/contracts/eosio.bios/` 121 | - `eosiocpp -o eosio.bios.wast eosio.bios.cpp` 122 | - `cleos set contract eosio ../eosio.bios -p eosio` 123 | - `cleos create account eosio currency ` 124 | - `cleos get account currency` 125 | 126 | ## 11) Uploading and testing your first contract 127 | - `cd ~/eos/contracts/currency` 128 | - `eosiocpp -o currency.wast currency.cpp` 129 | - `cleos set contract currency ../currency -p currency` 130 | - `cleos push action currency create '{"issuer":"currency","maximum_supply":"1000000.0000 CUR","can_freeze":"0","can_recall":"0","can_whitelist":"0"}' -p currency` 131 | - `cleos push action currency issue '{"to":"currency","quantity":"1000.0000 CUR","memo":""}' -p currency` 132 | - `cleos get table currency currency accounts` 133 | - `cleos push action currency transfer '{"from":"currency","to":"eosio","quantity":"20.0000 CUR","memo":"my first transfer"}' -p currency` 134 | - `cleos get table currency eosio accounts` 135 | - `cleos get table currency currency accounts` 136 | --------------------------------------------------------------------------------