Developing contracts
While developing AEAs, users frequently face the need of interacting with smart contracts in a third-party decentralized ledger. To achieve this, developers must create Contract packages: components that provide wrappers around the API or ABI of the target smart contract.
In this guide, we will learn how to develop our own contract package to interface with an ERC20 token. We will use the WETH contract deployed on Ethereum at this address.
Step-by-step instructions
Before starting this guide, ensure that you have done one of the following things:
-
You have cloned our developer template. Once setup, it will generate a virtual environment with Open AEA installed, an empty local registry, some useful tools for checking packages and dummy tests.
-
You have gone through the quickstart to verify that your machine satisfies the framework requirements and that you have followed the setup instructions so you have the Open AEA framework and the Ethereum plugin installed.
Now, let's create our contract package:
-
Download the WETH contract ABI here: scroll down to the Contract ABI section and click the copy button on the right. Then, create a new file called
IERC20.json
and paste the content you have just copied there. -
Initialize the IPFS registry:
autonomy init --reset --author john_doe --remote --ipfs --ipfs-node "/dns/registry.autonolas.tech/tcp/443/https"
-
Scaffold the ERC20 contract:
You'll find the contract in the local registry ataea scaffold --to-local-registry contract ERC20Contract /path/to/IERC20.json
packages/john_doe/contracts/ERC20Contract
. -
Now it is time to call the new method from an agent. Let's say that we would like to get the WETH balance for the WETH account itself. In any skill's
behaviour.py
file, first import the contract package and set the target address:from packages.john_doe.contracts.erc20.contract import ( ContractApiMessage, ) WETH_ADDRESS = "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"
And finally, call the get_balance method from any behaviour:
contract_api_msg = yield from self.get_contract_api_response( performative=ContractApiMessage.Performative.GET_STATE, # type: ignore contract_address=WETH_ADDRESS, contract_id=str(ERC20Contract.contract_id), contract_callable="balanceOf", address=WETH_ADDRESS, ) if contract_api_msg.performative != ContractApiMessage.Performative.STATE: self.context.logger.info("Error retrieving the balance") return balance = contract_api_msg.state.body
At some point we might need to have some custom implementation in our contract package methods. For those cases, proceed as follows:
-
Open the contract at
packages/john_doe/contracts/ERC20Contract/contract.py
and add the following imports at the top of the file:from aea_ledger_ethereum import EthereumApi from typing import Optional
Also add a custom method to retrieve balances to the
ERC20Contract
class@classmethod def balance_of( cls, ledger_api: EthereumApi, contract_address: str, owner_address: str ) -> Optional[JSONLike]: """Gets an account's balance.""" contract_instance = cls.get_instance(ledger_api, contract_address) # Implement your custom logic here. This is just an example: return ledger_api.contract_method_call( contract_instance=contract_instance, method_name="balanceOf", owner=owner_address, )
-
Fingerprint the contract so its hash matches our changes:
aea packages lock
-
Proceed with the call as we did previously.