Introduction
This document shows you how to develop and test your smart contracts locally against Ganache and Loom.
Develop Locally Against Ganache
Install Truffle
First, let's make sure that our environment is properly set up before we get the ball rolling. Start by installing Truffle and making it available globally:
npm install truffle -g
Installing Truffle takes a bit of time. Once the process is finished, you should see something similar to the following:
+ [email protected]
added 91 packages from 305 contributors in 22.95s
Initialize our Project
Now that we've installed Truffle, it is time to initialize our new project. Fire up a new terminal window, create a directory called develop-locally
, and cd
into it.
Next, you can initialize the project by executing the following command:
truffle init
You should see something like the following printed out to the console:
✔ Preparing to download
✔ Downloading
✔ Cleaning up temporary files
✔ Setting up box
Unbox successful. Sweet!
Commands:
Compile: truffle compile
Migrate: truffle migrate
Test contracts: truffle test
Note that truffle init
creates a bunch of folders and config files:
├── contracts
├── Migrations.sol
├── migrations
├── 1_initial_migration.js
└── test
truffle-config.js
truffle.js
SimpleStore
Smart Contract
Compile the For the scope of this tutorial, we're going to use the famous SimpleStore.sol
which has:
a
set
function that takes a parameter called_value
and changes the state by saving it to the blockchain. Then, it fires an event calledNewValueSet
.a
get
function that lets you read the valuevariable
.
Create a new file in the contracts
folder called SimpleStore.sol
and copy the following snippet into it:
pragma solidity ^0.5.0;
contract SimpleStore {
uint value;
event NewValueSet(uint _value);
function set(uint _value) public {
value = _value;
emit NewValueSet(value);
}
function get() public view returns (uint) {
return value;
}
}
Everything is set up properly. It is time to compile our smart contract:
truffle compile
This will print out the following to the console:
Compiling your contracts...
===========================
> Compiling ./contracts/Migrations.sol
> Compiling ./contracts/SimpleStore.sol
> Artifacts written to /Users/andrei/Documents/develop-locally/build/contracts
> Compiled successfully using:
- solc: 0.5.0+commit.1d4f565a.Emscripten.clang
Create a Migration
To deploy to Ganache we will have to create something called a migration.
Migrations are JavaScript files that help Truffle deploy the code to the blockchain. Note that truffle init
created a special contract called Migrations.sol
. Its role is to keep track of the changes you're making to your code. If you've ever used Rails, the concept should be familiar to you.
We'll start from the file truffle init
already created for us- ./contracts/1_initial_migration.js
. Let's take a look at what's inside:
const Migrations = artifacts.require("Migrations");
module.exports = function(deployer) {
deployer.deploy(Migrations);
};
To get ready for deployment, do:
cp ./contracts/1_initial_migration.js ./contracts/2_simple_store.js
Next, let's modify ./contracts/2_simple_store.js
to this:
var SimpleStore = artifacts.require("./SimpleStore.sol");
module.exports = function(deployer) {
deployer.deploy(SimpleStore);
};
Update truffle-config-js
Next, open the truffle-config.js
file in your favorite text editor and modify the development
network to this:
development: {
host: "127.0.0.1",
port: 7545,
network_id: "*",
},
Install Ganache
Before migrating the SimpleStore
smart contract, you need to spin up a local blockchain instance. You can do this using a tool called Ganache.
Even if a cli version of Ganache is bundled with Truffle, we encourage you to check the link above and download the GUI version.
So, download Ganache from the link above, and launch the application.
Migrate the SimpleStore Smart Contract
Now you’re all set to migrate the SimpleStore
smart contract without having to type too much:
truffle migrate --network development
In the output below we see Truffle checking if our contracts are compiled. Then, it deploys them to Ganache:
Compiling your contracts...
===========================
> Everything is up to date, there is nothing to compile.
Starting migrations...
======================
> Network name: 'development'
> Network id: 5777
> Block gas limit: 0x38d7ea4c68000
1_initial_migration.js
======================
Replacing 'Migrations'
----------------------
> transaction hash: 0x41a167d346e3aa3928b2973a1e32939773e6f6a51d6a2bda4130fad9e5c9dee5
> Blocks: 0 Seconds: 0
> contract address: 0xE447875DC7A195b1966ECD04F4896Da8484EC0Ce
> block number: 5
> block timestamp: 1558940207
> account: 0x360731F6e87f8F0F1CdAE33Ce6a7eC6c4eD167b5
> balance: 99.98122416
> gas used: 284908
> gas price: 20 gwei
> value sent: 0 ETH
> total cost: 0.00569816 ETH
> Saving migration to chain.
> Saving artifacts
-------------------------------------
> Total cost: 0.00569816 ETH
2_simple_store.js
=================
Deploying 'SimpleStore'
-----------------------
> transaction hash: 0xeb4ea40eb75385f3ec4d8f94221e214271a745d1c0eff93cd3f3cb4937d668e2
> Blocks: 0 Seconds: 0
> contract address: 0x096F0fF4506776785C539AE7Af8bBA0BbBc3Ea1a
> block number: 7
> block timestamp: 1558940207
> account: 0x360731F6e87f8F0F1CdAE33Ce6a7eC6c4eD167b5
> balance: 99.97776358
> gas used: 130995
> gas price: 20 gwei
> value sent: 0 ETH
> total cost: 0.0026199 ETH
> Saving migration to chain.
> Saving artifacts
-------------------------------------
> Total cost: 0.0026199 ETH
Summary
=======
> Total deployments: 2
> Final cost: 0.00831806 ETH
Now let's check the Transactions
tab in Ganache. You should see something like the following:
Test Against Ganache
The test
folder is where we are going to be putting our tests. Note that Truffle provides support for tests written in JavaScript and Solidity but, for the scope of this tutorial, we are going to keep things simple and stick to JavaScript.
Let’s begin by creating a new file. In the terminal, run:
touch test/SimpleStore.js
Next, add the following conent to the test/SimpleStore.js
file:
const BN = require('bn.js')
const SimpleStore = artifacts.require('SimpleStore')
contract('Test', (accounts) => {
let [alice] = accounts
let contractInstance
beforeEach(async () => {
contractInstance = await SimpleStore.new()
});
it('should be able to set a value', async () => {
let value = 4
const result = await contractInstance.set(value, {from: alice})
assert.equal(result.receipt.status, true)
assert.equal(result.logs[0].args._value.toString(), value.toString())
})
it('should be able to set and then get a value', async () => {
let value = 5
await contractInstance.set(value, {from: alice})
const result = await contractInstance.get({from: alice})
assert.equal(result.toString(), value.toString())
})
})
To test our smart contract, use the following command:
truffle test
The output should look similar to this:
Using network 'development'.
Compiling your contracts...
===========================
> Everything is up to date, there is nothing to compile.
Contract: Test
✓ should be able to set a value (154ms)
✓ should be able to set and then get a value (154ms)
2 passing (636ms)
And there you go! That's the final step of this section.
Next, we’re going to show you how to deploy and test the SimpleStore
smart contract against Loom.
Develop Locally Against Loom
To develop locally against Loom, the first step is to download the Loom binary.
Download Loom
Use the following commands to download Loom:
mkdir loom &&
cd loom/ &&
curl https://raw.githubusercontent.com/loomnetwork/loom-sdk-documentation/master/scripts/get_loom.sh | sh &&
cd ..
You should see something similar to this printed out to the console:
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 615 100 615 0 0 1924 0 --:--:-- --:--:-- --:--:-- 1927
Downloading loom executable...
2.0.0+b789
All done.
Create a Private Key
Next, we would want to create a new private key:
loom genkey -a loom_public_key -k loom_private_key
local address: 0x42F401139048AB106c9e25DCae0Cf4b1Df985c39
local address base64: QvQBE5BIqxBsniXcrgz0sd+YXDk=
Start a Local Loom Chain
At this point, we would want to initialize and then start a local Loom chain.
In a new terminal, run the following:
cd loom && ./loom init
Now, let's spin up Loom by typing:
./loom run
Install loom-truffle-provider
In order to make Web3.js calls compatible with Loom DAppChain, we must use loom-truffle-provider
. Let's install it:
npm install loom-truffle-provider
Update truffle-config.js
Now that loom-truffle-provider
is installed, the next thing we are required to do is to initialize it as follows.
const LoomTruffleProvider = require('loom-truffle-provider');
Then, we would want to tell Truffle know how to deploy to Loom. To do so, we're going to create a new network called local_loom
and a provider
function that will overwrite Truffle's default provider:
local_loom: {
provider: function() {
const privateKey = readFileSync(path.join(__dirname, 'loom_private_key'), 'utf-8')
const chainId = 'default'
const writeUrl = 'http://127.0.0.1:46658/rpc'
const readUrl = 'http://127.0.0.1:46658/query'
const loomTruffleProvider = new LoomTruffleProvider(chainId, writeUrl, readUrl, privateKey)
loomTruffleProvider.createExtraAccountsFromMnemonic("gravity top burden flip student usage spell purchase hundred improve check genre", 10)
return loomTruffleProvider
},
network_id: '*'
}
At this point, you truffle-config.js
file should look like this:
const { readFileSync } = require('fs')
const path = require('path')
const { join } = require('path')
const LoomTruffleProvider = require('loom-truffle-provider')
module.exports = {
networks: {
development: {
host: "127.0.0.1",
port: 7545,
network_id: "*",
},
local_loom: {
provider: function() {
const privateKey = readFileSync(path.join(__dirname, 'loom_private_key'), 'utf-8')
const chainId = 'default'
const writeUrl = 'http://127.0.0.1:46658/rpc'
const readUrl = 'http://127.0.0.1:46658/query'
const loomTruffleProvider = new LoomTruffleProvider(chainId, writeUrl, readUrl, privateKey)
loomTruffleProvider.createExtraAccountsFromMnemonic("gravity top burden flip student usage spell purchase hundred improve check genre", 10)
return loomTruffleProvider
},
network_id: '*'
}
}
}
Deploy to Loom
Now we have everything in place to deploy to Loom with just one command:
truffle migrate --network local_loom
Note that we're using the --network
flag to tell Truffle that we want it to deploy to Loom.
It will take just a few seconds to deploy and you'll see something like this:
Compiling your contracts...
===========================
> Everything is up to date, there is nothing to compile.
Starting migrations...
======================
> Network name: 'local_loom'
> Network id: 13654820909954
> Block gas limit: 0x0
1_initial_migration.js
======================
Deploying 'Migrations'
----------------------
> transaction hash: 0x61d8857997b8cc4c0ffee17a61699d56f2daf7942a04ae9af0e61858f82b7b11
> Blocks: 0 Seconds: 0
> contract address: 0x90703c72B51BE5bdf5173bB31Ac083086e5C8081
> block number: 12
> block timestamp: 1559224998
> account: 0x4a8A206E9521745110421324C620D0CaEB6d5c61
> balance: 0
> gas used: 0
> gas price: 0 gwei
> value sent: 0 ETH
> total cost: 0 ETH
> Saving migration to chain.
> Saving artifacts
-------------------------------------
> Total cost: 0 ETH
2_simple_store.js
=================
Deploying 'SimpleStore'
-----------------------
> transaction hash: 0x6ca7ee75073a87042fa7b33be3404ded61fe2bd01145cbeb6397d823a18fae83
> Blocks: 0 Seconds: 0
> contract address: 0x943023e5e7E38D92069e04320BFBE27220786B1b
> block number: 18
> block timestamp: 1559225004
> account: 0x4a8A206E9521745110421324C620D0CaEB6d5c61
> balance: 0
> gas used: 0
> gas price: 0 gwei
> value sent: 0 ETH
> total cost: 0 ETH
> Saving migration to chain.
> Saving artifacts
-------------------------------------
> Total cost: 0 ETH
Summary
=======
> Total deployments: 2
> Final cost: 0 ETH
Test Against Loom
So we've successfully deployed the SimpleStore
smart contract to Loom. Now, all that's left to do is to test it.
And the beauty of it is that we're only required to run truffle test
with the --network
flag as follows:
truffle test --network local_loom
You should see something similar to the following:
Using network 'local_loom'.
Compiling your contracts...
===========================
> Everything is up to date, there is nothing to compile.
Contract: Test
✓ should be able to set a value (3023ms)
✓ should be able to set and then get a value (3025ms)
2 passing (14s)
Great! Now that you've learned how to develop and test locally, you might want to further your knowledge and learn how to join the Extdev Testnet. If so, head over to this guide and follow the instructions.