Skip to content

Commit 1f313bb

Browse files
committed
init commit
0 parents  commit 1f313bb

27 files changed

+1066
-0
lines changed

.env.example

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ANVIL=http://127.0.0.1:8545

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
venv
2+
.build

README.md

Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
# Apeworx (Vyper) Starter Kit
2+
3+
<br/>
4+
<p align="center">
5+
<a href="https://chain.link" target="_blank">
6+
<img src="./img/apeworx-chainlink.png" width="225" alt="Chainlink Apeworx logo">
7+
</a>
8+
</p>
9+
<br/>
10+
11+
12+
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)
13+
14+
It shows how to use the these frameworks and languages as well as the following Chainlink features:
15+
- [Chainlink Price Feeds](https://docs.chain.link/docs/using-chainlink-reference-contracts)
16+
- [Chainlink VRF](https://docs.chain.link/docs/chainlink-vrf)
17+
- [Chainlink Keepers](https://docs.chain.link/docs/chainlink-keepers/introduction/)
18+
19+
- [Apeworx (Vyper) Starter Kit](#apeworx-vyper-starter-kit)
20+
- [Getting Started](#getting-started)
21+
- [Requirements](#requirements)
22+
- [Quickstart](#quickstart)
23+
- [Usage](#usage)
24+
- [Deploying Contracts](#deploying-contracts)
25+
- [Price Feed Consumer](#price-feed-consumer)
26+
- [Keepers Consumer](#keepers-consumer)
27+
- [VRFv2 Consumer](#vrfv2-consumer)
28+
- [Deploying to Local, Adhoc, Mainnet, and Testnets](#deploying-to-local-adhoc-mainnet-and-testnets)
29+
- [Importing an account](#importing-an-account)
30+
- [Deploy to a local or adhoc network](#deploy-to-a-local-or-adhoc-network)
31+
- [Deploy to a mainnet or test network](#deploy-to-a-mainnet-or-test-network)
32+
- [Interacting with Contracts](#interacting-with-contracts)
33+
- [Miscellaneous](#miscellaneous)
34+
- [Contributing](#contributing)
35+
- [Resources](#resources)
36+
37+
# Getting Started
38+
39+
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.
40+
41+
## Requirements
42+
43+
- [git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)
44+
- You'll know you did it right if you can run `git --version` and you see a response like `git version x.x.x`
45+
- [Python](https://www.python.org/downloads/)
46+
- You'll know you've installed python right if you can run:
47+
- `python --version` or `python3 --version` and get an ouput like: `Python x.x.x`
48+
- [pipx](https://pypa.github.io/pipx/installation/)
49+
- `pipx` is different from [pip](https://pypi.org/project/pip/)
50+
- You may have to close and re-open your terminal
51+
- You'll know you've install it right if you can run:
52+
- `pipx --version` and see something like `x.x.x.x`
53+
- [eth-ape (ape)](https://docs.apeworx.io/ape/stable/userguides/quickstart.html#installation)
54+
- 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.
55+
- You'll know you've done it right if you run `ape --version` and see an output like `x.x.x`
56+
57+
## Quickstart
58+
59+
1. Clone repo and install dependencies
60+
61+
```bash
62+
git clone https://github.com/smartcontractkit/apeworx-starter-kit
63+
cd apeworx-starter-kit
64+
ape plugins install alchemy vyper
65+
```
66+
67+
2. You're ready to go!
68+
69+
70+
Run tests:
71+
72+
```
73+
ape test
74+
```
75+
76+
# Usage
77+
78+
If you run `ape --help` you'll get an output of all the tasks you can run.
79+
80+
## Deploying Contracts
81+
82+
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).
83+
84+
After your script completes, the network deletes itself.
85+
86+
### Price Feed Consumer
87+
88+
```
89+
ape run scripts/deploy_price_feed_consumer.py
90+
```
91+
92+
### Keepers Consumer
93+
94+
```
95+
ape run scripts/deploy_keepers_consumer.py
96+
```
97+
98+
### VRFv2 Consumer
99+
100+
```
101+
ape run scripts/deploy_vrf_consumer.py
102+
```
103+
104+
## Deploying to Local, Adhoc, Mainnet, and Testnets
105+
106+
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.
107+
108+
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!
109+
110+
### Importing an account
111+
112+
To import an account into ape, run the following:
113+
114+
```
115+
ape accounts import default
116+
```
117+
118+
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.
119+
120+
### Deploy to a local or adhoc network
121+
122+
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=)
123+
124+
We recommend using [Foundry's Anvil](https://book.getfoundry.sh/anvil/) as your local network.
125+
126+
1. Install Foundry / Anvil
127+
128+
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)`
129+
130+
2. Start up anvil
131+
132+
Run:
133+
134+
```
135+
anvil
136+
```
137+
138+
You'll see an output with many private keys.
139+
140+
If you'd like to use this as your main "default" account, run the following:
141+
142+
```
143+
ape accounts delete default
144+
```
145+
And then, re-import your private key from anvil by following [the importing an account guide](#importing-an-account)
146+
147+
3. Run your script
148+
149+
> 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)
150+
151+
```
152+
ape run scripts/deploy_price_consumer.py --network http://127.0.0.1:8545
153+
```
154+
155+
You'll be prompted for your password.
156+
157+
158+
### Deploy to a mainnet or test network
159+
160+
1. Import an account
161+
162+
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.
163+
164+
2. Set your RPC_URL
165+
166+
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:
167+
168+
```
169+
export WEB3_ALCHEMY_PROJECT_ID=MY_API_TOKEN
170+
```
171+
172+
> 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.
173+
174+
3. Update your `helper_config.py`
175+
176+
If you're using a network not covered in `helper_config.py` be sure to add it.
177+
178+
4. Run your script!
179+
180+
```
181+
ape run scripts/deploy_price_feed_consumer.py --network ethereum:rinkeby:alchemy
182+
```
183+
184+
### Interacting with Contracts
185+
186+
To interact with contracts, we recommend using the console.
187+
188+
```
189+
ape console --network ethereum:rinkeby:alchemy
190+
```
191+
192+
# Miscellaneous
193+
194+
1. Testing and forking is a bit tricky at the moment.
195+
196+
197+
## Contributing
198+
199+
Contributions are always welcome! Open a PR or an issue!
200+
201+
Thank You!
202+
203+
## Resources
204+
205+
- [Chainlink Documentation](https://docs.chain.link/)
206+
- [Ape Documentation](https://docs.apeworx.io/ape/stable/)

ape-config.yaml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
name: apeworx-starter-kit
2+
contracts_folder: contracts
3+
deployments:
4+
ethereum:
5+
rinkeby:
6+
- contract_type: AggregatorV3Interface
7+
address: "0x8A753747A1Fa494EC906cE90E9f37563A8AF630e"
8+
- contract_type: VRFCoordinatorV2
9+
address: "0x6168499c0cFfCaCD319c818142124B7A15E857ab"
10+
- contract_type: LinkToken
11+
address: "0x01be23585060835e02b77ef475b0cc51aa1e0709"
12+
default_ecosystem: ethereum
13+
foundry:
14+
fork:
15+
ethereum:
16+
mainnet:
17+
upstream_provider: alchemy

contracts/KeepersConsumer.vy

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# SPDX-License-Identifier: MIT
2+
# @version ^0.3.3
3+
4+
counter: public(uint256)
5+
INTERVAL: immutable(uint256)
6+
last_time_stamp: uint256
7+
8+
@external
9+
def __init__(update_interval: uint256):
10+
INTERVAL = update_interval
11+
self.last_time_stamp = block.timestamp
12+
self.counter = 0
13+
14+
@external
15+
@view
16+
def checkUpkeep(checkData: Bytes[1]) -> (bool, Bytes[1]):
17+
upkeep_needed: bool = (block.timestamp - self.last_time_stamp) > INTERVAL
18+
return(upkeep_needed, b"\x00")
19+
20+
@external
21+
def performUpkeep(calldata: Bytes[1]):
22+
upkeep_needed: bool = (block.timestamp - self.last_time_stamp) > INTERVAL
23+
assert upkeep_needed, "upkeep not needed!"
24+
self.last_time_stamp = block.timestamp
25+
self.counter = self.counter + 1

contracts/PriceConsumer.vy

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# SPDX-License-Identifier: MIT
2+
# @version ^0.3.3
3+
import interfaces.AggregatorV3Interface as AggregatorV3Interface
4+
5+
price_feed: public(AggregatorV3Interface)
6+
7+
@external
8+
def __init__(_price_feed_address: address):
9+
self.price_feed = AggregatorV3Interface(_price_feed_address)
10+
11+
@external
12+
@view
13+
def get_latest_price() -> int256:
14+
a: uint80 = 0
15+
price: int256 = 0
16+
b: uint256 = 0
17+
c: uint256 = 0
18+
d: uint80 = 0
19+
(a, price, b, c, d) = self.price_feed.latestRoundData()
20+
return price

contracts/VRFConsumerV2.vy

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# SPDX-License-Identifier: MIT
2+
# @version ^0.3.3
3+
import interfaces.VRFCoordinatorV2 as VRFCoordinatorV2
4+
5+
NUM_WORDS: constant(uint32) = 1
6+
REQUEST_CONFIRMATIONS: constant(uint16) = 3
7+
CALLBACK_GAS_LIMIT: constant(uint32) = 100000
8+
9+
vrf_coordinator: public(VRFCoordinatorV2)
10+
subscription_id: uint64
11+
key_hash: bytes32
12+
random_words: public(uint256[NUM_WORDS])
13+
14+
event ReturnedRandomness:
15+
random_words: uint256[NUM_WORDS]
16+
17+
@external
18+
def __init__(_subscription_id: uint64, _vrf_coordinator_address: address, _key_hash: bytes32):
19+
self.vrf_coordinator = VRFCoordinatorV2(_vrf_coordinator_address)
20+
self.subscription_id = _subscription_id
21+
self.key_hash = _key_hash
22+
23+
@external
24+
def request_random_words():
25+
self.vrf_coordinator.requestRandomWords(
26+
self.key_hash,
27+
self.subscription_id,
28+
REQUEST_CONFIRMATIONS,
29+
CALLBACK_GAS_LIMIT,
30+
NUM_WORDS
31+
)
32+
33+
@internal
34+
def fulfillRandomWords(request_id: uint256, _random_words: uint256[NUM_WORDS]):
35+
self.random_words = _random_words
36+
log ReturnedRandomness(_random_words)
37+
38+
# In solidity, this is the equivalent of inheriting the VRFConsumerBaseV2
39+
# Vyper doesn't have inheritance, so we just add the function here
40+
@external
41+
def rawFulfillRandomWords(requestId: uint256, randomWords: uint256[NUM_WORDS]):
42+
assert msg.sender == self.vrf_coordinator.address, "Only coordinator can fulfill!"
43+
self.fulfillRandomWords(requestId, randomWords)
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# @version ^0.3.3
2+
3+
4+
@external
5+
@view
6+
def decimals() -> uint8:
7+
return 0
8+
9+
10+
@external
11+
@view
12+
def description() -> String[1000]:
13+
return ""
14+
15+
16+
@external
17+
@view
18+
def version() -> uint256:
19+
return 0
20+
21+
22+
@external
23+
@view
24+
def getRoundData(_roundId: uint80) -> (uint80, int256, uint256, uint256, uint80):
25+
return (0, 0, 0, 0, 0)
26+
27+
28+
@external
29+
@view
30+
def latestRoundData() -> (uint80, int256, uint256, uint256, uint80):
31+
return (0, 0, 0, 0, 0)
32+
33+
34+
# Inline interface example:
35+
# interface AggregatorV3Interface:
36+
# def decimals() -> uint8: view
37+
# def description() -> String[1000]: view
38+
# def version() -> uint256: view
39+
# def getRoundData(_roundId: uint80) -> (uint80, int256, uint256, uint256, uint80): view
40+
# def latestRoundData() -> (uint80, int256, uint256, uint256, uint80): view
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
2+
# @version ^0.3.3
3+
# https://github.com/smartcontractkit/chainlink/blob/develop/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol
4+
5+
6+
@external
7+
@view
8+
def getRequestConfig() -> (uint16, uint32, Bytes[1000]):
9+
return (0, 0, b"\x00")
10+
11+
12+
@external
13+
def requestRandomWords(keyHash: bytes32, subId: uint64,minimumRequestConfirmations: uint16,callbackGasLimit: uint32,numWords: uint32) -> uint256:
14+
return 0
15+
16+
17+
@external
18+
def createSubscription() -> uint64:
19+
return 0

0 commit comments

Comments
 (0)