Oracle skills
This demo shows how an AEA can be used to maintain an oracle and how another AEA can request the oracle value.
Discussion
Oracle agents are agents that have permission to update or validate updates to state variables in a smart contract and whose goal is to accurately estimate or predict some real world quantity or quantities.
This demonstration shows how to set up a simple oracle agent who deploys an oracle contract and updates the contract with a token price fetched from a public API. It also shows how to create an oracle client agent that can request the value from the oracle contract.
Preparation instructions
Dependencies
Follow the Preliminaries and Installation sections from the AEA quick start.
Demo
Create the oracle AEA
Fetch the AEA that will deploy and update the oracle contract.
Alternatively, create from scratch.
Create the AEA that will deploy the contract.
aea create coin_price_oracle
cd coin_price_oracle
aea add connection fetchai/http_client:0.24.0
aea add connection fetchai/ledger:0.20.0
aea add connection fetchai/p2p_libp2p:0.26.0
aea add skill fetchai/advanced_data_request:0.7.0
aea add skill fetchai/simple_oracle:0.15.0
aea config set --type dict agent.dependencies \
'{
"aea-ledger-fetchai": {"version": "<2.0.0,>=1.0.0"},
"aea-ledger-ethereum": {"version": "<2.0.0,>=1.0.0"}
}'
aea config set agent.default_connection fetchai/p2p_libp2p:0.26.0
aea install
aea build
aea config set --type str vendor.fetchai.skills.advanced_data_request.models.advanced_data_request_model.args.url "https://api.coingecko.com/api/v3/simple/price?ids=fetch-ai&vs_currencies=usd"
The following steps depend on the type of ledger the oracle will be run on.
Select a ledger type by setting a temporary variable to either fetchai
or ethereum
:
Update the default ledger and cert requests using the chosen ledger.
aea config set agent.default_ledger $LEDGER_ID
aea config set --type list vendor.fetchai.connections.p2p_libp2p.cert_requests \
'[{"identifier": "acn", "ledger_id": '"\"$LEDGER_ID\""', "not_after": "2022-01-01", "not_before": "2021-01-01", "public_key": "fetchai", "message_format": "{public_key}", "save_path": ".certs/conn_cert.txt"}]'
Set the following configuration for the oracle skill:
If running on the Fetch.ai ledger:aea config set vendor.fetchai.skills.simple_oracle.models.strategy.args.update_function update_oracle_value
aea config set vendor.fetchai.skills.simple_oracle.models.strategy.args.update_function updateOracleValue
Additionally, create the private key for the oracle AEA. Generate and add a key for use with the ledger:
If running on a testnet (not including Ganache), generate some wealth for your AEA:
Next, create a private key used to secure the AEA's communications:
aea generate-key fetchai fetchai_connection_private_key.txt
aea add-key fetchai fetchai_connection_private_key.txt --connection
Finally, certify the keys for use by the connections that request them:
Create the oracle client AEA
From a new terminal (in the same top-level directory), fetch the AEA that will deploy the oracle client contract and call the function that requests the coin price from the oracle contract.
Alternatively, create from scratch.
Create the AEA that will deploy the contract.
aea create coin_price_oracle_client
cd coin_price_oracle_client
aea add connection fetchai/http_client:0.24.0
aea add connection fetchai/ledger:0.20.0
aea add skill fetchai/simple_oracle_client:0.12.0
aea config set --type dict agent.dependencies \
'{
"aea-ledger-fetchai": {"version": "<2.0.0,>=1.0.0"},
"aea-ledger-ethereum": {"version": "<2.0.0,>=1.0.0"}
}'
aea config set agent.default_connection fetchai/ledger:0.20.0
aea install
aea build
Similar to above, set a temporary variable LEDGER_ID=fetchai
or LEDGER_ID=ethereum
.
Set the default ledger:
Set the following configuration for the oracle client skill: If running on the Fetch.ai ledger:aea config set vendor.fetchai.skills.simple_oracle_client.models.strategy.args.query_function query_oracle_value
aea config set vendor.fetchai.skills.simple_oracle_client.models.strategy.args.query_function queryOracleValue
Create the private key for the oracle client AEA. Generate and add a key for use on the ledger:
If running on a testnet (not including Ganache), generate some wealth for your AEA:
The oracle AEAs require either a locally running test node or a connection to a remote testnet.
Setting up with a local Ganache node (Ethereum ledger only)
The easiest way to test the oracle agents on an Ethereum-based ledger to set up a local test node using Ganache. This can be done by running the following docker command from the directory you started from (in a new terminal). This command will also fund the accounts of the AEAs:
docker run -p 8545:8545 trufflesuite/ganache-cli:latest --verbose --gasPrice=0 --gasLimit=0x1fffffffffffff --account="$(cat coin_price_oracle/ethereum_private_key.txt),1000000000000000000000" --account="$(cat coin_price_oracle_client/ethereum_private_key.txt),1000000000000000000000"
Run the enclosed Python script (with web3
installed) from the top-level directory to deploy a mock Fetch ERC20 contract and give some test FET to the client agent.
import json
import os
from web3 import Web3
FILE_DIR = os.path.dirname(os.path.realpath(__file__))
CONTRACT_PATH = os.path.join(FILE_DIR, "coin_price_oracle_client/vendor/fetchai/contracts/fet_erc20/build/FetERC20Mock.json")
ORACLE_PRIVATE_KEY_PATH = os.path.join(FILE_DIR, "coin_price_oracle/ethereum_private_key.txt")
CLIENT_PRIVATE_KEY_PATH = os.path.join(FILE_DIR, "coin_price_oracle_client/ethereum_private_key.txt")
# Solidity source code
with open(CONTRACT_PATH) as file:
compiled_sol = json.load(file)
# web3.py instance
w3 = Web3(Web3.HTTPProvider('http://127.0.0.1:8545'))
# Import oracle account from private key and set to default account
with open(ORACLE_PRIVATE_KEY_PATH) as file:
private_key = file.read()
oracle_account = w3.eth.account.privateKeyToAccount(private_key)
w3.eth.defaultAccount = oracle_account.address
# Import client account from private key
with open(CLIENT_PRIVATE_KEY_PATH) as file:
private_key = file.read()
client_account = w3.eth.account.privateKeyToAccount(private_key)
# Deploy mock Fetch ERC20 contract
FetERC20Mock = w3.eth.contract(abi=compiled_sol['abi'], bytecode=compiled_sol['bytecode'])
# Submit the transaction that deploys the contract
tx_hash = FetERC20Mock.constructor(
name="FetERC20Mock",
symbol="MFET",
initialSupply=int(1e23),
decimals_=18).transact()
# Wait for the transaction to be mined, and get the transaction receipt
tx_receipt = w3.eth.waitForTransactionReceipt(tx_hash)
# Print out the contract address
print("FetERC20Mock contract deployed at:", tx_receipt.contractAddress)
# Get deployed contract
fet_erc20_mock = w3.eth.contract(address=tx_receipt.contractAddress, abi=compiled_sol['abi'])
# Transfer some test FET to oracle client account
tx_hash = fet_erc20_mock.functions.transfer(client_account.address, int(1e20)).transact()
tx_receipt = w3.eth.waitForTransactionReceipt(tx_hash)
Set the ERC20 contract address for the oracle AEA (Ethereum ledger only):
whereERC20_ADDRESS
is in the output of the script above.
Run the oracle AEA
Run the oracle agent. This will deploy a contract to the testnet, grant oracle permissions to the AEA's wallet address, and periodically update the contract with the latest price of FET (or whichever coin was specified).
After a few moments, you should see the following notices in the logs:
info: [coin_price_oracle] Oracle contract successfully deployed at address: ...
...
info: [coin_price_oracle] Oracle role successfully granted!
...
info: [coin_price_oracle] Oracle value successfully updated!
Set the ERC20 and oracle contract addresses for the oracle client AEA:
aea config set vendor.fetchai.skills.simple_oracle_client.models.strategy.args.erc20_address ERC20_ADDRESS
aea config set vendor.fetchai.skills.simple_oracle_client.models.strategy.args.oracle_contract_address ORACLE_ADDRESS
ORACLE_ADDRESS
should be set to the address shown in the oracle AEA logs:
Run the oracle client AEA
Run the oracle client agent. This will deploy an oracle client contract to the testnet, approve the contract to spend tokens on behalf of the AEA, and periodically call the contract function that requests the latest price of FET (or whichever coin was specified).
After a few moments, you should see the following notices in the logs:
The AEA will continue to request the latest coin price at the default time interval (every 15 seconds).