Using Ape to Deploy To Moonbeam¶
Introduction¶
Ape is an Ethereum development environment that helps Python developers manage and automate the recurring tasks inherent to building smart contracts and DApps. Ape can directly interact with Moonbeam's Ethereum API, so you can also use Ape to deploy smart contracts on Moonbeam.
This guide will walk you through using Ape to compile, deploy, and interact with Ethereum smart contracts on the Moonbase Alpha TestNet. You can adapt this guide for Moonbeam, Moonriver, or a Moonbeam development node.
Checking Prerequisites¶
To get started, ensure you have the following:
- MetaMask installed and connected to Moonbase Alpha
- 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
Creating an Ape Project¶
If you don't already have an Ape project, you must install Ape and create a new one. You can follow the steps below to get started and create an empty project:
-
Create a directory for your project
mkdir ape && cd ape
-
If you don't have
pipx
installed, install itpython3 -m pip install --user pipx python3 -m pipx ensurepath
-
pipx install eth-ape
-
Create a project
ape init
-
Enter a name for your project
ape init Please enter project name: ape-demo
SUCCESS: ape-demo is written in ape-config.yaml
ls ape-config.yaml contracts scripts tests
Your Ape project contains a bare-bones ape-config.yaml
file for customizing specific settings and the following empty directories:
contracts
- an empty directory for storing smart contractsscripts
- an empty directory for storing Python scripts, such as deployment scripts and scripts to interact with your deployed contractstests
- an empty directory for pytest testing scripts
Configure Accounts¶
You'll need to import an account before you can deploy smart contracts or interact with previously deployed contracts from your Ape project. You can run the following command, which will import your account and give it a name:
ape accounts import INSERT_ACCOUNT_NAME
You'll then be prompted to enter your private key and add a password to encrypt your account.
Note
If you wish to use a mnemonic instead, you can append the --use-mnemonic
option to the import command.
Create Smart Contracts¶
Now that you have set up your account, you can start writing smart contracts. As a basic example, you can use the following Box
contract to store a value you can retrieve later.
Start by creating a file named Box.sol
inside the contracts directory:
touch contracts/Box.sol
Open the file and add the following contract to it:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.1;
contract Box {
uint256 private value;
event ValueChanged(uint256 newValue);
function store(uint256 newValue) public {
value = newValue;
emit ValueChanged(newValue);
}
function retrieve() public view returns (uint256) {
return value;
}
}
You can store any additional contracts in the contracts
directory.
Compile the Solidity Contract¶
Before compiling the Solidity, you must install the Solidity compiler plugin. Running the following command will install the latest version of the plugin:
ape plugins install solidity
To use a specific version of Solidity or a specific EVM version, you can modify your ape-config.yaml
file as follows:
solidity:
version: INSERT_VERSION
evm_version: INSERT_VERSION
For more information on the Solidity plugin, please refer to the README of the ape-solidity
repository on GitHub.
With the Solidity plugin installed, the next step is to compile the smart contract:
ape compile
After compilation, you can find the bytecode and ABI for your contracts in the .build
directory.
Test the Contract¶
Before you deploy your contract, you can test it out directly inside your Ape project using the pytest framework to make sure it works as you expect.
You should already have a tests
directory where you'll create your tests, but if not, please create one, as all tests must be located in a directory named tests
. Additionally, each test file name must start with test_
and end with .py
. So, first, you can create a test file for the Box.sol
contract:
touch tests/test_box.py
In addition to the test file, you can create a conftest.py
file that will define a couple of essential fixtures. Fixtures allow you to define functions that set up the necessary environment or resources to run your tests. Note that while the Box.sol
contract is simple, incorporating fixtures into your testing process is good practice.
To create the file, you can run the following command:
touch tests/conftest.py
Since your tests will rely on the injection of the fixtures, you must define the fixtures first. When defining fixtures, you need to apply the pytest.fixture
decorator above each function. For this example, you'll create two fixtures: one that defines the owner of the contract and one that deploys the contract from the owner's account.
The owner
fixture will use the built-in accounts
fixture to take the first account in the list of test accounts provided by Ape and return it. The box
fixture will deploy the Box
contract type using the built-in project
fixture, you simply have to provide the name of the contract and use the owner
fixture to deploy it.
import pytest
@pytest.fixture
def owner(accounts):
return accounts[0]
@pytest.fixture
def box(owner, project):
return owner.deploy(project.Box)
Now that you've created the fixtures, you can start creating your tests. Each test function name must start with test_
and describe what the test does. For this example, you can use test_store_value
, as you'll create a test for the store
function. The test will store a value and then retrieve it, asserting that the retrieved value is equal to the value passed into the store
function.
To use the owner
and box
fixtures, you must pass them into your test function, which will inject the fixtures automatically for you to use. The owner
account will be used to call the store
function of the box
contract instance.
def test_store_value(box, owner):
new_value = 5
box.store(new_value, sender=owner)
assert box.retrieve() == new_value
And that's it! That's all you'll need inside your test file. You can use the following command to run the test:
ape test
The results of running the test will be printed to the terminal.
tests/test_box.py . [100%]
====================== 1 passed in 0.70s =======================
Now that you have confidence in your contract, the next step is to deploy it.
Deploy the Contract¶
To deploy your contracts, create a deployment script named deploy.py
inside of the scripts
directory:
touch scripts/deploy.py
Next, you'll need to write the deployment script. You'll need to load the account you will use to deploy the contract and access it by its name using the project manager.
from ape import project, accounts
def main():
# Load your account by its name
account = accounts.load("alice")
# Deploy the contract using your account
return account.deploy(project.Box)
Now you're ready to deploy the Box
contract! 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.
Take the following steps to initiate and send the deployment transaction:
-
Run the deployment script using the
ape run deploy
commandape run deploy --network INSERT_RPC_API_ENDPOINT
ape run deploy --network INSERT_RPC_API_ENDPOINT
ape run deploy --network https://rpc.api.moonbase.moonbeam.network
ape run deploy --network http://127.0.0.1:9944
Note
For the
ape run deploy
command to work as intended, the deployment script must be nameddeploy.py
and stored in thescripts
directory, and the script must define amain()
method. -
Review the transaction details and enter y to sign the transaction
- Enter your passphrase for your account
- Enter y to leave your account unlocked or n to lock it
After you follow the prompts and submit the transaction, the transaction hash, total fees paid, and contract address will be displayed in the terminal.
DynamicFeeTransaction: chainId: 1287 from: 0x097D9Eea23DE2D3081169e0225173d0C55768338 gas: 123964 nonce: 372 value: 0 data: 0x307836...303333 type: 2 maxFeePerGas: 125000000 maxPriorityFeePerGas: 0 accessList: []
Sign: [y/N]: y Enter passphrase to unlock 'alice' []: Leave 'alice' unlocked? [y/N]: n INFO: Confirmed 0x365cd903e7fac5ad1f815d7a6f211b1aa32bd7d78630c2e81d67514cfb9e55bb (total fees paid = 15326250000000) SUCCESS: Contract 'Box' deployed to: 0x68039277300E8B104dDf848029dCA04C2EFe8610
Congratulations! Your contract is live! Save the address to interact with your contract in the following section.
Interact with the Contract¶
You can interact with contracts using the Ape console for quick debugging and testing, or write a script.
Using The Ape Console¶
To interact with your newly deployed contract, you can launch the Ape console by running:
ape console --network INSERT_RPC_API_ENDPOINT
ape console --network INSERT_RPC_API_ENDPOINT
ape console --network https://rpc.api.moonbase.moonbeam.network
ape console --network http://127.0.0.1:9944
Next, you'll need to create a contract instance using the contract's address:
box = Contract("INSERT_CONTRACT_ADDRESS")
alice = accounts.load("alice") box = Contract("0x68039277300E8B104dDf848029dCA04C2EFe8610")
Now, you can interact with your contract instance! For example, you can set the variable to be stored in the Box
contract using the following commands:
-
Call the
store
method by passing in a value to store and the account you want to use to send the transaction:box.store(4, sender=alice)
-
Review the transaction details and enter y to sign the transaction
- If you previously locked your account, you must enter your passphrase to unlock it. Otherwise, Ape will use the cached key for your account
- If you unlocked your account in the previous step, you'll be asked if you want to leave your account unlocked. You can enter y to leave it unlocked or n to lock it
After you follow the prompts and submit the transaction, the transaction hash and total fees paid will be displayed in the terminal.
Sign: [y/N]: y Enter passphrase to unlock 'alice' []: Leave 'alice' unlocked? [y/N]: n INFO: Confirmed 0xd2e8305f22f33c1ab8ccaaef94252a93ff0f69c9bf98503fc2744bf257f1ef67 (total fees paid = 5573750000000) Receipt 0xd2e8305f22f33c1ab8ccaaef94252a93ff0f69c9bf98503fc2744bf257f1ef67
Then, you can retrieve the stored value by calling the retrieve
method:
box.retrieve()
The number you just stored in the previous steps will be printed to the console.
Using a Script¶
You can also write a script to interact with your newly deployed contract. To get started, you can create a new file in the scripts
directory:
touch scripts/store-and-retrieve.py
Next, you can write a script that stores and retrieves a value. Note that when creating a contract instance to interact with, you must pass in the address of the deployed contract.
from ape import Contract, accounts
def main():
account = accounts.load("alice")
box = Contract("INSERT_CONTRACT_ADDRESS")
store = box.store(4, sender=account)
print("Transaction hash for updating the stored value:", store.txn_hash)
retrieve = box.retrieve()
print("Stored value:", retrieve)
Now, you can run the script to set the stored value and retrieve it:
-
Run the script
ape run store-and-retrieve --network INSERT_RPC_API_ENDPOINT
ape run store-and-retrieve --network INSERT_RPC_API_ENDPOINT
ape run store-and-retrieve --network https://rpc.api.moonbase.moonbeam.network
ape run store-and-retrieve --network http://127.0.0.1:9944
-
Review the transaction details and enter y to sign the transaction
- If you previously locked your account, you must enter your passphrase to unlock it. Otherwise, Ape will use the cached key for your account
- If you unlocked your account in the previous step, you'll be asked if you want to leave your account unlocked. You can enter y to leave it unlocked or n to lock it
Once completed, you should see a transaction hash and a value of 4
printed to the console.
Sign: [y/N]: y Enter passphrase to unlock 'alice' []: Leave 'alice' unlocked? [y/N]: n INFO: Confirmed 0x6d74e48c23fd48438bf48baad34e235693c737bd880ef0077c0fb996f3896f5f (total fees paid = 3086250000000) Transaction hash for updating the stored value: 0x6d74e48c23fd48438bf48baad34e235693c737bd880ef0077c0fb996f3896f5f Stored value: 5
Congratulations! You have successfully deployed and interacted with a contract using Ape!
| Created: February 7, 2024