Setting Up Truffle to Deploy Smart Contracts
- How Do Ethereum Smart Contracts Work?
- Writing Smart Contracts in Remix
- Setting Up Truffle to Deploy Smart Contracts
- Truffle Console and Truffle Tests
- Ethereum Apps with Web3.js
I do love websites that allow you to quickly put together some code and get the result back immediately. The key reason is that they avoid installing or setting up any necessary infrastructure.
JavaScript has jsfiddle. Ethereum has Remix and ethfiddle too. But although these are great to to write very rapid prototypes, they are not full-fledged development tools.
Truffle is a suite of tools that help you through the full development cycle of Ethereum smart contracts. You get the ability to compile contracts, debug and deploy them to any Ethereum network.
It also comes with two consoles that you can use to load and exercise your code. In this post I show you how you can install it and get it set up for use.
Installing Truffle
Truffle is a Node.js package, so you first need to install node.js for it to run. While you’re doing so, it is also a good idea to install npm so that you can install and update node.js packages as needed. Then, you can install Truffle with:
npm install -g truffle
Before you start a new project, you must configure its base directory, by running the following in that path:
truffle init
This downloads the truffe installation and creates a basic hierarchy in that directory, including:
- a
contracts
directory with a default contract:Migrations.sol
- a
migrations
directory with a default file:1_initial_migration.js
- a
test
directory that is initially empty - a configuration file called
truffle.js
ortruffle-config.js
If I’m on Windows, I ensure my configuration file has the longer name. If it is called truffle.js
, this conflicts with the truffle
command, which I prefer to use rather than the Windows variant truffle.cmd
.
Interaction with the Blockchain
With the basics out of the way, the next step is to access the blockchain. You have three basic options:
- the Ethereum main net
- a live Ethereum test network
- a test network simulated by your computer
These are in opposite order of when they should be used in the development process. This is because deploying contracts to a live network may cost real money, and once there the contracts cannot be changed: only new instances can be created.
The higher the network you use, in the previous list, the most definitive your code will be and the harder it will be to test. It is also typically slower. Contracts take time to deploy and depending on how many you have can take from several seconds to long minutes, so in the early stages of development you want to choose the fastest one.
That means a local test network. I use Ganache as my test network, and am not really aware of other competing products. It is part of the Truffle suite and so interacts well with the rest of this setup.
You don’t really connect to a network. There is nothing like a session-based client-server-like interaction. Instead, you send your commands and data to a blockchain node. Since these may be running in remote machines, the most standard way to do so is to send JSON-RPC messages, encoding the function you want to call in the smart contract and the parameters you want to pass.
Accessing a Blockchain Network with Truffle
Truffle lets you be as flexible as you need, allowing you to communicate with different networks for development, test and production.
The default configuration file (truffle.js
) specifies a few (commented out) networks, all commented out. This is an excerpt of the default example:
module.exports = {
networks: {
development: {
host: "127.0.0.1", // Localhost (default: none)
port: 8545, // Standard Ethereum port (default: none)
network_id: "*", // Any network (default: none)
},
// Another network with more advanced options...
advanced: {
port: 8777, // Custom port
network_id: 1342, // Custom network
gas: 8500000, // Gas sent with each transaction (default: ~6700000)
gasPrice: 20000000000, // 20 gwei (in wei) (default: 100 gwei)
from: <address>, // Account to send txs from (default: accounts[0])
websockets: true // Enable EventEmitter interface for web3 (default: false)
},
// Useful for deploying to a public network.
// NB: It's important to wrap the provider as a function.
ropsten: {
provider: () => new HDWalletProvider(mnemonic, `https://ropsten.infura.io/v3/YOUR-PROJECT-ID`),
network_id: 3, // Ropsten's id
gas: 5500000, // Ropsten has a lower block limit than mainnet
confirmations: 2, // # of confs to wait between deployments. (default: 0)
timeoutBlocks: 200, // # of blocks before a deployment times out (minimum/default: 50)
skipDryRun: true // Skip dry run before migrations? (default: false for public nets )
},
// Useful for private networks
private: {
provider: () => new HDWalletProvider(mnemonic, `https://network.io`),
network_id: 2111, // This network is yours, in the cloud.
production: true // Treats this network as if it was a public net. (default: false)
}
}
};
This specifies, for each network, a short name, and its network address, including a port. You can also specify the maximum gas you are willing to spend in any transaction, and the price in Wei for the gas, where a single Wei is one 1018
-th of 1 Ether.
Compiling Contracts
Once the setup is done, it is time to write the contract, and then deploy it. Writing a contract is not the subject for this post, so I will just give you the example I’m going to use for tests in the next posts.
pragma solidity ^0.5.0;
contract TestContract {
uint a = 10;
function testCallNoArgs() public view returns (uint) {
return a;
}
function testCallWithArgs(uint b) public view returns (uint) {
return a * b;
}
function testMethod(uint newA) public {
a = newA;
}
// This modifies state and returns a value. Does not really work as such.
function testUpdate(uint checkA) public returns (bool) {
bool result = checkA < a;
a = checkA;
return result;
}
}
The above contract has a only a few test functions, to illustrate the difference between calling a function and sending a transaction to the contract. It has two view
functions, which is a way for Solidity to know these methods return values and do not change the contract’s internal state (the variables in storage). A similar function modifier, pure
, goes even further, by promising the function also does not read from the contract’s state. I will use this example in the next post, where I will greatly explore these differences.
Emulating the blockchain with Ganache
Once you have the contract, you can first compile it with
truffle compile
and then deploy it to your chosen network by using
truffle migrate --network <network name>
This requires that your Ethereum client is actually running and listening for connections on the same port that is defined in your configuration file. I use Ganache-cli
with default values, that is, port 8545.
When I do so, Ganache prints the address where it is running:
Listening on 127.0.0.1:8545
.
Plus, it gives me 10 addresses with their private keys, and some useful information like the network gas limit and price. In my case, these are
Gas Price
==================
20000000000
Gas Limit
==================
6721975
I leave this shell running, and continue with the example in another one.
The Migration Files
If you try now to deploy your contract from the default project, you’ll see that only one contract is deployed, named Migrations
. This is specified in contracts\Migrations.sol
and is a standard contract that keeps track of what version of a contract is deployed, to save the cost of replacing unchanged contracts.
The contracts to be deployed are specified in the files in the migrations
directory, 1_initial_migration.js,
which by default has only one file that installs the Migrations
contract.
In order to deploy our new contract, we have to specify a similar file, prefixed with the number 2, or larger, so that it follows the default one. This is what should go in there:
const TestContract = artifacts.require("TestContract");
module.exports = function(deployer) {
deployer.deploy(TestContract);
};
You can add several contracts to this file. Indeed, you can have quite a complex migrations file, with custom JS code. And you can have several migration files, if your project is really complicated and you like to deploy a coherent set of contracts in each file.
Here are a few points to note:
- every migration file has a number prefix and a description. The files are run in ascending order of their prefix. This is numerical ordering, not alphabetical, so that
30
comes after3
, but4
comes before30
. - at a minimum, files have to include an exports block, that is a function executing the migration. This takes a
deployer
as argument, and uses it to deploy contracts. - you can do complex logic in a migration script, and print log messages to the screen.
Deploying Contracts
Here is a minimal example:
const TestContract = artifacts.require("TestContract"); module.exports = function(deployer) { console.log("Deploying TestContract"); deployer.deploy(TestContract); };
The key is to invoke the deployer.deploy
method for each contract that you want to install in the blockchain. You can obtain the contract’s definition from artifacts.require
in Truffle, by indicating the name of the contract defined inside the Solidity file. For more details on this, and the whole migrations process, you can read Truffle’s documentation page.
To verify what contracts you have deployed, you can type:
truffle network
which will show all the contracts deployed on each network defined in your configuration file. That gives something like this:
The following networks are configured to match any network id ('*'):
development
Closely inspect the deployed networks below, and use `truffle networks --clean` to remove any networks that don't match your configuration. You should not use the wildcard configuration ('*') for staging and production networks for which you intend to deploy your application.
Network: UNKNOWN (id: 1568100253338)
Migrations: 0x33340c772d629a7aF583b273AfF01DBBb16EEB54
TestContract: 0xa991314921007722341F4a5515476Cf2Fa24A656
Where To Go From Here
If you followed the steps up to here, you’ll be ready to deploy several contracts, as needed by your project, in a network of your choice. This covers the basic setup with Truffle, and now you can start writing your code and testing it.
In the next posts, I’ll show you how to do that in an interactive console provided by Truffle, and then in an automated way using test scripts, again supported by Truffle. The first of these provides an experience comparable to that of Remix, but in a more convenient way, with access to a full JavaScript console and not just the interface of your contract.
Let me know if this has been useful, or if you have specific questions or requests. Have fun coding, and come back for the next leg of this journey.