A Simple Vyper smart contract to fetch the price of BTC using Chainlink and Apeworx.
Tags: vyper, web3, ape, ethereum
Published: 2024-06-28 06:27:42

In this tutorial, we are going to navigate the basics of consuming a price feed oracle from [Chainlink](https://www.google.com/url?sa=t&source=web&rct=j&opi=89978449&url=https://docs.chain.link/&ved=2ahUKEwiHl6Wxxf2GAxWz9rsIHTPgAM4QFnoECA8QAQ&usg=AOvVaw34dUttbsMwPLK93PKGzjoM) using the [Apeworkx](https://docs.apeworx.io/ape/stable/userguides/quickstart.html)(ape) framework and vyper. We are going to connect to the ethereum network using [Sepolia ](https://www.google.com/url?sa=t&source=web&rct=j&opi=89978449&url=https://www.alchemy.com/faucets/ethereum-sepolia&ved=2ahUKEwjxiYmL67mGAxWk3QIHHTcoE8AQFnoECBUQAQ&usg=AOvVaw3SPRlvlGFEBx-iKOSKlCWU)via [Alchemy](https://www.google.com/url?sa=t&source=web&rct=j&opi=89978449&url=https://www.alchemy.com/&ved=2ahUKEwi0vP366rmGAxWb0AIHHVGFCHkQFnoECBsQAQ&usg=AOvVaw1UlSJxXmtPQHfkuY4FAfE8).

## Prerequisites:

1. [Development environment already setup](https://dev.to/mosesmuwawu/how-to-compile-deploy-and-interact-with-smart-contracts-using-apeworxape-and-vs-code-4hie)
2. Metamask wallet set up
3. [Configured Metamask](https://docs.chain.link/resources/acquire-link) 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; 

```yaml
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`:
```python
#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`
```python
# 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`
```python
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](https://docs.chain.link/data-feeds/price-feeds/addresses?network=ethereum&page=1) 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`
```bash
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 ](https://docs.apeworx.io/ape/stable/userguides/config.html#networks)from `ethereum:local:test` to `ethereum:sepolia:alchemy`
```yaml
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`
```python
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`:
```python
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