Manifest Named Contract (get_or_deploy_named_contract
)¶
Note
Be sure to at least read manifest introduction before continuing here.
An issue that developers run into when creating smart contracts, is knowing how to make their deployment and scripting pipelines portable and flexible.
What happens if you change chains that you want to deploy to?
How can you test to make sure your deploy scripts work exactly the way your tests run?
If I want to simulate a test with a forked test <forked-networks.rst>, will my scripts still work?
This is where the manifest_named
aka get_or_deploy_named_contract
function comes in. Let’s say you have a smart contract that has an address as a constructor parameter, because the address is different on different chains. This additionally is very helpful in testing, and NamedContract
s have built-in pytest
fixtures.
Interacting with a contract on multiple networks with one script¶
Let’s take the following contract:
# pragma version 0.4.0
# SPDX-License-Identifier: MIT
from interfaces import ERC20
usdc: public(ERC20)
@deploy
def __init__(usdc: address):
self.usdc = ERC20(usdc)
@external
def get_usdc_decimals() -> uint256:
return self.usdc.decimals()
We want to be able to test this contract on:
A locally running network
A forked network
A testnet
A mainnet
Potentially a second mainnet
In order for us to do this traditionally, we’d have to write a separate script for every single one of these cases. For example:
if network.name == "locally_running_chain":
# do something...
elif network.name == "forked_chain":
# do something...
elif network.name == "testnet":
# do something...
elif network.name == "mainnet":
# do something...
elif network.name == "second_mainnet":
# do something...
And this is awful! So instead, what we can do, is setup ALL of the configuration in our moccasin.toml
file and use the magic manifest_named
function to automatically deploy the contract on the correct network, with the correct parameters. Let’s look at the moccasin.toml
file:
# At the top, we can set some default parameters for the `usdc` named contract
# Every named contract will use the ERC20.vy contract in the project as it's ABI
# If the contract doesn't exist, it'll deploy with the `deploy_feed.py` script (for example, on a locally running network)
[networks.contracts]
usdc = { abi = "ERC20", deployer_script = "mock_deployer/deploy_feed.py"}
# We can then set the parameters for each network
[networks.mainnet]
url = "mainnet_url" # Enter your mainnet url here
[networks.mainnet.contracts]
usdc = { address = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" }
[networks.sepolia]
url = "sepolia_url" # Enter your mainnet url here
[networks.sepolia.contracts]
usdc = { address = "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238" }
[networks.arbitrum]
url = "arbitrum" # Enter your mainnet url here
[networks.arbitrum.contracts]
usdc = { address = "0xaf88d065e77c8cC2239327C5EDb3A432268e5831" }
And with this, we only need ONE script that works for all of these!
from moccasin.config import get_config
def get_decimals():
active_network = get_config().get_active_network()
usdc: VyperContract = active_network.manifest_named("usdc")
decimals = usdc.decimals()
print(decimals)
def moccasin_main():
get_decimals()
Then, we just need to adjust the –network flag and everything else will work automatically.
# Mainnet
mox run get_decimals --network mainnet
# Sepolia
mox run get_decimals --network sepolia
# Arbitrum
mox run get_decimals --network arbitrum
# These next two are special
# Forked
mox run get_decimals --network mainnet --fork
# Local (pyevm)
mox run get_decimals
The first 3 commands will do as you expect, directly connecting to the URL you set in your moccasin.toml
file. The last two are special:
The
--fork
flag will setup your script to run locally, using yourmainnet
url.If you don’t specify a network, you’ll use the special locally running pyevm network.