Using Truffle to Deploy to Moonbeam¶
Introduction¶
This guide walks through the process of deploying a Solidity-based smart contract to a Moonbeam node using Truffle, a commonly used development tool for smart contracts on Ethereum. Given Moonbeam’s Ethereum compatibility features, Truffle can be used directly with any of the Moonbeam networks.
To ease the process of getting started with Truffle, you can use the Moonbeam Truffle box. This provides a boilerplate setup to speed up the process to deploy contracts on Moonbeam. The Moonbeam Truffle box comes with the Moonbeam Truffle plugin, which enables you to get started with a Moonbeam development node quickly.
This guide will show you how to deploy a contract and interact with it using the Moonbeam Truffle box and the Moonbeam Truffle plugin on a locally running development node. You can adapt the instructions in this guide for Moonbeam, Moonriver, or the Moonbase Alpha TestNet.
Checking Prerequisites¶
As this guide will use the Moonbeam Truffle box and the Moonbeam Truffle plugin, you don't have to worry about creating an account and funding it. The Moonbeam development node comes with 10 pre-funded accounts. However, if you're adapting this guide for Moonbeam, Moonriver, or Moonbase Alpha you will need to have an account with funds. You can get DEV tokens for testing on Moonbase Alpha once every 24 hours from the Moonbase Alpha Faucet.
To test out the examples in this guide on Moonbeam or Moonriver, you will need to have your own endpoint and API key, which you can get from one of the supported Endpoint Providers.
To use the Moonbeam Truffle plugin, you will need to have Docker installed.
For the following examples, you don't need to have Truffle globally installed, as it is included as a dependency in the Moonbeam Truffle box. However, if you want to use the truffle
commands directly instead of running npx truffle
or ./node_modules/.bin/truffle
, you can globally install it by running:
npm install -g truffle
Creating a Project using the Moonbeam Truffle Box¶
To get started with the Moonbeam Truffle box, you can take the following steps:
-
If you have Truffle installed globally, you can execute:
mkdir moonbeam-truffle-box && cd moonbeam-truffle-box truffle unbox PureStake/moonbeam-truffle-box
Otherwise, you can directly clone the following repository:
git clone https://github.com/PureStake/moonbeam-truffle-box cd moonbeam-truffle-box
-
With the files in your local system, you can install all dependencies by running:
npm install
If you look inside of the moonbeam-truffle-box
directory, you'll find the following notable directories and files:
contracts
- a directory that is meant to store any Solidity contracts you create including the following ones that come in the Moonbeam Truffle box:Migrations.sol
- required contract to use Truffle's migration featureMyToken.sol
- example contract
migrations
- contains the JavaScript files that help you deploy contracts to the network. It comes with the following scripts:1_initial_migration.js
- script that deploys theMigrations.sol
contract. Since this contract would need to be deployed first to use migrations, it begins with1
and from there you can create new migrations with increasing numbered prefixes2_deploy_contracts.js
- script that deploys the exampleMyToken.sol
contract
truffle-config.js
- the configuration file for your project where you can define the networks your project can be deployed to, the compiler to use when compiling your contracts, and more
Using the Moonbeam Truffle Plugin to Run a Node¶
Now that you have created a simple Truffle project, you can spin up a local Moonbeam development node to deploy the contract to. The Moonbeam Truffle plugin provides a way to get started with a development node quickly by using Docker under the hood.
To start a Moonbeam development node in your local environment, you need to:
-
Download the corresponding Docker image:
truffle run moonbeam install
-
Once downloaded, you can proceed to start the local node with the following command:
truffle run moonbeam start
You will see a message indicating that the node has started, followed by both of the endpoinds available
Once you are finished using your Moonbeam development node, you can run the following lines to stop it and remove the Docker image if that is the case:
truffle run moonbeam stop && \
truffle run moonbeam remove
You also have the option to pause and unpause your Moonbeam development node:
truffle run moonbeam pause
truffle run moonbeam unpause
You can see the output of these commands in the following image:
Note
If you are familiar with Docker, you can skip the plugin commands and interact with the Docker image directly.
The Truffle Configuration File¶
The Truffle configuration file already includes everything you need to get started and deploy a contract to your local Moonbeam development node. Open the truffle-config.js
file and review the following details:
- The
HDWalletProvider
package from Truffle has been imported and is used as the hierarchical deterministic wallet - The
privateKeyDev
variable corresponds to the private key of one of your development accounts, which should hold some development funds. Your development node comes with 10 pre-funded accounts - Under the
networks
object, you'll see thedev
network configuration which is configured to use the port your local development node is running on along with the private key of your development account. Both of which are needed to deploy a contract to your local development node -
Under
compilers
, the solc version listed should be set to support the version of any contracts you wish to deploy. For this example it's set to support version0.7.0
and upNote
With the release of Solidity v0.8.20, support for the Shanghai hard fork has been introduced, which includes
PUSH0
opcodes in the generated bytecode. Support for thePUSH0
opcode on Moonbeam hasn't been rolled out yet. As such, if you'd like to use Solidity v0.8.20, you'll need to update thecompilers
config to use the London compiler:compilers: { solc: { version: '^0.8.0', settings: { evmVersion: 'london', }, }, },
If you attempt to use the default compiler of Solidity v0.8.20, you will see the following error:
"Migrations" -- evm error: InvalidCode(Opcode(95)).
After you edit your config, you may need to force a compile by running:
truffle compile --all
-
Under the
plugins
object, you'll see themoonbeam-truffle-plugin
which enables you to quickly spin up a local Moonbeam development node. You'll also find thetruffle-plugin-verify
plugin which automates the contract verification process for you. Please check out the Verify Smart Contracts with Etherscan Plugins for more information on how to use the plugin
// 1. Import HDWalletProvider
const HDWalletProvider = require('@truffle/hdwallet-provider');
// 2. Moonbeam development node private key
const privateKeyDev =
'99B3C12287537E38C90A9219D4CB074A89A16E9CDB20BF85728EBD97C343E342';
module.exports = {
networks: {
// 3. Configure networks
dev: {
provider: () => {
...
return new HDWalletProvider(privateKeyDev, 'http://127.0.0.1:9944')
},
network_id: 1281, // 0x501 in hex,
},
},
// 4. Configure compiler & version
compilers: {
solc: {
version: '^0.7.0',
},
},
// 5. Plugin configuration
plugins: ['moonbeam-truffle-plugin', 'truffle-plugin-verify'],
};
Note
For the purpose of this guide, some of the configuration file was removed from the above example.
If you're adapting this guide for Moonbeam, Moonriver, or Moonbase Alpha, you will need to update the configuration file with the appropriate network. To configure your project for Moonbeam or Moonriver, you will need to have your own endpoint and API key which you can get from one of the supported Endpoint Providers. You'll also need to update the private key to one that has funds on that network:
moonbeam: {
provider: () => {
...
return new HDWalletProvider(
'PRIVATE_KEY_HERE', // Insert your private key here
'INSERT_RPC_API_ENDPOINT' // Insert your RPC URL here
)
},
network_id: 1284 // (hex: 0x504),
},
moonriver: {
provider: () => {
...
return new HDWalletProvider(
'PRIVATE_KEY_HERE', // Insert your private key here
'INSERT_RPC_API_ENDPOINT' // Insert your RPC URL here
)
},
network_id: 1285 // (hex: 0x505),
},
moonbase: {
provider: () => {
...
return new HDWalletProvider(
'PRIVATE_KEY_HERE', // Insert your private key here
'https://rpc.api.moonbase.moonbeam.network' // Insert your RPC URL here
)
},
network_id: 1287 // (hex: 0x507),
},
The Contract File¶
Under the contracts
directory, you'll find an ERC-20 token contract, called MyToken
, that mints a given amount of tokens to the contract owner:
pragma solidity ^0.7.5;
// Import OpenZeppelin Contract
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
// This ERC-20 contract mints the specified amount of tokens to the contract creator.
contract MyToken is ERC20 {
constructor(uint256 initialSupply) ERC20("MyToken", "MYTOK") {
_mint(msg.sender, initialSupply);
}
}
This is a simple ERC-20 contract based on the OpenZepplin ERC-20 contract template. It creates MyToken
which has MYTOK
as the symbol and the standard 18 decimal places. Furthermore, it assigns the created initial token supply to the contract creator.
The Migration Script¶
Truffle uses a concept called migrations. Migrations are JavaScript files that help you deploy contracts to the network.
If you take a look at the migration scripts in the migrations
directory, you'll see there are two files. As previously mentioned, the 1_initial_migration.js
script needs to be deployed first and is required to enable Truffle's migration feature. If you take a look at the migration script under migrations/2_deploy_contracts.js
, it contains the following:
var MyToken = artifacts.require('MyToken');
module.exports = function (deployer) {
deployer.deploy(MyToken, '8000000000000000000000000');
};
This script imports the MyToken
contract artifact which is created when you compile the contract. It is then used to deploy the contract with any initial constructor values.
For this example, 8000000000000000000000000
is the number of tokens to initially mint with the contract, i.e., 8 million with 18 decimal places.
Deploying a Contract to Moonbeam Using Truffle¶
Before you can deploy your contracts, you must compile them. As a reminder, you will be deploying the Migrations.sol
contract first using the migrations/1_initial_migration.js
script. This will enable you to use Truffle's migration feature. You can take the following steps to compile and deploy your contract:
-
Compile the contracts:
truffle compile
If successful, you should see output like the following:
-
Deploy the compiled contracts:
truffle migrate --network moonbeam
truffle migrate --network moonriver
truffle migrate --network moonbase
truffle migrate --network dev
If successful, you will see deployment actions, including the address of the deployed contract:
Forking with Ganache¶
Ganache is part of the Truffle suite of development tools and is your own personal blockchain for local development and testing. You can use Ganache to fork Moonbeam, which will simulate the live network locally, enabling you to interact with already deployed contracts on Moonbeam in a local test environment.
There are some limitations to be aware of when forking with Ganache. Since Ganache is based on an EVM implementation, you cannot interact with any of the Moonbeam precompiled contracts and their functions. Precompiles are a part of the Substrate implementation and therefore cannot be replicated in the simulated EVM environment. This prohibits you from interacting with cross-chain assets on Moonbeam and Substrate-based functionality such as staking and governance.
From your Truffle project, you can install Ganache CLI by running:
npm install ganache
Then you can add a script to run Ganache forking in your package.json
file:
...
"scripts": {
"ganache": "ganache --fork.url INSERT_RPC_API_ENDPOINT"
},
...
...
"scripts": {
"ganache": "ganache --fork.url INSERT_RPC_API_ENDPOINT"
},
...
...
"scripts": {
"ganache": "ganache --fork.url https://rpc.api.moonbase.moonbeam.network"
},
...
When you spin up the forked instance, you'll have 10 development accounts that are pre-funded with 1,000 test tokens. The forked instance is available at http://127.0.0.1:8545/
. The output in your terminal should resemble the following:
To verify you have forked the network, you can query the latest block number:
curl --data '{"method":"eth_blockNumber","params":[],"id":1,"jsonrpc":"2.0"}' -H "Content-Type: application/json" -X POST localhost:8545
If you convert the result
from hex to decimal, you should get the latest block number from the time you forked the network. You can cross reference the block number using a block explorer.
From here you can deploy new contracts to your forked instance of Moonbeam or interact with contracts already deployed by creating a local instance of the deployed contract.
To interact with an already deployed contract, you can create a new script in the scripts
directory using Ethers.js or Web3.js. First, you'll need to install the JavaScript library of your choice:
npm install ethers
npm install web3
Then you can create a new script to access a live contract on the network:
const ethers = require('ethers');
async function main() {
const provider = new ethers.JsonRpcProvider('http://127.0.0.1:8545/');
const contract = new ethers.Contract(
'INSERT_CONTRACT_ADDRESS', 'INSERT_CONTRACT_ABI', provider
);
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
const Web3 = require('web3');
async function main() {
const web3 = new Web3('http://127.0.0.1:8545/');
const contract = new web3.eth.Contract('INSERT_CONTRACT_ADDRESS', 'INSERT_CONTRACT_ABI');
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
To run the script, you can use the following command:
truffle exec INSERT_PATH_TO_FILE
| Created: March 1, 2022