Skip to content

Commit

Permalink
init commit
Browse files Browse the repository at this point in the history
  • Loading branch information
PatrickAlphaC committed Aug 2, 2022
0 parents commit 1f313bb
Show file tree
Hide file tree
Showing 27 changed files with 1,066 additions and 0 deletions.
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ANVIL=http://127.0.0.1:8545
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
venv
.build
206 changes: 206 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
# Apeworx (Vyper) Starter Kit

<br/>
<p align="center">
<a href="https://chain.link" target="_blank">
<img src="./img/apeworx-chainlink.png" width="225" alt="Chainlink Apeworx logo">
</a>
</p>
<br/>


This is a repo to work with and use Chainlink smart contracts in a python, [apeworx](https://www.apeworx.io/) & [vyper](https://vyper.readthedocs.io/en/stable/index.html) environment. If you're brand new to Chainlink, check out the beginner walk-through in remix to [learn the basics.](https://docs.chain.link/docs/beginners-tutorial)

It shows how to use the these frameworks and languages as well as the following Chainlink features:
- [Chainlink Price Feeds](https://docs.chain.link/docs/using-chainlink-reference-contracts)
- [Chainlink VRF](https://docs.chain.link/docs/chainlink-vrf)
- [Chainlink Keepers](https://docs.chain.link/docs/chainlink-keepers/introduction/)

- [Apeworx (Vyper) Starter Kit](#apeworx-vyper-starter-kit)
- [Getting Started](#getting-started)
- [Requirements](#requirements)
- [Quickstart](#quickstart)
- [Usage](#usage)
- [Deploying Contracts](#deploying-contracts)
- [Price Feed Consumer](#price-feed-consumer)
- [Keepers Consumer](#keepers-consumer)
- [VRFv2 Consumer](#vrfv2-consumer)
- [Deploying to Local, Adhoc, Mainnet, and Testnets](#deploying-to-local-adhoc-mainnet-and-testnets)
- [Importing an account](#importing-an-account)
- [Deploy to a local or adhoc network](#deploy-to-a-local-or-adhoc-network)
- [Deploy to a mainnet or test network](#deploy-to-a-mainnet-or-test-network)
- [Interacting with Contracts](#interacting-with-contracts)
- [Miscellaneous](#miscellaneous)
- [Contributing](#contributing)
- [Resources](#resources)

# Getting Started

It's recommended that you've gone through the [apeworx getting started documentation](https://docs.apeworx.io/ape/stable/userguides/quickstart.html) before proceeding here.

## Requirements

- [git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)
- You'll know you did it right if you can run `git --version` and you see a response like `git version x.x.x`
- [Python](https://www.python.org/downloads/)
- You'll know you've installed python right if you can run:
- `python --version` or `python3 --version` and get an ouput like: `Python x.x.x`
- [pipx](https://pypa.github.io/pipx/installation/)
- `pipx` is different from [pip](https://pypi.org/project/pip/)
- You may have to close and re-open your terminal
- You'll know you've install it right if you can run:
- `pipx --version` and see something like `x.x.x.x`
- [eth-ape (ape)](https://docs.apeworx.io/ape/stable/userguides/quickstart.html#installation)
- We recommend using `pipx` but you can [follow the ape documentation](https://docs.apeworx.io/ape/stable/userguides/quickstart.html#installation) for other installation methods.
- You'll know you've done it right if you run `ape --version` and see an output like `x.x.x`

## Quickstart

1. Clone repo and install dependencies

```bash
git clone https://github.com/smartcontractkit/apeworx-starter-kit
cd apeworx-starter-kit
ape plugins install alchemy vyper
```

2. You're ready to go!


Run tests:

```
ape test
```

# Usage

If you run `ape --help` you'll get an output of all the tasks you can run.

## Deploying Contracts

The following will deploy your contracts to a temporary ape test network. Additionally, if on a local network, it will deploy mock Chainlink contracts for you to interact with. If you'd like to interact with your deployed contracts, skip down to [Interacting with Deployed Contracts](#interacting-with-deployed-contracts).

After your script completes, the network deletes itself.

### Price Feed Consumer

```
ape run scripts/deploy_price_feed_consumer.py
```

### Keepers Consumer

```
ape run scripts/deploy_keepers_consumer.py
```

### VRFv2 Consumer

```
ape run scripts/deploy_vrf_consumer.py
```

## Deploying to Local, Adhoc, Mainnet, and Testnets

In order to deploy to a local, adhoc, mainnet, or testnet , you'll need to first create accounts. For the scripts we currently have, it'll default to the "default" account. If you'd like to have the scripts point to a different account, go to `helper_functions.py` and change the `get_account` function to look for you account instead of `default.

Ape doesn't support `.env` files or keeping your private keys in plaintext, which means it's harder for you to release your private key to the world!

### Importing an account

To import an account into ape, run the following:

```
ape accounts import default
```

Where `default` will be the name of your account. Ape will then prompt you for your private key and password, and encrypt it on your computer. The only way to use this key moving forward will be to decrypt the key with your password.

### Deploy to a local or adhoc network

Ape doesn't come with a built in local network like hardhat or ganache, so we will have to use our own. Ape also prefers users to build plugins for working additional networks, [you can find a list of the plugins on their github.](https://github.com/ApeWorX?q=ape-&type=all&language=&sort=)

We recommend using [Foundry's Anvil](https://book.getfoundry.sh/anvil/) as your local network.

1. Install Foundry / Anvil

You'll know you did it right if you can run `anvil --version` and get an output like `anvil 0.1.0 (f016135 2022-07-04T00:15:02.655418Z)`

2. Start up anvil

Run:

```
anvil
```

You'll see an output with many private keys.

If you'd like to use this as your main "default" account, run the following:

```
ape accounts delete default
```
And then, re-import your private key from anvil by following [the importing an account guide](#importing-an-account)

3. Run your script

> Note: This will only work since the chain Id is `31337` for anvil! For working with non-local networks, please see [Deploy to a mainnet or testnet](#deploy-to-a-main-or-test-network)
```
ape run scripts/deploy_price_consumer.py --network http://127.0.0.1:8545
```

You'll be prompted for your password.


### Deploy to a mainnet or test network

1. Import an account

Please see [import an account](#importing-an-account). And be sure your account has plenty of testnet or mainnet tokens if working on live network. See [this faucet](https://faucets.chain.link/) for testnet tokens.

2. Set your RPC_URL

Since we are working with Alchemy, create an [environment variables](https://www.twilio.com/blog/2017/01/how-to-set-environment-variables.html) called `WEB3_ALCHEMY_PROJECT_ID` or `WEB3_ALCHEMY_API_KEY`. If using a linux or mac environment, you can set it by running:

```
export WEB3_ALCHEMY_PROJECT_ID=MY_API_TOKEN
```

> Note: At this time, it's really tricky to change networks with Ape. If you want to use another network ape doesn't have a plugin for, you can use an adhoc network as shown above.
3. Update your `helper_config.py`

If you're using a network not covered in `helper_config.py` be sure to add it.

4. Run your script!

```
ape run scripts/deploy_price_feed_consumer.py --network ethereum:rinkeby:alchemy
```

### Interacting with Contracts

To interact with contracts, we recommend using the console.

```
ape console --network ethereum:rinkeby:alchemy
```

# Miscellaneous

1. Testing and forking is a bit tricky at the moment.


## Contributing

Contributions are always welcome! Open a PR or an issue!

Thank You!

## Resources

- [Chainlink Documentation](https://docs.chain.link/)
- [Ape Documentation](https://docs.apeworx.io/ape/stable/)
17 changes: 17 additions & 0 deletions ape-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
name: apeworx-starter-kit
contracts_folder: contracts
deployments:
ethereum:
rinkeby:
- contract_type: AggregatorV3Interface
address: "0x8A753747A1Fa494EC906cE90E9f37563A8AF630e"
- contract_type: VRFCoordinatorV2
address: "0x6168499c0cFfCaCD319c818142124B7A15E857ab"
- contract_type: LinkToken
address: "0x01be23585060835e02b77ef475b0cc51aa1e0709"
default_ecosystem: ethereum
foundry:
fork:
ethereum:
mainnet:
upstream_provider: alchemy
25 changes: 25 additions & 0 deletions contracts/KeepersConsumer.vy
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# SPDX-License-Identifier: MIT
# @version ^0.3.3

counter: public(uint256)
INTERVAL: immutable(uint256)
last_time_stamp: uint256

@external
def __init__(update_interval: uint256):
INTERVAL = update_interval
self.last_time_stamp = block.timestamp
self.counter = 0

@external
@view
def checkUpkeep(checkData: Bytes[1]) -> (bool, Bytes[1]):
upkeep_needed: bool = (block.timestamp - self.last_time_stamp) > INTERVAL
return(upkeep_needed, b"\x00")

@external
def performUpkeep(calldata: Bytes[1]):
upkeep_needed: bool = (block.timestamp - self.last_time_stamp) > INTERVAL
assert upkeep_needed, "upkeep not needed!"
self.last_time_stamp = block.timestamp
self.counter = self.counter + 1
20 changes: 20 additions & 0 deletions contracts/PriceConsumer.vy
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# SPDX-License-Identifier: MIT
# @version ^0.3.3
import 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
43 changes: 43 additions & 0 deletions contracts/VRFConsumerV2.vy
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# SPDX-License-Identifier: MIT
# @version ^0.3.3
import interfaces.VRFCoordinatorV2 as VRFCoordinatorV2

NUM_WORDS: constant(uint32) = 1
REQUEST_CONFIRMATIONS: constant(uint16) = 3
CALLBACK_GAS_LIMIT: constant(uint32) = 100000

vrf_coordinator: public(VRFCoordinatorV2)
subscription_id: uint64
key_hash: bytes32
random_words: public(uint256[NUM_WORDS])

event ReturnedRandomness:
random_words: uint256[NUM_WORDS]

@external
def __init__(_subscription_id: uint64, _vrf_coordinator_address: address, _key_hash: bytes32):
self.vrf_coordinator = VRFCoordinatorV2(_vrf_coordinator_address)
self.subscription_id = _subscription_id
self.key_hash = _key_hash

@external
def request_random_words():
self.vrf_coordinator.requestRandomWords(
self.key_hash,
self.subscription_id,
REQUEST_CONFIRMATIONS,
CALLBACK_GAS_LIMIT,
NUM_WORDS
)

@internal
def fulfillRandomWords(request_id: uint256, _random_words: uint256[NUM_WORDS]):
self.random_words = _random_words
log ReturnedRandomness(_random_words)

# In solidity, this is the equivalent of inheriting the VRFConsumerBaseV2
# Vyper doesn't have inheritance, so we just add the function here
@external
def rawFulfillRandomWords(requestId: uint256, randomWords: uint256[NUM_WORDS]):
assert msg.sender == self.vrf_coordinator.address, "Only coordinator can fulfill!"
self.fulfillRandomWords(requestId, randomWords)
40 changes: 40 additions & 0 deletions contracts/interfaces/AggregatorV3Interface.vy
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# @version ^0.3.3


@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)


# Inline interface example:
# interface AggregatorV3Interface:
# def decimals() -> uint8: view
# def description() -> String[1000]: view
# def version() -> uint256: view
# def getRoundData(_roundId: uint80) -> (uint80, int256, uint256, uint256, uint80): view
# def latestRoundData() -> (uint80, int256, uint256, uint256, uint80): view
19 changes: 19 additions & 0 deletions contracts/interfaces/VRFCoordinatorV2.vy
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@

# @version ^0.3.3
# https://github.com/smartcontractkit/chainlink/blob/develop/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol


@external
@view
def getRequestConfig() -> (uint16, uint32, Bytes[1000]):
return (0, 0, b"\x00")


@external
def requestRandomWords(keyHash: bytes32, subId: uint64,minimumRequestConfirmations: uint16,callbackGasLimit: uint32,numWords: uint32) -> uint256:
return 0


@external
def createSubscription() -> uint64:
return 0
Loading

0 comments on commit 1f313bb

Please sign in to comment.