What we will do in this workshop:
You will need the following software installed on your computer in order to work through this tutorial.
POSIX compliant shell
cmd
terminal, or PowerShell,
the commands here may not work.
Consider installing
Git for Windows,
which comes with Git Bash bundled.
Here’s a great
tutorial on installing and using Git Bash.NodeJs
node
on your computer is
nvm.nvm install 12
nvm use 12
Java
java -version
displays an error,
or displays a version other than 1.8
,
you will need to install it.There are a variety of ways to do this, and SDKman is one which allows you to install and switch between multiple versions as needed:
curl -s "https://get.sdkman.io/" | bash
source "$HOME/.sdkman/bin/sdkman-init.sh"
# to get a filtered list of available java versions
sdk list java | grep "8\." # copy a selection for use below
# install the version of java copied above
# (replace accordingly)
sdk install java 8.0.242.j9-adpt
# show installed versions, and switch to the selected one
# (replace accordingly)
sdk list java | grep installed
sdk use java 8.0.242.j9-adpt
java -version
curl
curl --version
displays an error,
download curl
.Code editor
Let’s install Truffle,
which is the main development tool that we’ll be using;
as well as mnemonics
,
which is a simple utility that can be used to generate
BIP39
mnemonics.
npm i -g truffle@5.1.22 mnemonics@1.1.3
We’ll create a directory for this new project,
and then initialise a git repo in it.
In order to be able to git push
,
you will need to create a new repository,
and copy its remote URL.
Change your folder name and git remote URL as appropriate.
mkdir -p ~/code/rsk/workshop-rsk-truffle-box-bguiz-live
cd ~/code/rsk/workshop-rsk-truffle-box-bguiz-live
git init
git remote add origin git@github.com:bguiz/workshop-rsk-truffle-box-bguiz-live.git
The truffle unbox
command sets up a project
based on a known template.
In this workshop, we will be using the “pet shop” Truffle box,
which is very commonly used in demos.
truffle unbox pet-shop
git add .
git commit -m "step: 01-01: truffle unbox"
Tell git not to care about the NodeJs dependencies - we don’t want to commit those!
git init
echo "/node_modules" > .gitignore
git add .gitignore
git commit -m "step: 01-02: .gitignore"
Let’s inspect the directory structure and files
that were generated by truffle unbox
.
tree -I node_modules
Install a dependency that allows Truffle to make use of a Hierarchically Deterministic Wallet (BIP39). We will make use of this shortly.
npm i --save-exact @truffle/hdwallet-provider@1.0.34
git add -p package.json
git commit -m "step: 01-03: npm install dependencies"
git push origin master
We create a new smart contract called Adoption.sol
,
within the contracts
folder.
touch contracts/Adoption.sol
git add contracts/Adoption.sol
code contracts/Adoption.sol
This is an empty smart contract, the minimum for it to be able to compile.
pragma solidity 0.5.2;
contract Adoption {
}
We use truffle compile
to run solc
, a Solidity compiler,
on all the smart contracts within the contracts
folder.
There should be two of them: Migrations.sol
and Adoption.sol
.
Take note of the compiler version in the output.
truffle compile
git add -p
git commit -m "step: 02-01: smart contract"
We add a state variable which stores the accounts
(of type address
) for the adopters.
We also add a function that updates this state variable to remember which account adopted which pet.
Finally, we add another function that allows us to retrieve the full list of adopters.
pragma solidity 0.5.2;
contract Adoption {
address[16] public adopters;
// Adopting a pet
function adopt(uint petId) public returns (uint) {
require(petId >= 0 && petId <= 15);
adopters[petId] = msg.sender;
return petId;
}
// Retrieving the adopters
function getAdopters() public view returns (address[16] memory) {
return adopters;
}
}
git add -p
git commit -m "step: 02-02: state variables, setter, getter"
git push origin master
Open up the config file used by Truffle in your code editor. Specify the version of the solidity compiler that you wish to use.
code truffle-config.js
compilers: {
solc: {
version: '0.5.2',
// ...
},
},
git add -p
git commit -m "step: 03-01: specify compiler version"
Now run truffle compile
again,
and take note of the compiler version in the output.
Did it differ from the previous time you ran truffle compile
?
truffle compile
git add build/contracts
git commit -m "step: 03-02: compiled contracts output"
git push origin master
Get RSKj running locally, this will provide you with a localhost
-only network,
for fast testing.
For this part, open up a new shell, as you will need to leave processes running in the background while you continue with the rest of your tutorial.
Now you’re ready to download and install RSKj, which is the RSK node. This enables you to run an instance locally, connecting various RSK networks: Mainnet, Testnet, and Regtest.
cd ~/code/rsk
mkdir -p ~/code/rsk/rskj-node
cd ~/code/rsk/rskj-node
curl \
-L \
https://github.com/rsksmart/rskj/releases/download/PAPYRUS-2.0.1/rskj-core-2.0.1-PAPYRUS-all.jar \
> ./rskj-core-2.0.1-PAPYRUS-all.jar
sha256sum rskj-core-2.0.1-PAPYRUS-all.jar
# 43149abce0a737341a0b063f2016a1e73dae19b8af8f2e54657326ac8eedc8a0 rskj-core-2.0.1-PAPYRUS-all.jar
Note: When installing and running the RSKj node, it is always a good idea to verify that your copy is legitimate. Full instructions on how to do this.
For the purposes of this workshop, we will run RSKj on Regtest.
java -cp rskj-core-2.0.1-PAPYRUS-all.jar co.rsk.Start --regtest
If you see no output - that is a good thing.
Leave this running in an open shell, and switch back to your original shell for the rest of this workshop.
Now we have to fund the accounts, so that we know that they exist
Generate a 12-word BIP39 mnemonic using iancoleman.io/bip39,
and save to .secret
.
Alternatively, use mnemonics
to do the same.
mnemonics > .secret
git add .secret
git commit -m "step: 04-01: save BIP39 mnemonic"
Get the current gas price of the network, and save to .gas-price.json
.
curl \
https://public-node.testnet.rsk.co/2.0.1/ \
-X POST -H "Content-Type: application/json" \
--data '{"jsonrpc":"2.0","method":"eth_gasPrice","params":[],"id":1}' \
> .gas-price-testnet.json
curl \
http://localhost:4444/2.0.1/ \
-X POST -H "Content-Type: application/json" \
--data '{"jsonrpc":"2.0","method":"eth_gasPrice","params":[],"id":1}' \
> .gas-price-regtest.json
git add .gas-price-testnet.json .gas-price-regtest.json
git commit -m "step: 04-02: save gas price JSON_RPC responses for testnet and regtest"
Modify the Truffle config again so that we can do the following:
code truffle-config.js
This part reads in the BIP39 mnemonic phrase and the gas price.
const HDWalletProvider = require('@truffle/hdwallet-provider');
const fs = require('fs');
const gasPriceTestnetRaw = fs.readFileSync(".gas-price-testnet.json").toString().trim();
const gasPriceTestnet = parseInt(JSON.parse(gasPriceTestnetRaw).result, 16);
if (typeof gasPriceTestnet !== 'number' || isNaN(gasPriceTestnet)) {
throw new Error('unable to retrieve network gas price from .gas-price-testnet.json');
}
const gasPriceRegtestRaw = fs.readFileSync(".gas-price-regtest.json").toString().trim();
const gasPriceRegtest = parseInt(JSON.parse(gasPriceRegtestRaw).result, 16);
console.log(gasPriceRegtest);
if (typeof gasPriceRegtest !== 'number' || isNaN(gasPriceRegtest)) {
throw new Error('unable to retrieve network gas price from .gas-price-regtest.json');
}
const mnemonic = fs.readFileSync(".secret").toString().trim();
if (!mnemonic || mnemonic.split(' ').length !== 12) {
throw new Error('unable to retrieve mnemonic from .mnemonic');
}
console.log({
mnemonic,
gasPriceTestnet,
gasPriceRegtest,
});
// NOTE only do the above in demo code.
// This is not, by far, secure enough for a real use scenario.
git add -p truffle-config.js
git commit -m "step: 04-03: read gas prices and BIP39 mnemonic in from files"
This part configures a connection to the RSK Testnet. Note that we specify a gas price that is slightly higher than the minimum specified by the network. For example, the gas price at the time of creating this workshop was 60 million, and we configure a gas price of 61 million. The effect that this has is to get a slightly higher priority for our transactions being added to blocks.
networks: {
testnet: {
provider: () => new HDWalletProvider(
mnemonic,
'https://public-node.testnet.rsk.co/2.0.1/',
),
network_id: 31,
gasPrice: gasPriceTestnet + 1e6,
networkCheckTimeout: 1e9
},
// ...
},
Test the connection to RSK Testnet.
truffle console --network testnet
(await web3.eth.getBlockNumber()).toString()
(await web3.eth.net.getId()).toString()
.exit
git add -p truffle-config.js
git commit -m "step: 04-04: configure RSK Testnet connection"
This part configures a connection to the RSK Regtest.
networks: {
regtest: {
host: '127.0.0.1',
port: 4444,
network_id: 33,
gasPrice: gasPriceRegtest,
networkCheckTimeout: 1e3
},
// ...
},
Test the connection to RSK Regtest.
truffle console --network regtest
(await web3.eth.getBlockNumber()).toString()
(await web3.eth.net.getId()).toString()
.exit
git add -p truffle-config.js
git commit -m "step: 04-05: configure RSK Regtest connection"
git push origin master
Let’s create a deployment script for our adoption smart contract.
touch migrations/2_deploy_contracts.js
code migrations/2_deploy_contracts.js
const Adoption = artifacts.require("Adoption");
module.exports = function(deployer) {
deployer.deploy(Adoption);
};
git add migrations/2_deploy_contracts.js
git commit -m "step: 05-01: add deployment script for adoption contract"
Use truffle migrate
to run the deployment script.
You should notice that Truffle has updated the contents of
build/contracts
.
truffle migrate --network regtest
git add -p build
git commit -m "step: 05-02: run truffle migrate on regtest"
git push origin master
Let’s create a file that tests whether our smart contract works properly.
touch test/adoption.test.js
code test/adoption.test.js
const Adoption = artifacts.require('Adoption');
contract('Adoption', (accounts) => {
});
We run this empty test file, and should be able to see zero tests.
truffle test --network regtest
# 0 passing (1ms)
git add test/adoption.test.js
git commit -m "step: 06-01: add empty test file for adoption"
Let’s add a test for whether an account can adopt a pet.
const Adoption = artifacts.require('Adoption');
const assert = require('assert');
const BN = web3.utils.BN;
it('account can adopt pet', async () => {
const inst = await Adoption.deployed();
const adopterAccount = accounts[5];
const expectedPetId = new BN(8);
const adoptTxInfo = await inst.adopt(
expectedPetId,
{
from: adopterAccount,
},
);
assert.equal(
adoptTxInfo.receipt.status,
true,
'adoption transaction failed',
);
});
This time, when we run the tests, we should see one passing test.
truffle test --network regtest
# 1 passing (2s)
git add -p test/adoption.test.js
git commit -m "step: 06-02: test for adopt function"
Let’s add another test for that checks whether the state of the smart contract was modified correctly in the previous function invocation.
it('remembers adopter account', async () => {
inst = await Adoption.deployed();
const adopterAccount = accounts[5];
const expectedPetId = new BN(8);
const returnedAccount = await inst.adopters(
expectedPetId,
);
assert.equal(
returnedAccount,
adopterAccount,
'returned adopter account mismatch',
);
});
When you run the tests, you should get a 2 passing tests this time.
truffle test --network regtest
# 2 passing (3s)
git add -p test/adoption.test.js
git commit -m "step: 06-03: test for state change after adopt function called"
git push origin master
Thus far we have only connected to a blockchain that runs using just 1 node, that runs on your own computer. Let’s now switch to interacting with a “real” blockchain, which is running on multiple nodes distributed across multiple computers!
Start the Truffle console, and this time, interact with the RSK Testnet.
truffle console --network testnet
Wait for a second, and you should see a prompt which looks like:
truffle(testnet)>
.
Test that the connection is OK by attempting to get the block number, like so:
(await web3.eth.getBlockNumber()).toString()
'791905'
The addresses of the first 10 wallets in our
hierarchically deterministic wallet can be obtained now,
and we write them to a file named .accounts
const accounts = Object.keys(web3.currentProvider.wallets)
accounts
await require('fs').promises.writeFile('.accounts', accounts.join('\n'))
.exit
(Typing .exit
quits the Truffle console.)
Save the list of account addresses.
git add .accounts
git commit .accounts -m "step: 07-01: save list of account addresses"
Fund your first Testnet account with some tR-BTC
using the RSK Testnet faucet -
faucet.rsk.co.
Use the address which is in the first line of the .accounts
file.
You will need this in order to pay for the gas need for smart contract deployment.
head -n 1 < .accounts
Check that you have tR-BTC
truffle console --network testnet
const accounts = Object.keys(web3.currentProvider.wallets)
web3.eth.getBalance(accounts[0])
.exit
Deploy the contracts, this time to Testnet instead of Regtest.
truffle migrate --network testnet
git add -p build
git commit -m "step: 07-02: truffle migrate on testnet"
git push origin master
Congratulations on making it through till the end of this workshop!
You are now able to use Truffle like a pro: Unbox, compile, test, and migrate (to multiple networks)!
Here are a few things that you can explore next:
src
folder that was generated during truffle unbox
,
it contains a stubbed implementation of a front end to interact with
the smart contract that you just created.
We have a basic tutorial on developing a front end for a smart contract.
Experiment with this.
├── src
│ ├── css
│ │ <...>
│ ├── fonts
│ │ <...>
│ ├── images
│ │ ├── boxer.jpeg
│ │ ├── french-bulldog.jpeg
│ │ ├── golden-retriever.jpeg
│ │ └── scottish-terrier.jpeg
│ ├── index.html
│ ├── js
│ │ ├── app.js
│ │ ├── bootstrap.min.js
│ │ ├── truffle-contract.js
│ │ └── web3.min.js
│ └── pets.json
Go to top