Using Foundry to Deploy To Moonbeam¶
Introduction¶
Foundry is an Ethereum development environment written in Rust that helps developers manage dependencies, compile projects, run tests, deploy contracts, and interact with blockchains from the command line. Foundry can directly interact with Moonbeam's Ethereum API so it can be used to deploy smart contracts into Moonbeam.
There are three tools that make up Foundry:
- Forge - compiles, tests, and deploys contracts
- Cast - a command line interface for interacting with contracts
- Anvil - a local TestNet node for development purposes that can fork preexisting networks
This guide will cover how to use Foundry to compile, deploy, and debug Ethereum smart contracts on the Moonbase Alpha TestNet. This guide can also be adapted for Moonbeam, Moonriver, or a Moonbeam development node.
Checking Prerequisites¶
To get started, you will need the following:
- 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.
- Have Foundry installed
Creating a Foundry Project¶
You will need to create a Foundry project if you don't already have one. You can create one by completing the following steps:
-
Install Foundry if you haven't already. If on Linux or MacOS, you can run these commands:
curl -L https://foundry.paradigm.xyz | bash foundryup
If on Windows, you'll have to install Rust & then build Foundry from source:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs/ | sh cargo install --git https://github.com/foundry-rs/foundry foundry-cli anvil --bins --locked
-
Create the project, which will create a folder with three folders within it:
forge init foundry
With the default project created, you should see three folders.
lib
- all of the project's dependencies in the form of git submodulessrc
- where to put your smart contracts (with functionality)test
- where to put the forge tests for your project, which are written in Solidity
In addition to these three folders, a git project will also be created along with a prewritten .gitignore
file with relevant file types and folders ignored.
The Source Folder¶
The src
folder may already contain Contract.sol
, a minimal Solidity contract. Feel free to delete it. Instead, you will be deploying an ERC-20 contract. In the contracts directory, you can create the MyToken.sol
file:
cd src
touch MyToken.sol
Open the file and add the following contract to it:
pragma solidity ^0.8.0;
// Import OpenZeppelin Contract
import "openzeppelin-contracts/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);
}
}
Before you attempt to compile, install OpenZeppelin contracts as a dependency. You may have to commit previous changes to git beforehand. By default, Foundry uses git submodules instead of npm packages, so the traditional npm import path and command are not used. Instead, use the name of OpenZeppelin's Github repository:
forge install OpenZeppelin/openzeppelin-contracts
Compiling Solidity¶
Once all dependencies have been installed, you can compile the contract:
forge build
After compilation, two folders will be created: out
and cache
. The ABI and bytecode for your contracts will be contained within the out
folder. These two folders are already ignored by the .gitignore
included in the default Foundry project initialization.
Deploying the Contract¶
Deploying the contract with Forge takes a single command, but you will need to include an RPC endpoint, a funded private key, and constructor arguments. MyToken.sol
asks for an initial supply of tokens in its constructor, so each of the following commands include 100 as a constructor argument. You can deploy the MyToken.sol
contract using the command for the correct network:
forge create --rpc-url RPC-API-ENDPOINT-HERE \
--constructor-args 100 \
--private-key YOUR_PRIVATE_KEY \
src/MyToken.sol:MyToken
forge create --rpc-url RPC-API-ENDPOINT-HERE \
--constructor-args 100 \
--private-key YOUR_PRIVATE_KEY \
src/MyToken.sol:MyToken
forge create --rpc-url https://rpc.api.moonbase.moonbeam.network \
--constructor-args 100 \
--private-key YOUR_PRIVATE_KEY \
src/MyToken.sol:MyToken
forge create --rpc-url http://127.0.0.1:9933 \
--constructor-args 100 \
--private-key YOUR_PRIVATE_KEY \
src/MyToken.sol:MyToken
After a few seconds, the contract is deployed, and you should see the address in the terminal.
Congratulations, your contract is live! Save the address, as you will use it to interact with this contract instance in the next step.
Interacting with the Contract¶
Foundry includes cast, a CLI for performing Ethereum RPC calls.
Try to retreive your token's name using cast, where YOUR_CONTRACT_ADDRESS
is the address of the contract that you deployed in the previous section:
cast call YOUR_CONTRACT_ADDRESS "name()" --rpc-url RPC-API-ENDPOINT-HERE
cast call YOUR_CONTRACT_ADDRESS "name()" --rpc-url RPC-API-ENDPOINT-HERE
cast call YOUR_CONTRACT_ADDRESS "name()" --rpc-url https://rpc.api.moonbase.moonbeam.network
cast call YOUR_CONTRACT_ADDRESS "name()" --rpc-url http://127.0.0.1:9933
You should get this data in hexidecimal format:
0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000074d79546f6b656e00000000000000000000000000000000000000000000000000
This is far from readable, but you can use cast to convert it into your desired format. In this case, the data is text, so you can convert it into ascii characters to see "My Token":
cast --to-ascii 0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000074d79546f6b656e00000000000000000000000000000000000000000000000000
You can also mutate data with cast as well. Try burning tokens by sending them to the zero address.
cast send --private-key YOUR_PRIVATE_KEY \
--rpc-url RPC-API-ENDPOINT-HERE \
--chain 1284 \
YOUR_CONTRACT_ADDRESS \
"transfer(address,uint256)" 0x0000000000000000000000000000000000000001 1
cast send --private-key YOUR_PRIVATE_KEY \
--rpc-url RPC-API-ENDPOINT-HERE \
--chain 1285 \
YOUR_CONTRACT_ADDRESS \
"transfer(address,uint256)" 0x0000000000000000000000000000000000000001 1
cast send --private-key YOUR_PRIVATE_KEY \
--rpc-url https://rpc.api.moonbase.moonbeam.network \
--chain 1287 \
YOUR_CONTRACT_ADDRESS \
"transfer(address,uint256)" 0x0000000000000000000000000000000000000001 1
cast send --private-key YOUR_PRIVATE_KEY \
--rpc-url http://127.0.0.1:9933 \
--chain 1281 \
YOUR_CONTRACT_ADDRESS \
"transfer(address,uint256)" 0x0000000000000000000000000000000000000001 1
The transaction will be signed by your Moonbase account and be broadcasted to the network. The output should look similar to:
Congratulations, you have successfully deployed and interacted with a contract using Foundry!
Forking with Anvil¶
As previously mentioned, Anvil is a local TestNet node for development purposes that can fork preexisting networks. Forking Moonbeam allows you to interact with live contracts deployed on the network.
There are some limitations to be aware of when forking with Anvil. Since Anvil 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.
To fork 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 fork Moonbeam from the command line, you can run the following command from within your Foundry project directory:
anvil --fork-url RPC-API-ENDPOINT-HERE
anvil --fork-url RPC-API-ENDPOINT-HERE
anvil --fork-url https://rpc.api.moonbase.moonbeam.network
Your forked instance will have 10 development accounts that are pre-funded with 10,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. Building off of the previous example in this guide, you can make a call using Cast to check the balance of the minted MYTOK tokens in the account you deployed the contract with:
cast call INSERT-CONTRACT-ADDRESS "balanceOf(address)(uint256)" INSERT-YOUR-ADDRESS --rpc-url http://localhost:8545