Skip to content

Commit

Permalink
init commit
Browse files Browse the repository at this point in the history
  • Loading branch information
PatrickAlphaC committed Oct 15, 2024
0 parents commit 709c0d8
Show file tree
Hide file tree
Showing 15 changed files with 1,509 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
RPC_URL=https://asdfasdfs
# NEVER DO THIS WITH A REAL KEY!!!
# We will show you encryption methods later ;)
PRIVATE_KEY="my_bad_key"
MY_ADDRESS="0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.venv
.env
.password
keystore.json
.keystore.json
.ruff_cache
__pycache__
168 changes: 168 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
# web3py Favorites

This is from the [Cyfrin Updraft Vyper Course]().

- [web3py Favorites](#web3py-favorites)
- [Getting Started](#getting-started)
- [Prerequisites](#prerequisites)
- [Optional prerequisites](#optional-prerequisites)
- [Installation](#installation)
- [uv](#uv)
- [pip/python](#pippython)
- [Quickstart](#quickstart)
- [Deploying with Python](#deploying-with-python)
- [1. Setup Tenderly Virtual Network](#1-setup-tenderly-virtual-network)
- [2. Fund a wallet](#2-fund-a-wallet)
- [3. Get your RPC URL](#3-get-your-rpc-url)
- [The Unsafe Way](#the-unsafe-way)
- [4. Run the unsafe version](#4-run-the-unsafe-version)
- [The Safer Way](#the-safer-way)
- [4. Encrypt your private key](#4-encrypt-your-private-key)
- [5. Run the safe version](#5-run-the-safe-version)
- [Maintainer notes](#maintainer-notes)
- [Build a new requirements.txt](#build-a-new-requirementstxt)


# Getting Started

## Prerequisites

- [uv](https://docs.astral.sh/uv/)
- You'll know you've done it right if you can run `uv --version` and see a version number.
- [git](https://git-scm.com/)
- You'll know you've done it right if you can run `git --version` and see a version number.

### Optional prerequisites

If you're an advanced python user, you can use virtual environments and classic python/pip to work here.

- [python](https://www.python.org/)
- [pip](https://pypi.org/project/pip/)

## Installation

```bash
git clone https://github.com/cyfrin/web3py-favorites-cu
cd web3py-favorites-cu
```

### uv

```bash
uv sync
```

### pip/python

```bash
python -m venv ./venv
source ./venv/bin/activate
pip install -r requirements.txt
```

## Quickstart

```bash
uv run hello.py # for UV
# or
python hello.py # for pip/python
```

# Deploying with Python

## 1. Setup Tenderly Virtual Network

Go to [tenderly](https://dashboard.tenderly.co/) and sign up, and then select `Create Virtual TestNet`.

![Tenderly Virtual Network](./img/virtual_network.png)

Your config should look like this:

![Tenderly Virtual Network](./img/config.png)

## 2. Fund a wallet

Select your network, and hit `Fund Account` and paste in an address.

We recommend using `0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266` since it's a well-known testing address.

![Tenderly Virtual Network](./img/fund.png)

## 3. Get your RPC URL

Create a `.env` file, get your RPC URL from the tenderly dashboard, and add it to your `.env` file.

![Tenderly Virtual Network](./img/RPC.png)

Example `.env`:

```bash
RPC_URL=https://asdfasdfs
```

## The Unsafe Way

### 4. Run the unsafe version

Add your private key to your `.env` file. This should be the private key associated with the account you funded. If you used the address above, use the following:

_example `.env`_
```bash
RPC_URL="your_rpc_url"

# NEVER DO THIS WITH A REAL KEY!!!
PRIVATE_KEY="0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"
MY_ADDRESS="0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"
```

This is OK since the address and private key are well known testing keys.

Then, you can run:

```bash
uv run deploy_favorites_unsafe.py # uv
# or
python deploy_favorites_unsafe.py # pip/python
```

And you'll deploy the contract!

## The Safer Way

## 4. Encrypt your private key

We want you to practice not having your private key in plain text! So run the following:

```bash
uv run encrypt_key.py # uv
# or
python encrypt_key.py # pip/python
```

This will prompt you for a password and private key. We recommend using the following:

```
0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
```

Since this is a well-known testing key, feel free to use an easy password - TYPICALLY YOU SHOULD NEVER SHARE YOUR PRIVATE KEY.

### 5. Run the safe version

```bash
uv run deploy_favorites.py # uv
# or
python deploy_favorites.py # pip/python
```

This will prompt you for a password to decrypt, and then you'll deploy your contract without exposing your private key! Huzzah!

# Maintainer notes

If you're a student, ignore this section!

## Build a new requirements.txt

```bash
uv pip compile pyproject.toml -o requirements.txt
```
75 changes: 75 additions & 0 deletions deploy_favorites.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
from web3 import Web3
from dotenv import load_dotenv
from vyper import compile_code
import os
from encrypt_key import KEYSTORE_PATH
from eth_account import Account
import getpass

load_dotenv()

RPC_URL = os.getenv("RPC_URL")


def main():
print("Let's read in the Vyper code and deploy it to the blockchain!")
w3 = Web3(Web3.HTTPProvider(RPC_URL))
with open("favorites.vy", "r") as favorites_file:
favorites_code = favorites_file.read()
compliation_details = compile_code(
favorites_code, output_formats=["bytecode", "abi"]
)

chain_id = 31337 # Make sure this matches your virtual network!

print("Getting environment variables...")
my_address = os.getenv("MY_ADDRESS")

# private_key = os.getenv("PRIVATE_KEY")
private_key = decrypt_key()

# Create the contract in Python
favorites_contract = w3.eth.contract(
abi=compliation_details["abi"], bytecode=compliation_details["bytecode"]
)

# Submit the transaction that deploys the contract
nonce = w3.eth.get_transaction_count(my_address)

# We could do this next line as a shortcut :)
# tx_hash = favorites_contract.constructor().transact()

print("Building the transaction...")
transaction = favorites_contract.constructor().build_transaction(
{
"chainId": chain_id,
# "gasPrice": w3.eth.gas_price,
"gasPrice": 1,
"from": my_address,
"nonce": nonce,
}
)

print("Signing transaction...")
signed_txn = w3.eth.account.sign_transaction(transaction, private_key=private_key)
print("We signed it, check it out:")
print(signed_txn)

print("Deploying Contract!")
tx_hash = w3.eth.send_raw_transaction(signed_txn.raw_transaction)
print("Waiting for transaction to finish...")
tx_receipt = w3.eth.wait_for_transaction_receipt(tx_hash)
print(f"Done! Contract deployed to {tx_receipt.contractAddress}")


def decrypt_key() -> str:
with open(KEYSTORE_PATH, "r") as fp:
encrypted_account = fp.read()
password = getpass.getpass("Enter your password for your keystore.json:\n")
key = Account.decrypt(encrypted_account, password)
print("Decrypted key!")
return key


if __name__ == "__main__":
main()
61 changes: 61 additions & 0 deletions deploy_favorites_unsafe.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
from web3 import Web3
from dotenv import load_dotenv
from vyper import compile_code
import os

load_dotenv()

RPC_URL = os.getenv("RPC_URL")


def main():
print("Let's read in the Vyper code and deploy it to the blockchain!")
w3 = Web3(Web3.HTTPProvider(RPC_URL))
with open("favorites.vy", "r") as favorites_file:
favorites_code = favorites_file.read()
compliation_details = compile_code(
favorites_code, output_formats=["bytecode", "abi"]
)

chain_id = 31337 # Make sure this matches your virtual network!

print("Getting environment variables...")
my_address = os.getenv("MY_ADDRESS")
private_key = os.getenv("PRIVATE_KEY")

# Create the contract in Python
favorites_contract = w3.eth.contract(
abi=compliation_details["abi"], bytecode=compliation_details["bytecode"]
)

# Submit the transaction that deploys the contract
nonce = w3.eth.get_transaction_count(my_address)

# We could do this next line as a shortcut :)
# tx_hash = favorites_contract.constructor().transact()

print("Building the transaction...")
transaction = favorites_contract.constructor().build_transaction(
{
"chainId": chain_id,
# "gasPrice": w3.eth.gas_price,
"gasPrice": 1,
"from": my_address,
"nonce": nonce,
}
)

print("Signing transaction...")
signed_txn = w3.eth.account.sign_transaction(transaction, private_key=private_key)
print("We signed it, check it out:")
print(signed_txn)

print("Deploying Contract!")
tx_hash = w3.eth.send_raw_transaction(signed_txn.raw_transaction)
print("Waiting for transaction to finish...")
tx_receipt = w3.eth.wait_for_transaction_receipt(tx_hash)
print(f"Done! Contract deployed to {tx_receipt.contractAddress}")


if __name__ == "__main__":
main()
23 changes: 23 additions & 0 deletions encrypt_key.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from eth_account import Account
import getpass
from pathlib import Path
import json

KEYSTORE_PATH = Path(".keystore.json")


def main():
# input for your private key
private_key = getpass.getpass("Enter your private key:\n")
my_account = Account.from_key(private_key)

password = getpass.getpass("Enter a password:\n")
encrypted_account = my_account.encrypt(password)

print(f"Saving to {KEYSTORE_PATH}...")
with KEYSTORE_PATH.open("w") as fp:
json.dump(encrypted_account, fp)


if __name__ == "__main__":
main()
30 changes: 30 additions & 0 deletions favorites.vy
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# SPDX-License-Identifier: MIT
# pragma version 0.4.0

my_favorite_number: uint256

struct Person:
favorite_number: uint256
name: String[100]

# Static Array/List
list_of_people: public(Person[5])
list_of_people_index: uint256

name_to_favorite_number: HashMap[String[100], uint256]

@external
def store(favorite_number: uint256):
self.my_favorite_number = favorite_number

@external
@view
def retrieve() -> uint256:
return self.my_favorite_number

@external
def add_person(name: String[100], favorite_number: uint256):
new_person: Person = Person(favorite_number= favorite_number, name=name)
self.list_of_people[self.list_of_people_index] = new_person
self.list_of_people_index += 1
self.name_to_favorite_number[name] = favorite_number
6 changes: 6 additions & 0 deletions hello.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
def main():
print("Hello from web3py-simple-storage-cu!")


if __name__ == "__main__":
main()
Binary file added img/RPC.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added img/config.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added img/fund.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added img/virtual_network.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[project]
name = "web3py-simple-storage-cu"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.11"
dependencies = [
"python-dotenv>=1.0.1",
"vyper>=0.4.0",
"web3>=7.2.0",
]
Loading

0 comments on commit 709c0d8

Please sign in to comment.