Named Contracts

One of the major differentiators between moccasin and other smart contract development tools is more flexible and powerful scripting. One of the such cabailities named contracts.

Named contracts allow you to define deployment scripts, addresses by chain, fixtures settings for testing, and more.

Named Contract Example - Minimal Example

Let’s look at a minimal moccasin.toml with a ETH mainnet network fork with a named contract:

[project]
src = "contracts"

[networks.mainnet-fork]
url = "https://ethereum-rpc.publicnode.com"
chain_id = 1
fork = true

# Look here! We have a named contract named "usdc"
[networks.mainnet-fork.contracts]
usdc = { address = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"}

The NamedContract in this case, is usdc. And it’s this named contract that we can access our scripts!

from moccasin.config import get_config

def print_contract_address():
    active_network = get_config().get_active_network()
    named_contract = active_network.get_named_contract("usdc")
    print(named_contract.address)

def moccasin_main():
    print_contract_address()

We could run this script, and it would, print out the address from our config.

mox run print_contract_address --network mainnet-fork

0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48

Named Contract Example - Interacting with the contract

Now, just getting the address is pretty boring, typically you want to interact with the contract. To do so, we have a number of parameters you can set in the config to get the contract ABI (Application Binary Interface).

Note

You can learn more about what an ABI is from Cyfrin Updraft or the Cyfrin Blog.

There are a number of flags we can set for our NamedContract:

[networks.mainnet-fork.contracts.usdc]
address = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
abi = "ERC20.vy"
abi_from_explorer = true
deployment_script = "script/deploy_erc20.py"
force_deploy = false

Let’s break these down:

  • address: The address of the contract on the network.

  • abi`: The ABI source of the contract. This is can be any of the following:
    • .json file path to an ABI file

    • .vy file path to a Vyper contract

    • Coming soon .vyi file path to a Vyper interface

    • A “raw” ABI string

  • abi_from_explorer: If you want to get the ABI from an explorer. This is useful if you don’t have the ABI and you want to get it from a public source. You’ll need to set a explorer_api_key in your moccasin.toml, or an EXPLORER_API_KEY environment variable.

  • deployment_script: The path to the deployment script for this named contract, this will be a shorthand for deploying in the future.

  • force_deploy`: If you want to force deploy the contract when manifesting the contract.

As we know, to interact with a contract, one of the most important things is the ABI. For us to interact with any named contract, we give it an ABI, and we can start interacting with that named contract using the manifest_named function.

from moccasin.config import get_config

def print_contract_address():
    active_network = get_config().get_active_network()
    usdc: VyperContract = active_network.manifest_named("usdc")
    decimals = usdc.decimals()
    print(decimals)

def moccasin_main():
    print_contract_address()

And running this on mainnet-fork will get the resulting output:

# Run this
mox run print_contract_address --network mainnet-fork

# Output
6 # USDC has 6 decimals because it is weird

The key here, was the mainifest_contract, which does a lot of things under the hood, including:

  • Deploys a contract if one doesn’t exist

  • Returns the named contract at it’s address if it’s on a chain we recognize

  • Sets up local testing environments for us to test our contracts

And more! Let’s read more about the power of NamedContracts and how they can help you in your development process.