diff --git a/apps/academy/src/content/intro-to-ethereum/1.mdx b/apps/academy/src/content/intro-to-ethereum/1.mdx new file mode 100644 index 00000000..e57e4161 --- /dev/null +++ b/apps/academy/src/content/intro-to-ethereum/1.mdx @@ -0,0 +1,432 @@ +--- +title: A Developer's Guide to Ethereum, Pt. 1 +description: Introduction to Ethereum with web3.py and Python. +icons: ["ethereum", "etherscan"] +--- + +import { LessonHeader } from "../../../components/mdx/LessonHeader"; +import Layout from "../../../components/Layout"; + + + + +So, you’ve heard about this [Ethereum](https://ethereum.org/) thing and are +ready to venture down the rabbit hole? This post will quickly cover some +blockchain basics, then get you interacting with a simulated Ethereum node – +reading block data, checking account balances, and sending transactions. Along +the way, we’ll highlight the differences between traditional ways of building +apps and this new decentralized paradigm. Some code is included, but the focus +is on the concepts, not a final product. + +### **(Soft) prerequisites** + +This post aspires to be accessible to a wide range of developers. Python tools +will be involved, but they are just a vehicle for the ideas. Reproducing the +code is not required, but may help the ideas sink in. For those coding along, +I'm making just a few assumptions about what you already know, so we can quickly +move on the Ethereum-specific bits. + +Assumptions, if you are going to reproduce the code: + +- you can get around in a terminal, +- you've written a few lines of Python code, +- Python version 3.7 or greater is installed on your machine (use of + a [virtual environment](https://realpython.com/effective-python-environment/#virtual-environments) is + strongly encouraged), and +- you’ve used `pip`, Python’s package installer. + +Again, if any of these are untrue, just follow along conceptually. + +### **Blockchains, briefly** + +There are many ways to describe Ethereum, but at its heart is a blockchain. +Blockchains are made up of a series of blocks, so let’s start there. In the +simplest terms, each block on the Ethereum blockchain is some metadata and a +list of transactions. In JSON format, that looks something like this: + +```python +{ + "number": 1234567, + "hash": "0xabc123...", + "parentHash": "0xdef456...", + ..., + "transactions": [...] +} +``` + +Each block has a reference to the block that came before it; the `parentHash` is +simply the hash of the previous block. + + + **Note:** Ethereum makes regular use of hash functions to produce fixed-size values (“hashes”). + Hashes play an important role in Ethereum, but you can safely think of them as unique IDs for now. + + +![https://cdn-images-1.medium.com/max/2400/1*WfNXz5Svsxk9yB-PEIM9BQ.png](https://cdn-images-1.medium.com/max/2400/1*WfNXz5Svsxk9yB-PEIM9BQ.png) + +A blockchain is essentially a linked list; each block has a reference to the +previous block. + +This data structure is nothing novel, but the rules (i.e., peer-to-peer +protocols) that govern the network are. There’s no central authority; the +network of peers must collaborate to sustain the network, and compete to decide +which transactions to include in the next block. So, when you want to send some +asset to a friend, you’ll need to broadcast that transaction to the network, +then wait for it to be included in an upcoming block. + +The only way for the blockchain to verify that asset was truly sent from one +user to another is to use assets native to (i.e., created and governed by) that +blockchain. In Ethereum, this native currency is called **ether**, and the +Ethereum blockchain contains the only official record of account balances. + +### **A new paradigm** + +This new decentralized tech stack has spawned new developer tools. Such tools +exist in many programming languages, but we’ll be looking through the Python +lens. To reiterate: even if Python isn’t your language of choice, it shouldn’t +be much trouble to follow along. + +Python developers that want to interact with Ethereum are likely to reach +for **[web3.py](https://web3py.readthedocs.io/)**. web3.py is a library that +greatly simplifies the way you connect to an Ethereum node, then send and +receive data from it. + + + **Note:** “Ethereum node” and “Ethereum client” are often used interchangeably. In either case, it + refers to the software that a participant in the Ethereum network runs. This software can read + block data, receive updates when new blocks are added to the chain, broadcast new transactions, + and more. + + +Ethereum clients can be configured to be reachable by IPC, HTTP, or Websockets. +web3.py refers to these connection options as **providers**. You’ll want to +choose one of the three providers to configure so that web3.py knows how to +communicate with your node. + +![https://cdn-images-1.medium.com/max/1600/1*OrElsXOF45w-AgBuezCqjQ.png](https://cdn-images-1.medium.com/max/1600/1*OrElsXOF45w-AgBuezCqjQ.png) + +The Ethereum node and web3.py need to be configured to communicate via the same +protocol, e.g., IPC in this diagram. + +Once web3.py is properly configured, you can begin to interact with the +blockchain. Here’s a couple of web3.py usage examples as a preview of what’s to +come: + +```python +# read block data: +w3.eth.get_block('latest') + +# send a transaction: +w3.eth.send_transaction({'from': ..., 'to': ..., 'value': ...}) +``` + +### **Installation** + +In this walkthrough, we’ll just be working within a Python interpreter. We won't +be creating any directories, files, classes or functions. + + + **Note:** In the examples below, commands that begin with `$` are intended to be run in the + terminal. (Do not type the `$`, it just signifies the start of the line.) + + +First, install [IPython](https://ipython.org/) for a user-friendly environment +to explore in. IPython offers tab completion, among other features, making it +much easier to see what’s possible within web3.py. + +```bash +$ pip install ipython +``` + +web3.py is published under the name `web3`. Install the latest version like so: + +```bash +$ pip install web3 +``` + +One more thing – we're going to simulate a blockchain later, which requires a +couple more dependencies. You can install those via: + +```bash +$ pip install "web3[tester]" +``` + +You’re all set up to go! + +### **Spin up a sandbox** + +Open up a new Python environment by running `ipython` in your terminal. This is +comparable to running `python`, but comes with more bells and whistles. + +```bash +$ ipython +``` + +This will print out some information about the versions of Python and IPython +you’re running, then you should see a prompt waiting for input: + +```python +In [1]: +``` + +You’re looking at an interactive Python shell now. Essentially, its a sandbox to +play in. If you’ve made it this far, its time to import web3.py: + +```python +In [1]: from web3 import Web3 +``` + +### **Introducing the Web3 module** + +Besides being a gateway to Ethereum, +the [Web3](https://web3py.readthedocs.io/en/stable/overview.html#base-api) module +offers a few convenience functions. Let’s explore a couple. + +In an Ethereum application, you will commonly need to convert currency +denominations. The Web3 module provides a couple of helper methods just for +this: [from_wei](https://web3py.readthedocs.io/en/stable/web3.main.html#web3.Web3.from_wei) and [to_wei](https://web3py.readthedocs.io/en/stable/web3.main.html#web3.Web3.to_wei). + + +**Note:** Computers are notoriously bad at handling decimal math. To get around this, developers often store dollar amounts in cents. For example, an item with a price of $5.99 may be stored in the database as 599. A similar pattern is used when handling transactions in ether. However, instead of two decimal points, ether has 18! The smallest denomination of ether is called *wei*, so that’s the value specified when sending transactions. + +1 ether = 1000000000000000000 wei + +1 wei = 0.000000000000000001 ether + + + +Try converting some values to and from wei. Note that there +are [names for many of the denominations](https://web3py.readthedocs.io/en/stable/examples.html#converting-currency-denominations) in +between ether and wei. One of the better known among them is **gwei**, as it’s +often how transaction fees are represented. + +```python +In [2]: Web3.to_wei(1, 'ether') +Out[2]: 1000000000000000000 + +In [3]: Web3.from_wei(500000000, 'gwei') +Out[3]: Decimal('0.5') +``` + +Other utility methods on the Web3 module include data format converters +(e.g., [to_hex](https://web3py.readthedocs.io/en/stable/web3.main.html#web3.Web3.toHex) +), address helpers +(e.g., [is_address](https://web3py.readthedocs.io/en/stable/web3.main.html#web3.Web3.isAddress) +), and hash functions +(e.g., [keccak](https://web3py.readthedocs.io/en/stable/web3.main.html#web3.Web3.keccak) +). Many of these will be covered later in the series. To view all the available +methods and properties, utilize IPython’s auto-complete by typing `Web3.` and +hitting the tab key twice after the period. + +### **Talk to the chain** + +The convenience methods are lovely, but let’s move on to the blockchain. The +next step is to configure web3.py to communicate with an Ethereum node. Here we +have the option to use the IPC, HTTP, or Websocket providers. + +We won't be going down this path, but an example of a complete workflow using +the HTTP Provider might look something like this: + +- Download an Ethereum node, e.g., [Geth](https://geth.ethereum.org/). +- Start Geth in one terminal window and wait for it to sync the network. The + default HTTP port is `8545`, but is configurable. +- Tell web3.py to connect to the node via HTTP, + on `localhost:8545`.`w3 = Web3(Web3.HTTPProvider('http://127.0.0.1:8545'))` +- Use the `w3` instance to interact with the node. + +While this is one “real” way to do it, the syncing process takes hours and is +unnecessary if you just want a development environment. web3.py exposes a fourth +provider for this purpose, the **EthereumTesterProvider**. This tester provider +links to a simulated Ethereum node with relaxed permissions and fake currency to +play with. + +![https://snakecharmers.ethereum.org/content/images/2020/08/Screen-Shot-2020-08-31-at-2.38.23-PM.png](https://snakecharmers.ethereum.org/content/images/2020/08/Screen-Shot-2020-08-31-at-2.38.23-PM.png) + +The EthereumTesterProvider connects to a simulated node and is handy for quick +development environments. + +That simulated node is +called [**eth-tester**](https://github.com/ethereum/eth-tester) and we installed +it as part of the `pip install "web3[tester]"` command. Configuring web3.py to +use this tester provider is as simple as: + +```python +In [4]: w3 = Web3(Web3.EthereumTesterProvider()) +``` + +Now you’re ready to surf the chain! That’s not a thing people say. I just made +that up. Let’s take a quick tour. + +### **The quick tour** + +First things first, a sanity check: + +```python +In [5]: w3.is_connected() +Out[5]: True +``` + +Since we’re using the tester provider, this isn’t a very valuable test, but if +it does fail, chances are you typed something in wrong when instantiating +the `w3` variable. Double-check that you included the inner parentheses, +i.e., `Web3.EthereumTesterProvider()`. + +### **Tour stop #1: accounts** + +As a convenience, the tester provider created some accounts and preloaded them +with test ether. First, let’s see a list of those accounts: + +```python +In [6]: w3.eth.accounts +Out[6]: ['0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf', + '0x2B5AD5c4795c026514f8317c7a215E218DcCD6cF', + '0x6813Eb9362372EEF6200f3b1dbC3f819671cBA69', ...] +``` + +If you run this command, you should see a list of ten strings that begin +with `0x`. Each is a **public address** and is, in some ways, analogous to the +account number on a checking account. You would provide this address to someone +that wanted to send you ether. + +As mentioned, the tester provider has preloaded each of these accounts with some +test ether. Let’s find out how much is in the first account: + +```python +In [7]: w3.eth.get_balance(w3.eth.accounts[0]) +Out[7]: 1000000000000000000000000 +``` + +That’s a lot of zeros! Before you go laughing all the way to the fake bank, +recall that lesson about currency denominations from earlier. Ether values are +represented in the smallest denomination, wei. Convert that to ether: + +```python +In [8]: w3.from_wei(1000000000000000000000000, 'ether') +Out[8]: Decimal('1000000') +``` + +One million test ether — still not too shabby. + +### **Tour stop #2: block data** + +Let’s take a peek at the state of this simulated blockchain: + +```python +In [9]: w3.eth.get_block('latest') +Out[9]: AttributeDict({ + 'number': 0, + 'hash': HexBytes('0x9469878...'), + 'parentHash': HexBytes('0x0000000...'), + ... + 'transactions': [] +}) +``` + +A lot of information gets returned about a block, but just a couple things to +point out here: + +1. The block number is zero — no matter how long ago you configured the tester + provider. Unlike the real Ethereum network, which includes a new block every + 13 seconds, this simulation will wait until you give it some work to do. +2. `transactions` is an empty list, for the same reason: we haven’t done + anything yet. This first block is an **empty block**, just to kick off the + chain. +3. Notice that the `parentHash` is just a bunch of empty bytes. This signifies + that it's the first block in the chain, also known as + the **genesis** **block**. + +### **Tour stop #3: transactions** + +We’re stuck at block zero until there’s a transaction to mine, so let’s give it +one. If you wanted to send a few test ether from one account to another that +code would look something like this: + +```python +In [10]: tx_hash = w3.eth.send_transaction({ + 'from': w3.eth.accounts[0], + 'to': w3.eth.accounts[1], + 'value': w3.to_wei(3, 'ether') +}) +``` + +In the "real" version, this is typically the point where you’d wait for several +seconds for your transaction to get included in a new block. The full process +goes something like this: + +1. Submit a transaction and hold on to the transaction hash. Until it's + included, the transaction is “pending.” + `tx_hash = w3.eth.send_transaction({ ... })` +2. Wait for the transaction to be included: + `w3.eth.wait_for_transaction_receipt(tx_hash)` +3. To view the successful transaction: `w3.eth.get_transaction(tx_hash)` + +Our simulated environment will add the transaction in a new block instantly, so +we can immediately view the transaction: + +```python +In [11]: w3.eth.get_transaction(tx_hash) +Out[11]: AttributeDict({ + 'hash': HexBytes('0x15e9fb95dc39...'), + 'blockNumber': 1, + 'transactionIndex': 0, + 'from': '0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf', + 'to': '0x2B5AD5c4795c026514f8317c7a215E218DcCD6cF', + 'value': 3000000000000000000, + ... +}) +``` + +You’ll see some familiar details here: the `from`, `to`, and `value` fields +should match the inputs of our `send_transaction` call. The other reassuring bit +is that this transaction was included as the first transaction +(`'transactionIndex': 0`) within block number `1`. + +We can also easily verify the success of this transaction by checking the +balances of the two accounts involved. Three ether should have moved from one to +another. + +```python +In [12]: w3.eth.get_balance(w3.eth.accounts[0]) +Out[12]: 999996999999999999979000 + +In [13]: w3.eth.get_balance(w3.eth.accounts[1]) +Out[13]: 1000003000000000000000000 +``` + +The latter looks good! The balance went from 1,000,000 to 1,000,003 ether. But +what happened to the first account? It appears to have lost slightly more than +three ether. Alas, nothing in life is free, and using the Ethereum public +network requires that you compensate your peers for their supporting role. A +small transaction fee was deducted from the account making the transaction to +the tune of 21000 wei. + + + **Note**: On the public network, transaction fees are variable based on network demand and how + quickly you'd like a transaction to be processed. + + + +### **And breathe** + +This seems as good a place as any to take a break. The rabbit hole continues on, +and we’ll continue exploring in part two of this series. Some concepts to come: +a deeper look into accounts, smart contracts, tooling and how to stake out on +your own as an Ethereum developer. Have follow-up questions? Requests for new +content? Let me know on [Twitter](https://twitter.com/wolovim). + + + **Note**: This series is being converted from the [Snake + Charmers](https://snakecharmers.ethereum.org/) blog. + + +import { ContributorFooter } from "../../../components/mdx/ContributorFooter"; + + + diff --git a/apps/academy/src/content/intro-to-ethereum/2.mdx b/apps/academy/src/content/intro-to-ethereum/2.mdx new file mode 100644 index 00000000..c5af72a1 --- /dev/null +++ b/apps/academy/src/content/intro-to-ethereum/2.mdx @@ -0,0 +1,286 @@ +--- +title: A Developer's Guide to Ethereum, Pt. 2 +description: Introduction to Ethereum with web3.py and Python. +icons: ["ethereum"] +--- + +import { LessonHeader } from "../../../components/mdx/LessonHeader"; +import Layout from "../../../components/Layout"; + + + + +Welcome back! In the first installment of this series we covered a lot of ground +while interacting with a simulated Ethereum network. At this point, you should +have at least a fuzzy idea of how to answer these questions: + +- What's a blockchain and what’s in a block? +- What makes Ethereum decentralized? +- What is ether and why is it a necessary component of the network? + +In this lesson, we’ll build on these concepts and explore some of the implications +for developers, so return to Part 1 if you skipped it or need a refresher. + +## What’s next? + +We’re going to take a deeper look at how you can interact with the Ethereum network, starting with accounts. There are some significant differences between Ethereum accounts and Web 2.0 accounts. + + + **Note**: “Web 2.0” was coined to describe the Internet era that introduced user-generated + content, e.g., social media and blogs. Ethereum and other decentralized technologies are said to + be a part of the next generation of the Internet: **Web 3.0**. The abbreviation, **Web3**, is used + by libraries like web3.js and web3.py, and elsewhere around the ecosystem. + + +## Web2 vs. Web3 + +It’s hard not to collect accounts in today’s web. You have one for each social media app, news site, delivery service, retailer, and transit provider — to name a few. Each of these accounts lives on the company's servers, which makes you subject to their terms and conditions, privacy policy, and security practices. Your account can be frozen, deleted, censored, or altered at the discretion of the host company. + +Web3 represents a paradigm shift for account management: you and you alone own your Ethereum accounts. When you create an account, it’s done outside of the scope of any company and can travel with you to multiple apps. In fact, creating an Ethereum account doesn’t require interaction with the Ethereum blockchain at all. Let’s create one now to prove it. + + + **Note**: This exercise is purely for educational purposes. Don't store real value in an account + unless you understand the security implications. Some mistakes cannot be undone! More context to + come. + + +## Creating an account + +Same drill as last time: the concepts will be demonstrated in an IPython shell. If you’re not a Python developer, no problem. Just follow along conceptually. + +### Environment setup + +Three steps to set the stage: + +1. Install web3.py, eth-tester, and IPython (if you didn't already in Part 1): + +```bash +$ pip install web3 "web3[tester]" ipython +``` + +2. Start up a new sandbox: + +```bash +$ ipython +``` + +3. Import the Web3 module: + +```python +In [1]: from web3 import Web3 +``` + +### Account generation + +Let's create that account: + +```python +In [2]: w3 = Web3() # No provider necessary for this demo + +In [3]: acct = w3.eth.account.create() + +# public address: +In [4]: acct.address +Out[4]: '0x33736Bf0Ac7A046eAC36648ca852B91EAe5f567A' + +# private key: +In [5]: acct.key +Out[5]: HexBytes('0x7aca78f5e54...') +``` + +That's all there is to it! There's no registration process and no round-trip to the blockchain or any server. In fact, you can disconnect from the Internet altogether and still create a valid Ethereum account. + +In the code above, you'll find two components of an account: a public address and a private key. Put simply, a **private key** is the password for an account. A **public address** is a shareable account number derived from the private key. As seen in the code sample, both are typically represented as hexadecimal numbers. + +## Using an account + +This is an important takeaway: **the only way to affect a change on the blockchain is via a transaction** and every transaction must be signed by an account. Accounts can initiate transactions which transfer ether, deploy smart contracts, or interact with contracts to do things like mint new tokens. Let's briefly explore each. + +### Transferring ether + +Do you recall that `EthereumTesterProvider` enables a test environment seeded with accounts and fake ether? Let's start by viewing some test accounts and an account balance: + +```python +In [1]: w3 = Web3(Web3.EthereumTesterProvider()) + +In [2]: w3.eth.accounts +Out[2]: ['0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf', + '0x2B5AD5c4795c026514f8317c7a215E218DcCD6cF', + '0x6813Eb9362372EEF6200f3b1dbC3f819671cBA69', ...] + +In [3]: acct_one = w3.eth.accounts[0] + +In [4]: w3.eth.get_balance(acct_one) +Out[4]: 1000000000000000000000000 +``` + +Next, we'll introduce a new account: + +```python +In [5]: acct_two = w3.eth.account.create() + +In [6]: acct_two.address +Out[6]: '0x2FF32Bcc040e98EBa3c0ae2d8ad9C451a78d3E24' + +In [7]: acct_two.key +Out[7]: HexBytes('0x02af55504048265...f70e9965a3505ea') +``` + +Then send one of those fake ether over to the new account: + +```python +In [8]: tx_hash = w3.eth.send_transaction({ + 'from': acct_one, + 'to': acct_two.address, + 'value': Web3.to_wei(1, 'ether') + }) +``` + +This transaction will execute immediately, but some important details are hidden from view. web3.py is smart enough to know that `EthereumTesterProvider` is managing `acct_one` and that we're using a test environment. For our convenience, `acct_one` is "unlocked", meaning transactions from the account are approved (signed) by default. + +So, what does a transaction look like if not from an unlocked account? To find out, let's send some ether from `acct_two` – an account not managed by `EthereumTesterProvider`. This more manual process takes three steps: 1) specify the transaction details, 2) sign the transaction, then 3) broadcast the transaction to the network. + +```python +# 1) manually build a transaction +In [9]: tx = { + 'to': acct_one, + 'value': 10000000, + 'gas': 21000, + 'gasPrice': w3.eth.get_block('pending')['baseFeePerGas'], + 'nonce': 0 + } + +# 2) sign the transaction with the sender's private key +In[10]: signed = w3.eth.account.sign_transaction(tx, acct_two.key) + +# 3) send the "raw" transaction +In[11]: tx_hash = w3.eth.send_raw_transaction(signed.rawTransaction) +``` + +Let's break that down. Step 1 defines a Python dictionary with the required transaction fields (A dictionary, or `dict`, is a Python type similar to an object in other programming languages). We briefly learned about gas (transaction fees) in Part 1, but nonce may be new territory. In Ethereum, a **nonce** is simply the transaction count of the account. The Ethereum protocol keeps track of this value to prevent double-spending. + +Since this is the first transaction being made by `acct_two`, its nonce is zero. If you supply the wrong value, the result is an invalid transaction and is rejected by web3.py: + +```bash +ValidationError: Invalid transaction nonce: Expected 0, but got 4 +``` + +Note that a nonce is still required when sending a transaction from `acct_one`, but `EthereumTesterProvider` keeps track of transaction counts for managed accounts and adds the appropriate nonce to new transactions. + +Another detail you may have noticed is that a `from` value is missing from the `tx` dict. In this case, the `sign_transaction` method can infer the sender's address from their private key. Again, a public address can be derived from a private key, but a private key cannot be reverse engineered from its public address. + +Finally, a "raw" transaction is just the transaction data and signature represented as bytes. Under the hood, `send_transaction` performs the same encoding required by `send_raw_transaction`. + +### Deploying smart contracts + +A full introduction to smart contracts will be covered in the next lesson, but this is a good opportunity for a sneak peak; interacting with smart contracts looks very similar to a standard transaction. + +Briefly, **smart contracts** are programs that live on the Ethereum blockchain, available for anyone to use. When you're ready to deploy a smart contract, you compile the code down to bytecode and include it in a transaction as a `data` value: + +```python +bytecode = "6080604052348015610...36f6c63430006010033" + +tx = { + 'data': bytecode, + 'value': 0, + 'nonce': 0, + ... +} +``` + +Besides requiring more gas, the only other distinction in a contract deployment transaction is the absence of a `to` value. Intuitively, this makes some sense: if you're not sending value to another user or interacting with a deployed smart contract, the only option remaining is the deployment of a new contract which does not yet have a public address. The rest of the process is identical to a standard transfer of ether. + +### Interacting with smart contracts + +Using a deployed contract is just another variation of the same transaction format. In this case, the `to` value points to the deployed contract's address and the `data` value will vary based on the inputs of the contract method being executed. + +Note that tools like web3.py offer more intuitive interfaces for deploying and interacting with contracts: + +```python +# interact with an existing contract: +myContract = web3.eth.contract(address=address, abi=abi) +twentyone = myContract.functions.multiply7(3).call() + +# deploy a new contract: +Example = w3.eth.contract(abi=abi, bytecode=bytecode) +tx_hash = Example.constructor().transact() +``` + +### Signing messages + +Transactions are the only way to affect the state of the blockchain, but they aren't the only way an account can be utilized. Simply proving ownership of a particular account can be useful in its own right. + +As an example, an NFT marketplace may allow you to bid on items for sale by signing a message with your account. Only when an auction expires or the seller accepts your offer is an actual transaction executed. (Important caveat: in order to execute a trade, you will have to have already approved the marketplace's ability to move your tokens.) The same marketplace can also use signed messages as a form of authentication before showing you some account details. + +Unlike transactions, signed messages cost nothing. They aren't broadcast to the network and aren't included in a block. A message is simply a bit of data signed with a private key. As you would expect, the private key of the sender remains hidden, but the receiver can mathematically prove the public address of the sender. In other words, the message sender can't be spoofed. + + + **Note**: the terms "onchain" and "offchain" are shorthand for whether data lives on the Ethereum + blockchain. For example, smart contract state is managed *onchain*, but message signing occurs + *offchain*. + + +We'll save a deep dive into message signing for a future lesson, but here's some pseudocode to give you an idea of the workflow: + +```python +# 1. write a message +msg = "amanaplanacanalpanama" + +# 2. sign it with an account's private key +pk = b"..." +signed_message = sign_message(message=msg, private_key=pk) + +# 3. send `signed_message` over the wire + +# 4. message receiver decodes sender's public address +sender = decode_message_sender(msg, signed_message.signature) +print(sender) +# '0x5ce9454...b9aB12E' +``` + +## Web3 account implications + +So, we can create Ethereum accounts with surprising ease: offline and separate from any app. Those accounts can be used to sign messages or send various flavors of transactions. What does this mean for app developers? + +### Permanent passwords + +A harsh reality of this world is that there are no password recovery services for basic account types. If you lose your private key and recovery phrase, you can kiss that account goodbye. Such is the double-edged sword of true ownership. App developers have an ethical obligation to onboard and educate Ethereum newcomers on this reality. (Note: social recovery wallets may improve this user experience and "account abstraction" is one related research effort – topics for another lesson.) + +### Onboarding challenges + +Introducing new users to Ethereum is complicated. As you've been learning, there are a number of paradigm shifts that aren't immediately obvious. You may have to guide visitors with no Ethereum account yet or users with no ether to pay for transaction fees. The amount of education material required will depend on your audience, but the whole ecosystem will benefit if you're able to onboard new users gracefully. + +### Fewer account management features + +Given that users create accounts outside of your app, you may discover that your use case requires few or no account management features at all. + +### New business models + +Data mining isn't going away, but this new account ownership model enables a healthy alternative to the Web 2.0 model in which a company owns every bit of a user's data and sells it to the highest bidder. Ethereum's smart contract platform offers a world of new incentive structures. + +### New software architectures + +An interesting wrinkle in the definition of your business model will be what to handle onchain vs. offchain. As we've discussed, message signing requires no onchain interaction. There's also nothing stopping you from using a private database for some of your data and the Ethereum blockchain for other bits of data or functionality. There are plenty of tradeoffs to consider: usability, cost, transparency, decentralization, privacy, and so on. + +## And breathe + +Did all that sink in? Test yourself: + +
+ + +There's no limit to the number of accounts you can generate and you're free to use the same account for several apps or create a new account for every app. This is partly what is meant when a public blockchain is described as **permissionless**: there is no gatekeeper between you and the network. Don't wait for anyone to give you permission to build. + +When you're ready to forge ahead, Part 3 more deeply introduces the next actor in the system: smart contracts. + +import { ContributorFooter } from "../../../components/mdx/ContributorFooter"; + + +
diff --git a/apps/academy/src/content/intro-to-ethereum/3.mdx b/apps/academy/src/content/intro-to-ethereum/3.mdx new file mode 100644 index 00000000..bb40a7ba --- /dev/null +++ b/apps/academy/src/content/intro-to-ethereum/3.mdx @@ -0,0 +1,198 @@ +--- +title: A Developer's Guide to Ethereum, Pt. 3 +description: Introduction to Ethereum with web3.py and Python. +icons: ["ethereum"] +--- + +import { LessonHeader } from "../../../components/mdx/LessonHeader"; +import Layout from "../../../components/Layout"; + + + + +Welcome to Part 3 in the saga! Part 1 covered blockchain fundamentals. Part 2 continued on to examine Ethereum accounts and how they enable participation in the network. Part 3 will build on those concepts with the introduction of smart contracts. + +## Smart contracts, an introduction + +The Ethereum blockchain has a great deal of value flowing through it. In the previous lesson, we discussed the flow of **ether** from one user to another via transactions. However, the network is capable of more sophisticated interactions, and those use cases are enabled by smart contracts. + +Let's start with a simple definition: a **smart contract** is code (i.e., a computer program) deployed to the blockchain. As for the buzzword name, _contract_ conveys the relative permanence of these programs as they determine how assets can change hands and _smart_ is a nod to their programmability. For brevity, they're commonly referred to as just contracts, so we'll do the same in this lesson. + +It may be helpful to think of contracts and individual accounts as the two types of actors within this system. With their programmed instructions, contracts can interact with the blockchain in much the same way as individual accounts: by sending and receiving ether or by interacting with other contracts. Contracts can go a step further by managing some internal state – a concept we'll explore shortly. + + + Note: Over the years, individual accounts have been described in a variety of ways. **Externally + owned account** (EOA) is the original term as defined in the Ethereum Whitepaper. You are likely + to see that acronym again. + + +A contract can be as complex or as simple as you need it to be. You can leave the contract open for the world to use, restrict its usage to only your account, or require a certain balance or the ownership of a particular asset to interact with it. Either way, if your contract is deployed to Ethereum Mainnet, that code is public! + +## Public source code? + +In this paradigm, that's a requirement. Users (or other contracts) may utilize your contract to move real value around. They need to be able to trust that your contract does what you say it does, and in order to do that, they need to be able to read it for themselves. + +In reality, most users aren't reading the source code of each smart contract they interact with, but most wisely won't touch a contract if that source code isn't verified (e.g., on Sourcify or Etherscan) and vetted (e.g., audited) by industry veterans. + +Consider the alternative: if contracts are black boxes, there's nothing to stop a bad actor from publishing a contract that appears harmless, but actually grants themselves the ability to move your assets. Today, bad actors can deploy such a contract and try to lure users in via social engineering, but often wallet interfaces will warn users when code is unverified and to proceed with caution. + +## What about my business model? + +Are you wondering how to preserve your competitive edge if all your smart contract code is open source? Public blockchains do force you to get creative here. + +It's not necessarily a more restrictive landscape though. Because each contract is open source, you have the ability to build a platform that others can build on top of, or you can build on top of what others have already done. Safe, for example, is an open source multisig wallet with a rich ecosystem of auxiliary financial tools being built around it. Anyone can build a product that's compatible with a Safe, without any sort of permission from the Safe team. + +Open source licenses also vary widely. Another prominent player in the industry, Uniswap, introduced a product with a unique time-delayed license. Their code was instantly available as open source software, but restricted in its commercial reuse for two years. Creative licensing will surely continue to be explored in this domain. + +## What does a contract look like? + +Ethereum smart contracts can be written in a handful of programming languages, each specially created for the purpose. Each have tradeoffs, but any will do the trick; in the end, the code just needs to compile down to bytecode that the EVM (Ethereum Virtual Machine) can read. Popular language options include Solidity, Vyper, and Fe. + +Each language is worth a look, but below is a "Hello, World"-style example written in Solidity, the most established of the languages. This example contract is titled `Billboard`, stores a single message, and contains one function to update that message. As written, anyone has the unrestricted ability to update that message. + +Imagine a website is displaying whatever message is stored and providing an input to type in a new message, replacing the current one. The combination of a smart contract and its user interface is what's referred to as a decentralized application, or "dapp" for short. + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity 0.8.23; + +contract Billboard { + string public message; + + constructor(string memory _message) { + message = _message; + } + + function writeBillboard(string memory _message) public { + message = _message; + } +} +``` + +If you're familiar with object-oriented programming, a contract will look an awful lot like the concept of a class. In effect, when a contract is deployed, a single instance becomes available for all the world to use — analogous to a "singleton" class. + +For all users, a deployed contract has a particular state at any given block in the blockchain. In other words, the Billboard's `message` is always the same for everyone, until someone updates it. A contract's state can keep track of all manner of things; within a token contract, for example, the state might include who owns how many of which assets. + +In a Solidity contract, the `constructor` method is executed only once, when the contract is first deployed. Continuing the class analogy, the `constructor` might remind you of the `__init__` method within a Python class or similar initialization methods in other languages. So, whoever deploys this contract gets to determine the starting billboard message. + +You may have noticed the JavaScript-like syntax of Solidity, including the use of camelCasing, semicolons, and inline comments. A few notable differences exist as well: the type system, a compiler version declaration, and new keywords. Hopefully this example was simple enough to convey the concepts, but the intricacies of the language are beyond the scope of this lesson. Continue learning in Academy to develop those skills. + +## How does a contract get on the blockchain? + +Earlier in this series, do you recall reading that the only way to make changes to the state of the Ethereum blockchain is via transactions? That remains true for deploying new contracts. + +While a contract is being written, developers will frequently compile their code for manual or automated testing. The output of each compilation is the contract's **bytecode**. + +To deploy a contract, one needs only to send a transaction with that contract's bytecode in the `data` field of the transaction and omit the `to` address. The EVM will take care of the rest. Once the transaction has been included in a block, the transaction receipt contains the deployed contract's address where it can be interacted with. + +```python +tx = { + "from": your_account, + "data": "0x60abc...", + ... +} + +w3.eth.send_transaction(tx) +``` + +Tools like web3.py offer slightly more human-friendly ways to go about this. One of the other outputs of a compilation is the contract's **ABI**, and some additional metadata. + + + **Note**: ABI stands for **Application Binary Interface**. An ABI is a machine-readable data blob + that conveys how a contract can be interacted with – which functions are available and expected + data types. It's some JSON that you pass into an Ethereum library (e.g., web3.py, ethers.js, + etc.), so that it can provide you with a human-friendly interface. Does the name make sense now? + An ABI communicates the interface for your application's bytecode. + + +Once web3.py is aware of a contract's ABI and bytecode (or, if already deployed, the contract address), the library can give you a more intuitive interface for interacting with the contract. + +```python +# Instantiate a contract factory: +Billboard = w3.eth.contract(abi=abi, bytecode=bytecode) + +# Deploy an instance of the contract: +tx_hash = Billboard.constructor("eth very wow").transact() + +# Wait for the transaction to be included and get the receipt: +tx_receipt = w3.eth.wait_for_transaction_receipt(tx_hash) + +# Retrieve the deployed contract instance: +billboard = w3.eth.contract( + address=tx_receipt.contractAddress, + abi=abi +) + +# Interact with the contract instance: +billboard.functions.message().call() +# eth very wow + +billboard.functions.writeBillboard("sneks everywhere").transact() + +billboard.functions.message().call() +# sneks everywhere +``` + +## How far can a contract go? + +So long as we're talking about the management of digital assets, you can program nearly anything. Physical assets have been tokenized on the blockchain too, but that's another rabbit hole. + +Over the years, standards for various digital assets have proposed, debated, and agreed upon, providing some foundational building blocks for more complex contracts. Among the most notable are the ERC-20 token standard (i.e., fungible tokens) and the ERC-721 standard (i.e., non-fungible tokens or "NFTs"). + + + **Note**: To save you the web search, _fungible_ means interchangeable or indistinguishable. In + other words, if you own 100 fungible tokens, it doesn't make any difference which 100 tokens they + are. NFTs, on the other hand, may each have unique qualities, so the particular token you own is + significant. + + +To demystify those standards: the different token types are simply smart contract patterns that anyone can make use of. The ERC-20 standard specifies which functions your fungible token contract must include, but at its core, the contract simply maintains list of public addresses and how many tokens each one owns, represented by an integer. + +The ERC-721 standard overlaps with ERC-20, but importantly introduces a unique token ID and some metadata for each token. + +These two standards are brought up to illustrate their compounding effect. As more of these building blocks are standardized, the easier it gets to quickly stack them in creative ways and innovate at the fringes. + +## Contracts within contracts + +Continuing the comparison to object-oriented classes, _inheritance_ is another concept you will commonly find in contracts. Well-trusted organizations like OpenZeppelin implement token standard contracts, for example, so that contract developers can simply import and inherit that functionality. + +Within Solidity, inheritance is declared using the `is` keyword: + +```solidity +import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; + +contract MyToken is ERC20 { + constructor() ERC20("ExampleToken", "XMPL") { ... } +} +``` + +Once deployed, the `MyToken` contract has access to all the functions defined in OpenZeppelin's `ERC20` contract implementation. Conveniently, you don't have to reinvent the wheel and can focus on what makes your token contract unique. + +Beyond inheritance, contracts have the ability to interact with other deployed contracts or even serve as a _factory_ or _proxy_ for deploying still more contracts! Those concepts are good topics for future lessons. + +## A note on upgradeability + +While the blockchain is said to be immutable, there are patterns of writing smart contracts that can support upgradeability. These patterns introduce trade-offs which may or may not make sense for your use case. The specifics are beyond the scope of this introductory lesson. + +## And breathe + +We covered a lot of ground! Did all that sink in? Test yourself: + +
+ + +For now at least, this concludes the three-part series, _A Developer's Guide to Ethereum_. If you're satisfied with your answers, you've now got a strong foundation on which to begin your dapp-building journey. Continue your next steps in Academy and be sure to document your own journey! Many of the tools in this industry are brand new or rapidly evolving. A great way to make a positive impact and grow your network is simply to help improve the documentation of each tool as you find opportunities to. + +Happy building! ⚡️ + +import { ContributorFooter } from "../../../components/mdx/ContributorFooter"; + + +
diff --git a/apps/academy/src/quizzes/intro-to-ethereum/quiz-eth-intro-1.json b/apps/academy/src/quizzes/intro-to-ethereum/quiz-eth-intro-1.json new file mode 100644 index 00000000..b7ae09d0 --- /dev/null +++ b/apps/academy/src/quizzes/intro-to-ethereum/quiz-eth-intro-1.json @@ -0,0 +1,167 @@ +{ + "title": "Quiz: Intro to Ethereum, Pt. 1", + "questions": [ + { + "question": "What is in a block?", + "options": [ + { + "answer": "The hash of each transaction in that block", + "correct": true + }, + { + "answer": "The email address of the block creator" + }, + { + "answer": "A reference to the previous block", + "correct": true + } + ] + }, + { + "question": "What is novel about blockchains?", + "options": [ + { + "answer": "Their efficiency as a data storage solution." + }, + { + "answer": "A high number of transactions per second." + }, + { + "answer": "The ability to be governed without a central authority.", + "correct": true + } + ] + }, + { + "question": "The native currency of Ethereum is called:", + "options": [ + { + "answer": "Bitcoin" + }, + { + "answer": "ether", + "correct": true + }, + { + "answer": "Ripple" + }, + { + "answer": "Ethereum" + } + ] + }, + { + "question": "The smallest denomination of Ethereum's currency is called:", + "options": [ + { + "answer": "gwei" + }, + { + "answer": "finney" + }, + { + "answer": "wei", + "correct": true + }, + { + "answer": "satoshi" + } + ] + }, + { + "question": "While developing your application, you should:", + "options": [ + { + "answer": "connect to a local test network for faster development cycles", + "correct": true + }, + { + "answer": "connect to Ethereum mainnet and spend real ether until you get it right" + }, + { + "answer": "publish your private key on GitHub" + } + ] + }, + { + "question": "A local test network provides you with:", + "options": [ + { + "answer": "transactions instantly included in new block", + "correct": true + }, + { + "answer": "accounts populated with test ether", + "correct": true + }, + { + "answer": "deployed sample smart contracts" + } + ] + }, + { + "question": "What does a basic transaction include?", + "options": [ + { + "answer": "coinbase, miner, gas" + }, + { + "answer": "sender, block number, amount" + }, + { + "answer": "from, to, value", + "correct": true + } + ] + }, + { + "question": "Sending ether from one account to another requires how much gas?", + "options": [ + { + "answer": "2300" + }, + { + "answer": "10000" + }, + { + "answer": "20000" + }, + { + "answer": "21000", + "correct": true + } + ] + }, + { + "question": "What are transaction fees in Ethereum?", + "options": [ + { + "answer": "An incentive for nodes to include your transaction in a block", + "correct": true + }, + { + "answer": "A fee paid to the Ethereum Foundation" + }, + { + "answer": "The cost of executing your transaction on the network", + "correct": true + } + ] + }, + { + "question": "A sent transaction is in a pending state, meaning what?", + "options": [ + { + "answer": "The transaction isn't executed until a node includes it in a valid block.", + "correct": true + }, + { + "answer": "The sender has a limited opportunity to spend double the amount." + }, + { + "answer": "The sender needs to be authorized before the transaction is valid." + } + ] + } + ] +} diff --git a/apps/academy/src/quizzes/intro-to-ethereum/quiz-eth-intro-2.json b/apps/academy/src/quizzes/intro-to-ethereum/quiz-eth-intro-2.json new file mode 100644 index 00000000..080a6364 --- /dev/null +++ b/apps/academy/src/quizzes/intro-to-ethereum/quiz-eth-intro-2.json @@ -0,0 +1,190 @@ +{ + "title": "Quiz: Intro to Ethereum, Pt. 2", + "questions": [ + { + "question": "What is meant by the term web3?", + "options": [ + { + "answer": "The third generation of the Internet, characterized by decentralized systems and native digital assets", + "correct": true + }, + { + "answer": "The third iteration of the Internet, characterized by the rise of social media and user-generated content" + }, + { + "answer": "Something related to radioactive spiders" + } + ] + }, + { + "question": "Who can help you if you lose your private key or seed phrase?", + "options": [ + { + "answer": "The Ethereum Foundation support channels" + }, + { + "answer": "Your bank" + }, + { + "answer": "You alone", + "correct": true + } + ] + }, + { + "question": "Who can prevent you from creating an Ethereum account?", + "options": [ + { + "answer": "No one", + "correct": true + }, + { + "answer": "Your bank" + }, + { + "answer": "The Ethereum Foundation" + }, + { + "answer": "Satoshi" + } + ] + }, + { + "question": "Which is the best analogy for a public address?", + "options": [ + { + "answer": "Rain on your wedding day" + }, + { + "answer": "An account number for your account", + "correct": true + }, + { + "answer": "A password for your wallet" + }, + { + "answer": "A savings account at a bank" + } + ] + }, + { + "question": "Which is the best analogy for a private key?", + "options": [ + { + "answer": "Ten thousand spoons when all you need is a knife" + }, + { + "answer": "A phone number for your account" + }, + { + "answer": "A random number generator" + }, + { + "answer": "A password for your account", + "correct": true + } + ] + }, + { + "question": "What can you do with an account?", + "options": [ + { + "answer": "Deploy a smart contract", + "correct": true + }, + { + "answer": "Send and receive ether", + "correct": true + }, + { + "answer": "Interact with a smart contract", + "correct": true + }, + { + "answer": "Sign transactions", + "correct": true + } + ] + }, + { + "question": "In a production environment, web3.py automatically signs and sends your transactions", + "options": [ + { + "answer": "True" + }, + { + "answer": "False", + "correct": true + } + ] + }, + { + "question": "What is a nonce?", + "options": [ + { + "answer": "The transaction count of an account", + "correct": true + }, + { + "answer": "A transaction hash" + }, + { + "answer": "A block hash" + }, + { + "answer": "Random data added to a transaction" + } + ] + }, + { + "question": "A transaction to deploy a new contract omits which value?", + "options": [ + { + "answer": "data" + }, + { + "answer": "from" + }, + { + "answer": "to", + "correct": true + }, + { + "answer": "nonce" + } + ] + }, + { + "question": "What is true of message signing?", + "options": [ + { + "answer": "The signer's private key is revealed" + }, + { + "answer": "No gas fee is required to sign a message", + "correct": true + }, + { + "answer": "The address of the signer can later be proven", + "correct": true + }, + { + "answer": "Message signing may be used to authenticate a user", + "correct": true + } + ] + }, + { + "question": "To build an Ethereum app, your entire technology stack must be open sourced", + "options": [ + { + "answer": "True" + }, + { + "answer": "False", + "correct": true + } + ] + } + ] +} diff --git a/apps/academy/src/quizzes/intro-to-ethereum/quiz-eth-intro-3.json b/apps/academy/src/quizzes/intro-to-ethereum/quiz-eth-intro-3.json new file mode 100644 index 00000000..5e03512e --- /dev/null +++ b/apps/academy/src/quizzes/intro-to-ethereum/quiz-eth-intro-3.json @@ -0,0 +1,177 @@ +{ + "title": "Quiz: Intro to Ethereum, Pt. 3", + "questions": [ + { + "question": "What is a smart contract?", + "options": [ + { + "answer": "A performant database" + }, + { + "answer": "A computer program that is deployed on a blockchain", + "correct": true + }, + { + "answer": "Code that is guaranteed to have no vulnerabilities" + } + ] + }, + { + "question": "What can a smart contract do?", + "options": [ + { + "answer": "Send and receive ether", + "correct": true + }, + { + "answer": "Deploy other smart contracts", + "correct": true + }, + { + "answer": "Interact with other smart contracts", + "correct": true + }, + { + "answer": "Maintain internal state", + "correct": true + } + ] + }, + { + "question": "Before entrusting a contract with nontrivial assets, you should:", + "options": [ + { + "answer": "Check for verified source code", + "correct": true + }, + { + "answer": "Write and deploy your own version of the contract" + }, + { + "answer": "Memorize the contract's bytecode" + }, + { + "answer": "Check for a contract's audit report", + "correct": true + } + ] + }, + { + "question": "You can build apps that rely on externally created smart contracts", + "options": [ + { + "answer": "True", + "correct": true + }, + { + "answer": "False" + } + ] + }, + { + "question": "Before you fork someone's project to build your own, you should:", + "options": [ + { + "answer": "Read and respect the software license", + "correct": true + }, + { + "answer": "Credit the original authors where appropriate", + "correct": true + }, + { + "answer": "Pretend it was all your idea" + } + ] + }, + { + "question": "Ethereum smart contract languages include:", + "options": [ + { + "answer": "Vyper", + "correct": true + }, + { + "answer": "EVM" + }, + { + "answer": "Fe", + "correct": true + }, + { + "answer": "Solidity", + "correct": true + } + ] + }, + { + "question": "For all users, a smart contract has the same state.", + "options": [ + { + "answer": "True", + "correct": true + }, + { + "answer": "False" + } + ] + }, + { + "question": "What does ABI stand for?", + "options": [ + { + "answer": "Average Binary Implementation" + }, + { + "answer": "Approximate Beverage Intake" + }, + { + "answer": "Alternate Bovine Intervention" + }, + { + "answer": "Application Binary Interface", + "correct": true + } + ] + }, + { + "question": "What is an ABI?", + "options": [ + { + "answer": "A machine-readable description of a contract's functions, events, and data types", + "correct": true + }, + { + "answer": "The compiled and optimized contract bytecode" + }, + { + "answer": "A convention for auditing smart contracts" + } + ] + }, + { + "question": "Using established token standards can make your app widely compatible with Ethereum assets and wallets.", + "options": [ + { + "answer": "True", + "correct": true + }, + { + "answer": "False" + } + ] + }, + { + "question": "Once a contract is deployed, it is impossible to upgrade it.", + "options": [ + { + "answer": "True" + }, + { + "answer": "False", + "correct": true + } + ] + } + ] +}