A Simple Vyper smart contract to fetch the price of BTC using Chainlink and Apeworx.
Muwawu Moses
Posted on June 28, 2024
In this tutorial, we are going to navigate the basics of consuming a price feed oracle from Chainlink using the Apeworkx(ape) framework and vyper. We are going to connect to the ethereum network using Sepolia via Alchemy.
Prerequisites:
- Development environment already setup
- Metamask wallet set up
- Configured Metamask to use LINK tokens.
Procedure:
Since we have everything ready including the development environment, we can now begin. Activate your virtual environment in the terminal and run ape init
to initialize the ape project. Go on and provide the project name of your choice.
Open the ape-config.yaml
file and add the following code;
dependencies:
- name: chainlink
github: smartcontractkit/chainlink-mix
branch: main
The above code will ensure that chainlink is installed into our environment as a dependency.
Next, we are going to write our smart contracts. Navigate to the contracts folder and create a new folder interfaces
. Inside the interfaces folder, add a file named AggregatorV3Interface.vy
:
#pragma version ^0.3.10
@external
@view
def decimals() -> uint8:
return 0
@external
@view
def description() -> String[1000]:
return ""
@external
@view
def version() -> uint256:
return 0
@external
@view
def getRoundData(_roundId: uint80) -> (uint80, int256, uint256, uint256, uint80):
return (0, 0, 0, 0, 0)
@external
@view
def latestRoundData() -> (uint80, int256, uint256, uint256, uint80):
return (0, 0, 0, 0, 0)
Within the contracts folder, add a new file; test/PriceConsumer.vy
# SPDX-License-Identifier: MIT
#pragma version ^0.3.10
import contracts.interfaces.AggregatorV3Interface as AggregatorV3Interface
price_feed: public(AggregatorV3Interface)
@external
def __init__(_price_feed_address: address):
self.price_feed = AggregatorV3Interface(_price_feed_address)
@external
@view
def get_latest_price() -> int256:
a: uint80 = 0
price: int256 = 0
b: uint256 = 0
c: uint256 = 0
d: uint80 = 0
(a, price, b, c, d) = self.price_feed.latestRoundData()
return price
Our code is now ready to compile. Please note that you should have an internet connection for this compilation to be successful because the project has to install chainlink as dependency before compiling.
Deployment:
In the scripts folder, let's create a file named deploy.py
from ape import accounts, project
def main():
account = accounts.load("my_wallet")
aggregator_address = "0x1b44F3514812d835EB1BDB0acB33d3fA3351Ee43"
contract = account.deploy(project.PriceConsumer, aggregator_address)
print(f"Contract deployed at: {contract.address}")
if __name__ == "__main__":
main()
Here, i will mainly talk about the aggregator_address = "0x1b44F3514812d835EB1BDB0acB33d3fA3351Ee43"
. Where do we get it? Chainlink provides price feed addresses for all supported assets. So, you just have to copy and paste the address of your desired pair. To me, it's BTC-USD
. To deploy the contract, run the command; ape run deploy --network ethereum:sepolia:alchemy
After successfully deploying the contract, copy the contract address because we are going to use for interacting with the contract.
Interacting with contract:
There are two ways in which we can interact with the deployed contract i.e through the console and through python scripts.
- console
In your vs code terminal, run; ape console --network ethereum:sepolia:alchemy
In [1]: account = accounts.load("my_account")
Please enter passphrase to unlock 'my_account':
In [2]: contract_address = "YourDeployedContractAddress"
In [3]: contract = project.PriceConsumer.at(contract_address)
In [4]: latest_price = contract.get_latest_price()
In [5]: print(f"The latest BTC price is: {latest_price / 1e8} USD")
The latest BTC price is: 61744.39100403 USD
my_account
is the alias used for your ape imported account from metamask.
- Using scripts
First, we should update the ape-config.yaml
file so as to set the default network from ethereum:local:test
to ethereum:sepolia:alchemy
name: price-checker
dependencies:
- name: chainlink
github: smartcontractkit/chainlink-mix
branch: main
vyper:
version: 0.3.10
ethereum:
default_network: sepolia
sepolia:
default_provider: alchemy
default_ecosystem: ethereum
In the scripts folder, create a new file get_price.py
from ape import accounts, project,networks
def main():
contract_address = "0x4Aac9382A3Cbb489DE2c67353a587983b9080343"
# Load the deployed contract
contract = project.PriceConsumer.at(contract_address)
latest_price = contract.get_latest_price()
print(f"The latest BTC price is: {latest_price / 1e8 } USD")
if __name__ == "__main__":
main()
For eductaion purposes, we can go for a more straight forward approach(without setting up the ape-config.yaml file) often used for multi-network scripting. get_price2.py
:
import click
from ape.cli import ape_cli_context
from ape import project
@click.command()
@ape_cli_context()
def cli(cli_ctx):
# There is no connection yet at this point.
testnets = {
"ethereum": ["sepolia"]
}
nm = cli_ctx.network_manager
for ecosystem_name, networks in testnets.items():
ecosystem = nm.ecosystems[ecosystem_name]
for network_name in networks:
# Start making connections.
network = ecosystem.get_network(network_name)
with network.use_provider("alchemy") as provider:
print(f"Connected to {provider.network_choice}")
contract_address = "0x4Aac9382A3Cbb489DE2c67353a587983b9080343"
# Load the deployed contract
contract = project.PriceConsumer.at(contract_address)
latest_price = contract.get_latest_price()
print(f"The latest BTC price is: {latest_price / 1e8 } USD")
Congratulations if you have reached up to this stage. Please like and follow if you found this post helpful
Posted on June 28, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.