Using Truffle Boxes with RSK
What we will do in this workshop:
- Pre-requisites
- Init
- Smart contract
- Truffle config
- Set up RSKj
- Configure RSK networks
- Deployment to Regtest
- Testing
- Deployment to Testnet
Part 0 - Set up pre-requisites
You will need the following software installed on your computer in order to work through this tutorial.
POSIX compliant shell
- Mac OSX and Linux distributions: Use the standard terminal
- Windows:
If you use the standard
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
- The most fuss-free way to install and manage
multiple versions of
node
on your computer is nvm. - This tutorial assumes that you have version 12 or later
nvm install 12
nvm use 12
Java
- You will need Java 8 in order to run RSKj
- If
java -version
displays an error, or displays a version other than1.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
- This is a system command that is likely already installed on your system
- If
curl --version
displays an error, downloadcurl
.
Code editor
- Software that is able to edit text files
- Preferably one that has support for syntax highlighting for both SOlidity and Javascript
- VS Code is a good choice if you don't already have one
Part 1 - Init
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
Part 2 - Smart contract
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
Part 3 - Truffle config
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
Set up RSKj
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/IRIS-3.1.0/rskj-core-3.1.0-IRIS-all.jar \
> ./rskj-core-3.1.0-IRIS-all.jar
sha256sum rskj-core-3.1.0-IRIS-all.jar
# 43149abce0a737341a0b063f2016a1e73dae19b8af8f2e54657326ac8eedc8a0 rskj-core-3.1.0-IRIS-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-3.1.0-IRIS-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.
Part 4 - Configure RSK networks
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/ \
-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:
- Use a hierarchically deterministic wallet with a BIP39 mnemonic phrase
- Use the updated gas price
- Configure it to connect to the RSK Regtest
- Configure it to connect to the RSK Testnet
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/',
),
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
Part 5 - Deployment to Regtest
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
Part 6 - Testing
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
Part 7 - Deployment to Testnet
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 tRBTC
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 tRBTC
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
Where to go from here
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:
- You now have a copy of RSKj running locally, you can try interacting with it using geth. We have a tutorial for that.
- In this workshop we focused on the bigger picture, and did not go into a lot of details about the Solidity language for smart contracts. We did not get into much detail about testing smart contracts either. Check out our webinars for more workshops like this one that we have planned for you.
- Take a look at the
src
folder that was generated duringtruffle 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