diff --git a/apps/academy/public/assets/tracks/rootstock-101/1/merged-mining.gif b/apps/academy/public/assets/tracks/rootstock-101/1/merged-mining.gif new file mode 100644 index 00000000..16a402df Binary files /dev/null and b/apps/academy/public/assets/tracks/rootstock-101/1/merged-mining.gif differ diff --git a/apps/academy/public/assets/tracks/rootstock-101/1/peg-in.gif b/apps/academy/public/assets/tracks/rootstock-101/1/peg-in.gif new file mode 100644 index 00000000..6605715a Binary files /dev/null and b/apps/academy/public/assets/tracks/rootstock-101/1/peg-in.gif differ diff --git a/apps/academy/public/assets/tracks/rootstock-101/1/peg-out.gif b/apps/academy/public/assets/tracks/rootstock-101/1/peg-out.gif new file mode 100644 index 00000000..3b85a4f4 Binary files /dev/null and b/apps/academy/public/assets/tracks/rootstock-101/1/peg-out.gif differ diff --git a/apps/academy/public/assets/tracks/rootstock-101/2/1.png b/apps/academy/public/assets/tracks/rootstock-101/2/1.png new file mode 100644 index 00000000..ee0a9437 Binary files /dev/null and b/apps/academy/public/assets/tracks/rootstock-101/2/1.png differ diff --git a/apps/academy/public/assets/tracks/rootstock-101/2/2.png b/apps/academy/public/assets/tracks/rootstock-101/2/2.png new file mode 100644 index 00000000..3fe17f3a Binary files /dev/null and b/apps/academy/public/assets/tracks/rootstock-101/2/2.png differ diff --git a/apps/academy/public/assets/tracks/rootstock-101/2/3.png b/apps/academy/public/assets/tracks/rootstock-101/2/3.png new file mode 100644 index 00000000..139deb8d Binary files /dev/null and b/apps/academy/public/assets/tracks/rootstock-101/2/3.png differ diff --git a/apps/academy/public/assets/tracks/rootstock-101/2/4.png b/apps/academy/public/assets/tracks/rootstock-101/2/4.png new file mode 100644 index 00000000..94ccc6ec Binary files /dev/null and b/apps/academy/public/assets/tracks/rootstock-101/2/4.png differ diff --git a/apps/academy/public/assets/tracks/rootstock-101/2/5.png b/apps/academy/public/assets/tracks/rootstock-101/2/5.png new file mode 100644 index 00000000..4b127cfc Binary files /dev/null and b/apps/academy/public/assets/tracks/rootstock-101/2/5.png differ diff --git a/apps/academy/public/assets/tracks/rootstock-101/2/6.png b/apps/academy/public/assets/tracks/rootstock-101/2/6.png new file mode 100644 index 00000000..219378ae Binary files /dev/null and b/apps/academy/public/assets/tracks/rootstock-101/2/6.png differ diff --git a/apps/academy/public/assets/tracks/rootstock-101/2/7.png b/apps/academy/public/assets/tracks/rootstock-101/2/7.png new file mode 100644 index 00000000..d9aa3f84 Binary files /dev/null and b/apps/academy/public/assets/tracks/rootstock-101/2/7.png differ diff --git a/apps/academy/public/assets/tracks/rootstock-101/3/1.png b/apps/academy/public/assets/tracks/rootstock-101/3/1.png new file mode 100644 index 00000000..946589e5 Binary files /dev/null and b/apps/academy/public/assets/tracks/rootstock-101/3/1.png differ diff --git a/apps/academy/public/assets/tracks/rootstock-101/3/2.png b/apps/academy/public/assets/tracks/rootstock-101/3/2.png new file mode 100644 index 00000000..25011873 Binary files /dev/null and b/apps/academy/public/assets/tracks/rootstock-101/3/2.png differ diff --git a/apps/academy/public/assets/tracks/rootstock-101/3/3.png b/apps/academy/public/assets/tracks/rootstock-101/3/3.png new file mode 100644 index 00000000..763798e0 Binary files /dev/null and b/apps/academy/public/assets/tracks/rootstock-101/3/3.png differ diff --git a/apps/academy/public/assets/tracks/rootstock-101/3/4.png b/apps/academy/public/assets/tracks/rootstock-101/3/4.png new file mode 100644 index 00000000..13f620da Binary files /dev/null and b/apps/academy/public/assets/tracks/rootstock-101/3/4.png differ diff --git a/apps/academy/public/assets/tracks/rootstock-101/4/1.png b/apps/academy/public/assets/tracks/rootstock-101/4/1.png new file mode 100644 index 00000000..869a46b6 Binary files /dev/null and b/apps/academy/public/assets/tracks/rootstock-101/4/1.png differ diff --git a/apps/academy/public/authors/dappadan.jpg b/apps/academy/public/authors/dappadan.jpg new file mode 100644 index 00000000..8bdf8855 Binary files /dev/null and b/apps/academy/public/authors/dappadan.jpg differ diff --git a/apps/academy/public/meta-images/rootstock-101/1.png b/apps/academy/public/meta-images/rootstock-101/1.png new file mode 100644 index 00000000..643645cd Binary files /dev/null and b/apps/academy/public/meta-images/rootstock-101/1.png differ diff --git a/apps/academy/public/meta-images/rootstock-101/2.png b/apps/academy/public/meta-images/rootstock-101/2.png new file mode 100644 index 00000000..80a014ac Binary files /dev/null and b/apps/academy/public/meta-images/rootstock-101/2.png differ diff --git a/apps/academy/public/meta-images/rootstock-101/3.png b/apps/academy/public/meta-images/rootstock-101/3.png new file mode 100644 index 00000000..6f6e343f Binary files /dev/null and b/apps/academy/public/meta-images/rootstock-101/3.png differ diff --git a/apps/academy/public/meta-images/rootstock-101/4.png b/apps/academy/public/meta-images/rootstock-101/4.png new file mode 100644 index 00000000..6698b35f Binary files /dev/null and b/apps/academy/public/meta-images/rootstock-101/4.png differ diff --git a/apps/academy/public/meta-images/rootstock-101/rootstock-101.png b/apps/academy/public/meta-images/rootstock-101/rootstock-101.png new file mode 100644 index 00000000..0be4b572 Binary files /dev/null and b/apps/academy/public/meta-images/rootstock-101/rootstock-101.png differ diff --git a/apps/academy/public/track-rootstock-101.png b/apps/academy/public/track-rootstock-101.png new file mode 100644 index 00000000..0ddf0bf0 Binary files /dev/null and b/apps/academy/public/track-rootstock-101.png differ diff --git a/apps/academy/src/data/questions/rootstock-101/1/quiz-1/Q1.json b/apps/academy/src/data/questions/rootstock-101/1/quiz-1/Q1.json new file mode 100644 index 00000000..a2a5b02b --- /dev/null +++ b/apps/academy/src/data/questions/rootstock-101/1/quiz-1/Q1.json @@ -0,0 +1,12 @@ +{ + "question": "What is the main purpose of a Bitcoin sidechain like Rootstock?", + "options": [ + { "answer": "To replace the Bitcoin network entirely" }, + { "answer": "To increase Bitcoin’s Proof of Work capabilities" }, + { + "answer": "To extend Bitcoin’s functionality, such as adding smart contract execution", + "correct": true + }, + { "answer": "To reduce transaction fees on Bitcoin" } + ] +} diff --git a/apps/academy/src/data/questions/rootstock-101/1/quiz-1/Q2.json b/apps/academy/src/data/questions/rootstock-101/1/quiz-1/Q2.json new file mode 100644 index 00000000..6eb6f83c --- /dev/null +++ b/apps/academy/src/data/questions/rootstock-101/1/quiz-1/Q2.json @@ -0,0 +1,12 @@ +{ + "question": "Which of the following conditions must a Bitcoin sidechain meet?", + "options": [ + { "answer": "The native token should be completely independent from Bitcoin" }, + { + "answer": "The native token of the sidechain should be transferable between the parent and sidechain", + "correct": true + }, + { "answer": "The sidechain should have its own independent consensus mechanism" }, + { "answer": "The sidechain should always use a different gas token" } + ] +} diff --git a/apps/academy/src/data/questions/rootstock-101/2/quiz-1/Q1.json b/apps/academy/src/data/questions/rootstock-101/2/quiz-1/Q1.json new file mode 100644 index 00000000..354e3942 --- /dev/null +++ b/apps/academy/src/data/questions/rootstock-101/2/quiz-1/Q1.json @@ -0,0 +1,9 @@ +{ + "question": "What is the primary purpose of The Graph?", + "options": [ + { "ansswer": "To mine cryptocurrencies" }, + { "ansswer": "To secure transactions" }, + { "ansswer": "To organize blockchain data", "correct": true }, + { "ansswer": "To replace the blockchain" } + ] +} diff --git a/apps/academy/src/data/questions/rootstock-101/2/quiz-1/Q2.json b/apps/academy/src/data/questions/rootstock-101/2/quiz-1/Q2.json new file mode 100644 index 00000000..5dbb49e7 --- /dev/null +++ b/apps/academy/src/data/questions/rootstock-101/2/quiz-1/Q2.json @@ -0,0 +1,9 @@ +{ + "question": "What must be staked by indexers in The Graph network?", + "options": [ + { "answer": "ETH" }, + { "answer": "BTC" }, + { "answer": "RIF" }, + { "answer": "GRT", "correct": true } + ] +} diff --git a/apps/academy/src/data/quizzes/rootstock-101/1.json b/apps/academy/src/data/quizzes/rootstock-101/1.json new file mode 100644 index 00000000..d9bdd39c --- /dev/null +++ b/apps/academy/src/data/quizzes/rootstock-101/1.json @@ -0,0 +1,41 @@ +{ + "title": "Final Quiz", + "questions": [ + { + "question": "How does the 2-Way Peg between Bitcoin and Rootstock work?", + "options": [ + { "answer": "BTC is destroyed to mint new RBTC" }, + { + "answer": "BTC is locked on the Bitcoin network and an equivalent amount of RBTC is released on Rootstock", + "correct": true + }, + { "answer": "RBTC and BTC are merged into a single token" }, + { "answer": "BTC can only be transferred one way to Rootstock" } + ] + }, + { + "question": "What is the function of the PowHSM devices in Rootstock's 2-Way Peg system?", + "options": [ + { "answer": "They provide additional hashing power for Bitcoin mining" }, + { + "answer": "They automatically sign transactions to ensure security during Bitcoin and RBTC transfers", + "correct": true + }, + { "answer": "They manually validate every transaction on the network" }, + { "answer": "They allow individual users to approve transactions using private keys" } + ] + }, + { + "question": "What makes Rootstock compatible with Ethereum smart contracts?", + "options": [ + { "answer": "It uses a new version of Bitcoin scripting language" }, + { "answer": "It is based on a Proof of Stake consensus algorithm" }, + { + "answer": "It is EVM-compatible, allowing Solidity-based contracts to run on Rootstock", + "correct": true + }, + { "answer": "It has no compatibility with Ethereum at all" } + ] + } + ] +} diff --git a/apps/academy/src/data/quizzes/rootstock-101/2.json b/apps/academy/src/data/quizzes/rootstock-101/2.json new file mode 100644 index 00000000..4344cd3f --- /dev/null +++ b/apps/academy/src/data/quizzes/rootstock-101/2.json @@ -0,0 +1,32 @@ +{ + "title": "Final Quiz", + "questions": [ + { + "question": "What language is used to query the subgraph?", + "options": [ + { "answer": "SQL" }, + { "answer": "REST" }, + { "answer": "JSON" }, + { "answer": "GraphQL", "correct": true } + ] + }, + { + "question": "What is the command to build the subgraph?", + "options": [ + { "answer": "graph init" }, + { "answer": "graph codegen && graph build", "correct": true }, + { "answer": "graph auth" }, + { "answer": "graph deploy" } + ] + }, + { + "question": "What is the purpose of the `graph codegen` command?", + "options": [ + { "answer": "To initialize the subgraph" }, + { "answer": "To build the subgraph" }, + { "answer": "To load the contract's ABI and create types", "correct": true }, + { "answer": "To deploy the subgraph" } + ] + } + ] +} diff --git a/apps/academy/src/data/quizzes/rootstock-101/3.json b/apps/academy/src/data/quizzes/rootstock-101/3.json new file mode 100644 index 00000000..e89aeab3 --- /dev/null +++ b/apps/academy/src/data/quizzes/rootstock-101/3.json @@ -0,0 +1,32 @@ +{ + "title": "Final Quiz", + "questions": [ + { + "question": "What is the purpose of the `voters` mapping in the contract?", + "options": [ + { "answer": "To track the number of votes" }, + { "answer": "To prevent double voting", "correct": true }, + { "answer": "To store the addresses of voters" }, + { "answer": "To count the total options" } + ] + }, + { + "question": "What type of tokens are needed to deploy the contract on the Rootstock testnet?", + "options": [ + { "answer": "BTC" }, + { "answer": "tRBTC", "correct": true }, + { "answer": "ETH" }, + { "answer": "RBTC" } + ] + }, + { + "question": "What is the purpose of the 'getOptionsCount' function in the smart contract?", + "options": [ + { "answer": "To add a new option to the poll" }, + { "answer": "To vote for an option" }, + { "answer": "To get the count of options", "correct": true }, + { "answer": "To get a specific option by index" } + ] + } + ] +} diff --git a/apps/academy/src/data/quizzes/rootstock-101/4.json b/apps/academy/src/data/quizzes/rootstock-101/4.json new file mode 100644 index 00000000..16f88380 --- /dev/null +++ b/apps/academy/src/data/quizzes/rootstock-101/4.json @@ -0,0 +1,35 @@ +{ + "title": "Final Quiz", + "questions": [ + { + "question": "Where can you find the ABI of your deployed smart contract?", + "options": [ + { "answer": "In the MetaMask wallet" }, + { "answer": "In the `artifacts/contracts/Poll.json` file", "correct": true }, + { "answer": "On the Rootstock documentation website" }, + { "answer": "In the RainbowKit configuration" } + ] + }, + { + "question": "Which library is NOT mentioned as a part of the starter kit for building the frontend?", + "options": [ + { "answer": "NextJS" }, + { "answer": "EtherJS" }, + { "answer": "Wagmi" }, + { "answer": "React Router", "correct": true } + ] + }, + { + "question": "What condition is used in the UI to check if the options are still being loaded?", + "options": [ + { "answer": "isLoadingOptionsCount", "correct": true }, + { "answer": "isVoting" }, + { "answer": "isLoadingPoll" }, + { "answer": "isLoadingTransaction" } + ] + } + ] +} + + + diff --git a/apps/academy/src/pages/tracks/rootstock-101/1.mdx b/apps/academy/src/pages/tracks/rootstock-101/1.mdx new file mode 100644 index 00000000..da99958a --- /dev/null +++ b/apps/academy/src/pages/tracks/rootstock-101/1.mdx @@ -0,0 +1,141 @@ +--- +title: Rootstock 101: Lesson 1 +description: What is Rootstock, how is it connected to Bitcoin, and what are the differences between Rootstock and Ethereum? +icons: ["rootstock", "fundamentals"] +--- + +import LessonLayout from "../../../components/LessonLayout"; +import Callout from "../../../components/mdx/Callout"; +import QuizStatusChecker from "../../../components/mdx/QuizStatusChecker"; +import Question from "../../../components/mdx/Question"; +import LessonQuestionsModal from "../../../components/mdx/LessonQuestionsModal"; +import LessonInformationalModal from "../../../components/mdx/LessonInformationalModal"; + + + +## About This Lesson + +A rootstock is a root system that is used to increase the growth and performance of fruit trees. + +Don't worry this course and lesson is not about growing fruit trees! + +However, knowing this concept is helpful when learning about Rootstock, the Bitcoin side chain. As you wil + +In this lesson, we will explore: + +- What is a Bitcoin Sidechain and how do they improve the performance of the network. +- How Rootstock bridges Ethereum and Bitcoin to grow to new capabilities. +- EVM Compatibility and the fruitful benefits that Rootstock brings to developers. + +Each lesson also has short quizes to check your knowledge. + +So let's get started digging into Rootstock! + +## What is a Bitcoin Sidechain? + +No blockchain network is perfect. Every blockchain has its strengths and weaknesses. + +The Bitcoin network excels at providing security through its Proof of Work consensus network. This makes Bitcoin a tamper-proof store of value. This ability does not extend to all use cases that we blockchains have been used for. + +Bitcoin sidechains extend the capability of the Bitcoin network to address different use cases that the original chain was not designed for. This can include features such as privacy and performance. + +Rootstock, the first Bitcoin sidechain, is focused on giving the Bitcoin network the ability to execute smart contracts. If you have worked with Ethereum before, smart contract execution is something that is performed by the chain through using the Ethereum Virtual Network (EVM). + +The world of Bitcoin sidechains is still an evolving one but a sidechain should meet these conditions: + +- The Native Token of the sidechain should be backed by the parent token, Bitcoin (BTC), and be able to be transferred between the parent and sidechain. +- The sidechain should not require any other token to function. This includes having a token that competes on value with the parent token or a specific gas token. +- Transaction finality should be reached independently from the parent chain. This means a transaction should not be changed after it is confirmed. + +Now that we have this definition, let us look at how Rootstock meets these conditions. + + + + + + +## How Rootstock Works + +There are 3 main concepts you need to know to understand how Rootstock works: + +- Merged Mining +- The 2 Way Peg +- EVM Compatibility + +### Merged Mining + +The security of Bitcoin is provided by its miners and the mining process is needed to add a new block to the chain. This process requires a large amount of computation and time to provide a solution to the mathematical challenge. + +Rootstock is merged and mined with the Bitcoin network. This means that the same computation and resources used by a miner can be used to mine a Rootstock block. Mining a Rootstock block is also faster. The process takes around 30 seconds compared to 10 minutes for a Bitcoin block. + +This is a positive for Bitcoin miners because it: + +- Enables them to reuse the computational resources used for Bitcoin mining. +- Offers an easier mathematical challenge for providing faster block time. +- Provides an additional revenue stream in Bitcoin by providing hashing power to the Rootstock network with minimal additional configuration. + +Each Bitcoin block include a reference to a Rootstock block. This reference is ignored by the Bitcoin network but allows for the mining solution to: + +- Be submitted by both Bitcoin and Rootstock if it meets Bitcoin's difficulty level +- Be submitted to only Rootstock if it it does not meet Bitcoin's difficulty level but does meet Rootstock's + +![Merged Mining](/assets/tracks/rootstock-101/1/merged-mining.gif) + +Merged mining also enables the stored token can be transferred from the mainchain to the sidechain and back. This is where the 2-way peg comes in. + +### Pow Peg - The 2-Way Peg + +The token of Rootstock is RBTC. It is 1 to 1 peg to BTC. 1 RBTC = 1 BTC. + +The real value of this peg is the ability to transfer BTC back and forth between the Bitcoin network and Rootstock. To do this, the amount of BTC needs to be locked on Bitcoin and the same amount in RBTC should be released on Rootstock. + +#### Peg-In - Locking Bitcoin + +The process of locking Bitcoin securely needs to be tamper-proof to prevent any double spend on either Rootstock or the Bitcoin network. Rootstock uses a group of participating nodes or functionaries to manage private keys for using the Bitcoin multi-signature protocol. This isn't like a traditional multi-sig as it uses hardware devices called PowHSMs. + +![Peg In](/assets/tracks/rootstock-101/1/peg-in.gif) + +PowHSMs are tamper-proof that are devices that are connected to a running Rootstock node. The difference between a traditional multi-sig wallet is that PowHSMs perform the signature on transactions automatically and not done by an individual. These signatures occur after a large amount of hashing power or completed blocks are confirmed. This prevents any type of attack or collusion to occur. + +### Peg-Out - Unlocking RBTC + +The other bridging method is to get RBTC out of Rootstock and into BTC. This is using the Bridge Smart Contract address on Rootstock called the exit address. The PowHSMs also require a large amount of confirmed blocks before the peg-out takes place and is final. + +![Peg Out](/assets/tracks/rootstock-101/1/peg-out.gif) + +As we discussed earlier, the main benefit of using Rootstock for developers and users is its ability to add smart contact functionality to the Bitcoin network. + +## EVM Compatibility + +Rootstock is EVM-compatible. For developers, this means that you can write smart contracts using Solidity and work with familiar tools like Hardhat and libraries like wagmi when interacting with smart contracts. This also allows for protocols deployed on other EVM-compatible networks to be ported to Rootstock as well. + +There is also a growing set of open-source tools to make building on Rootstock easier for developers. These tools are included in the set of protocols called the Rootstock Infrastructure Framework (RIF). Here are some of the tools: + +- RNS (RIF Name Service) - similar to the Ethereum Name Service (ENS), this replaces the complicated wallet addresses with nicknames to allow users to easily perform transactions. +- RIF Wallet - An open-source Bitcoin wallet that allows users to interact with smart contracts. +- Flyover - Enables users to transfer BTC between the Bitcoin and Rootstock networks. + + + +## Conclusion + +Great work finishing the introduction to Rootstock! Now we will put what you have learned to practice in the next lessons. The next lesson will using The Graph to read data from the Rootstock mainnet! + + diff --git a/apps/academy/src/pages/tracks/rootstock-101/2.mdx b/apps/academy/src/pages/tracks/rootstock-101/2.mdx new file mode 100644 index 00000000..167f439a --- /dev/null +++ b/apps/academy/src/pages/tracks/rootstock-101/2.mdx @@ -0,0 +1,217 @@ +--- +title: Rootstock 101: Lesson 2 +description: Reading Rootstock onchain data and existing smart contract state via a Web frontend and a Node.js backend. +icons: ["rootstock", "fundamentals"] +--- + +import LessonLayout from "../../../components/LessonLayout"; +import Callout from "../../../components/mdx/Callout"; +import QuizStatusChecker from "../../../components/mdx/QuizStatusChecker"; +import Question from "../../../components/mdx/Question"; +import LessonQuestionsModal from "../../../components/mdx/LessonQuestionsModal"; +import LessonInformationalModal from "../../../components/mdx/LessonInformationalModal"; + + + +## About This Lesson + +Blockchains are constantly producing data. From transactions, contract interactions, and deployment. Making sense of this activity is important for building apps that need to use this data. + +In this lesson, we will explore: + +- The purpose of The Graph and how it enables developers to use its data. +- How to setup a Subgraph to read an existing smart contract deployed on Rootstock mainnet. +- Querying the subgraph using either NextJS or NodeJS to integrated into an application. + +## What is The Graph? + +The Graph solves the problem of organizing the information produced by blockchains. It does this through the use of **subgraphs** and **indexers**. + +Subgraphs are open-source APIs that gather and organize blockchain data. Anyone can create a subgraph on a supported chain and a valid contract address. You can query contract activity and events. + +Indexers are nodes that are responsible for serving the subgraphs. These nodes stake GRT, the utility token of the Graph Network, and select which subgraphs to index. These nodes are then rewarded for both indexing and serving queries. + +## Setting up a Subgraph + +Let's set up a subgraph to read the RIF token contract deployed on the Rootstock mainnet. After we have completed this step, we will be able to see. + +- **id** - transaction hash of transactions interacting with the smart contract +- **from** - the sender address of the RIF tokens. +- **to** - the receiver address of the RIF token +- **value** - the amount of RIF tokens transferred + +### Creating the Subgraph + +We will use the Subgraph Studio provided by The Graph to create this subgraph. + +Visit [https://thegraph.com/studio/](https://thegraph.com/studio/) and sign up or connect your wallet if you're new to The Graph. + +After you log in, you will see the option to "Create a Subgraph" on the right side of the screen. + +![Create Subgraph](/assets/tracks/rootstock-101/2/1.png) + +From there you will be able to name your subgraph. For this lesson we will call this "RFI-Token" but you can name this whatever you like. + +![Name Subgraph](/assets/tracks/rootstock-101/2/2.png) + +This will create a "draft" subgraph in the main dashboard of your Subgraph Studio. + +The subgraph is in draft mode because we have not set up what we would like to index yet. Let's do this now by clicking on the RFI-Token subgraph. + +You should now be seeing a similar screen to this: + +![Subgraph Install](/assets/tracks/rootstock-101/2/3.png) + + + + + + +### Starting the Subgraph + +The first step is to install The Graph CLI. We will use this CLI tool to initialize and authenticate the subgraph. + +To install The Graph CLI, type this command in your terminal. + +```bash +npm install -g @graphprotocol/graph-cli +``` + +(Note: `yarn global add` is also available if you prefer to use yarn.) + +Next, we will initialize the subgraph by using the following command: + +```bash +graph init --studio rfi-token +``` + +Inside the terminal you will be asked what **Protocol** to use. In this case, select **Ethereum**. + +Then use the following settings: + +```txt +**Subgraph slug**: rfi-token +**Directory to create the subgraph in**: rfi-token +**Ethereum network:** Rootstock +**Contract Address**: +0x2aCc95758f8b5F583470bA265Eb685a8f45fC9D5 +**Start Block**: Default Value +**Contract Name**: RIFToken +**Index contract events as entities**: true +``` + +This will now start the initialization process in your terminal and install dependencies. + +### Authenticating and Deploying the Subgraph + +Before deploying our subgraph, we need to authenticate within the CLI. The quickest way to do this is using the command that is provided in the Subgraph Studio. + +It will be in this format: + +```bash +graph auth --studio AUTHKEY +``` + +You can do this by running the following command: + +![Auth-Key Cropped](/assets/tracks/rootstock-101/2/4.png) + +After that, go to the rfi-token directory by typing cd rfi-token in your terminal. + +To build the subgraph, use the following commands: + +```bash +graph codegen && graph build +``` + +This command will load the RFI token contract's ABI and create types for both the ABI and the GraphQL schema. + +If this command is successful, you will see Build completed: build/subgraph.yaml in your terminal. The last step is to deploy the subgraph by running the following command: + +```bash +graph deploy --studio rfi-token +``` + +After selecting a version number, you will see your **Subgraph endpoint** in the terminal. + +## Testing the Endpoint + +To test the endpoint, go back to the Subgraph Studio and select the **Playground** option. + +![playground.png](/assets/tracks/rootstock-101/2/5.png) + +On this page, you will see a sample query that you can use to query your subgraph: + +```graphql +{ + transfers(first: 5) { + id + from + to + value + } + ownershipRenounceds(first: 5) { + id + previousOwner + blockNumber + blockTimestamp + } +} +``` + +By clicking the Play button, you will see the result of the query. If you have just deployed your subgraph, it may take time for it to be be fully synced. + +You can find the progress of the synchronization under the subgraph details. + +![playbutton.png](/assets/tracks/rootstock-101/2/6.png) + +The results should look like similiar to this: + +```json +"transfers": [ + { + "id": "0x0000f61c00c7a782bfaf1803c31ae2275c919f8be6f0920514410ec2cc7e4d3e01000000", + "from": "0x0cfc4f6070819148c82087ec9fc1872f0455d7c2", + "to": "0xd9c79ced86ecf49f5e4a973594634c83197c35ab", + "value": "5000000000000000000" + }, + ... +], +``` + +Now that we know the subgraph is working, we can query it outside of the playground as well. + +## Querying the Subgraph (NextJS or NodeJS) + +You can find sample code to query the subgraph using React, Next.js, and Node on the endpoints page of Subgraph Studio. + +![code-examples.png](/assets/tracks/rootstock-101/2/7.png) + +From there, you can copy the code that fits your application code the best. + +Since The Graph uses GraphQL, you can adjust your query to get only the information you need. For example, you can add a blockTimestamp to return the timestamps for the blocks that included the transactions: + + + +## Conclusion + +Now that we can read data from the Rootstock blockchain, let's start build and deploy a smart contract! In the next lesson we will use Solidity and Hardhat to create a polling smart contract. + + diff --git a/apps/academy/src/pages/tracks/rootstock-101/3.mdx b/apps/academy/src/pages/tracks/rootstock-101/3.mdx new file mode 100644 index 00000000..5f0ba968 --- /dev/null +++ b/apps/academy/src/pages/tracks/rootstock-101/3.mdx @@ -0,0 +1,339 @@ +--- +title: Rootstock 101: Lesson 3 +description: Creating a simple, smart contract and deploying it to Rootstock. +icons: ["rootstock", "fundamentals"] +--- + +import LessonLayout from "../../../components/LessonLayout"; +import Callout from "../../../components/mdx/Callout"; +import QuizStatusChecker from "../../../components/mdx/QuizStatusChecker"; +import Question from "../../../components/mdx/Question"; +import LessonQuestionsModal from "../../../components/mdx/LessonQuestionsModal"; +import LessonInformationalModal from "../../../components/mdx/LessonInformationalModal"; + + + +## About This Lesson + +Rootstock is a Bitcoin sidechain that allows developers to create smart contracts compatible with the Ethereum Virtual Machine (EVM), as discussed in previous lessons. + +This means you can develop with Solidity along with familiar tools like Hardhat for your smart contracts. + +In this lesson, you will: + +- Write a Solidity Smart Contract that allows user to vote on in a public poll. +- Setup your wallet and receive testnet tokens for the Rootstock testnet. +- Deploy the smart contract to the Rootstock testnet using Hardhat. + +Let's get started building our smart contract! + +## Developing the Smart Contract + +### Installing the Starter Kit + +The first step to get started is to clone the Rootstock Hardhat Starter kit. Using this starter kit will make sure you have all the right dependencies. Run the following command in your terminal: + +```bash +git clone https://github.com/rsksmart/rootstock-hardhat-starterkit.git +``` + +After doing this, navigate to the folder and use npm to install the required dependencies: + +```bash +cd rootstock-hardhat-starterkit +npm install +``` + +### Setting Up the Environment Variables + +In the main directory of the `Rootstock-Hardhat-Starterkit` file, there is a file called `.env.example`. + +Copy this file and rename it to `.env` + +In this file you will see 3 environment variables that you will need to set to deploy a smart contract: + +```txt +PRIVATE_KEY: Your private key (e.g., from your Metamask account details). +RSK_MAINNET_RPC_URL: The RPC URL for the Rootstock mainnet. +RSK_TESTNET_RPC_URL: The RPC URL for the Rootstock testnet. +``` + +To get the Mainnet and Testnet RPC URLs, you need to create an account on the Rootstock RPC API page ([https://rpc.rootstock.io](https://rpc.rootstock.io).) + +Once you are there, create or login into your account. You will then see the API Dashboard. Select `+New API Key` to create your API Key. + +![create-apio.png](/assets/tracks/rootstock-101/3/1.png) + +First, create a Testnet API Key by selecting the Network as `TESTNET`: + +![Name API](/assets/tracks/rootstock-101/3/2.png) + +After creating the API Key, click the API button to get your testnet RPC URL from the Connect API screen below: + +![Connect API](/assets/tracks/rootstock-101/3/3.png) + +Copy this URL and add it to your `.env` file you have created: + +```txt +RSK_MAINNET_RPC_URL='' +RSK_TESTNET_RPC_URL='[https://rpc.testnet.rootstock.io/Fvdu04HN9y4dByGY4qRNXavHu3YWHU-T](https://rpc.testnet.rootstock.io/Fvdu04HN9y4dByGY4qRNXavHu3YWHU-T)' +PRIVATE_KEY='' +``` + +Now repeat the same steps for creating a Mainnet RPC URL. + +### Adding Your Private Key - Metamask + +The last environment variable to add is the private key of your wallet. + +**IMPORTANT:** To avoid any loss of funds, please create a new wallet for this course. + +To get your private key on your Metamask wallet, please follow [this guide](https://support.metamask.io/managing-my-wallet/secret-recovery-phrase-and-private-keys/how-to-export-an-accounts-private-key/). + +## Getting Testnet Tokens + +To deploy the contract and interact with it, you will need tRBTC tokens. These tokens are used on the Rootstock testnet. + +Head over to the Rootstock Faucet [https://faucet.rootstock.io](https://faucet.rootstock.io) to get the tokens. + +You will need to copy your wallet address from Metamask into the address field. + +It should take about 30 seconds for the tRBTC to reach your wallet, and you'll see a confirmation like this with the transaction hash: ![confirmation.png](/assets/tracks/rootstock-101/3/4.png) + +## Writing the Smart Contract + +Now that you have the environment variables set up and the testnet tokens, it's time to write the smart contract. + +This smart contract is a polling smart contract. It will: + +- Allow a user to select how long they have been in DeveloperDAO +- Sign the transaction so it lives onchain +- See the results of the poll completed by other users + +### Creating the Contract + +Inside the `contracts` folder of the Hardhat Starterkit you should see 3 sample contracts (ERC20, ERC721, ERC1155). + +Create a new file and call it `Poll.sol`. Here is where you will write the poll smart contract. + +If you would like the complete Soldity code, here it is: + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +contract Poll { + struct Option { + uint256 id; + string name; + uint256 voteCount; + } + Option[] public options; + mapping(address => bool) public voters; + event VotedEvent(uint256 optionId); + + constructor() { + addOption("1-2 Years"); + addOption("3-4+ Years"); + addOption("Not in Developer DAO"); + } + + function addOption(string memory _name) private { + options.push(Option(options.length, _name, 0)); + } + + function vote(uint _optionId) public { + if (voters[msg.sender]) revert AlreadyVoted(); + if (_optionId >= options.length) revert InvalidOption(); + voters[msg.sender] = true; + options[_optionId].voteCount++; + emit VotedEvent(_optionId); + } + + function getOptionsCount() public view returns (uint256) { + return options.length; + } + + function getOption(uint256 _index) public view returns (Option memory) { + require(_index < options.length, "Invalid index"); + return options[_index]; + } + + error AlreadyVoted(); + + error InvalidOption(); +} +``` + +Here is a breakdown of what this contract code is doing: + +```solidity +struct Option { + uint256 id; + string name; + uint256 voteCount; +} +``` + +This code defines a struct for the options you will add to the poll. Each open needs an id for tracking, a name to show the user and a voteCount to collect all the votes. + +```solidity +Option[] public options; +mapping(address => bool) public voters; +``` + +We then create a public array of options using the struct type Option defined above. We use the `public` keyword so that we can read this array externally later on. + +To keep track of the voters who have already vote, we use a public mapping of `voters` to an address. The boolean prevents double voting by changing to true after someone votes. + +```solidity +event VotedEvent(uint256 optionId); + +constructor() { + addOption("1-2 Years"); + addOption("3-4+ Years"); + addOption("Not in Developer DAO"); +} +``` + +We then create an event that will get emitted once a user has voted for a specific `optionId`. The constructor creates the options that users will vote on in the poll. Feel free to change these to whatever you like! + +```solidity +function addOption(string memory _name) private { + options.push(Option(options.length, _name, 0)); +} +``` + +The next step is adding options for user to vote on by using this `addOption` function. + +As we defined the `Option` struct, each option has an id, name and vote count. + +In this function the id is automatically assigned based on the length of the array, and the name is the parameter we pass in. The vote count is set to 0 by default. + +```solidity +function vote(uint _optionId) public { + if (voters[msg.sender]) revert AlreadyVoted(); + if (_optionId >= options.length) revert InvalidOption(); + voters[msg.sender] = true; + options[_optionId].voteCount++; + emit VotedEvent(_optionId); +} +``` + +This function is where the voting "magic" happens. + +First it checks if the user has already voted by checking the `voters` mapping. If they have, it reverts the transaction. + +Then it checks if the option id is valid. If not, it reverts the transaction. + +After that, it sets the `voters` mapping to true for the address that is voting. + +Finally, it increments the vote count for the option that the user has voted on and emits the `VotedEvent` event. + +```solidity +function getOptionsCount() public view returns (uint256) { + return options.length; +} + +function getOption(uint256 _index) public view returns (Option memory) { + require(_index < options.length, "Invalid index"); + return options[_index]; +} +``` + +These two functions will be helpful later on when you build the frontend. + +They allow you to get the total count of options with `getOptionsCount` and to get the details of a specific option with `getOption`. + +## Deploying the Contract + +Now that the code is added, its time to get it onchain. + +In the `deploy` folder of the Starterkit is where the deployment scripts are stored. These files are written in Typescript and used by Hardhat for deployment. + +Create a file called `DeployPoll.ts` and copy the code below: + +```typescript +import { DeployFunction } from "hardhat-deploy/types"; + +import { HardhatRuntimeEnvironment } from "hardhat/types"; + +const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { + const { deployer, owner } = await hre.getNamedAccounts(); + + await hre.deployments.deploy("Poll", { + from: deployer, + + log: true, + }); +}; + +export default func; + +func.tags = ["poll"]; +``` + +This script is used to deploy the `Poll` contract to the Rootstock Testnet. It uses the `hardhat-deploy` library to deploy the contract. + +To make it easier when using the Hardhat Shorthand package, it also defines a tag for the contract, `poll`, which you be used when deploying the contract. + +### Compile and Deploy the Poll Contract + +Before compiling the contract, install the Hardhat Shorthand package. This will help you save time typing the Hardhat commands in the terminal. + +```bash +npm i -g hardhat-shorthand +``` + +After that, you can run the shorthand command to compile the contract which is: + +```bash +hh compile +``` + +You should see a confirmation in your terminal that the contract has been compiled, similar to the example below: + +```txt +Generating typings for: 1 artifacts in dir: typechain-types for target: ethers-v6 +Successfully generated 44 typings! +Compiled 1 Solidity file successfully (evm target: paris). +``` + +Now it is time to deploy the contract to the Rootstock Testnet. You can do this by running the following command: + +```bash +hh deploy --network rskTestnet --tags poll +``` + +Once you deploy the contract, you will see the address where it was deployed, similar to this in your terminal: + +```txt +deploying "Poll" (tx: 0x87309aa6ee40e5f57f767fb82823c7ee0e1726464d48b6dabef647d5ba36fca9)...: deployed at 0xa7e25C1782c9e831DDFcE71316Be8808Ec3C41ba with 747599 gas +``` + +**Save this address somewhere as you will use it to for the next lesson as well!** + + + +## Conclusion + +Great work, now you have a deployed smart contract on Rootstock. You were able to use Hardhat and write in Solidity just like other EVM compatible chains. So that your smart contract is not just sitting on the testnet lonely, in the next lesson you will build a frontend to interact with it. + + diff --git a/apps/academy/src/pages/tracks/rootstock-101/4.mdx b/apps/academy/src/pages/tracks/rootstock-101/4.mdx new file mode 100644 index 00000000..7bdb79af --- /dev/null +++ b/apps/academy/src/pages/tracks/rootstock-101/4.mdx @@ -0,0 +1,328 @@ +--- +title: Rootstock 101: Lesson 4 +description: Connect your deployed contract to a React frontend application, allowing users to interact with that contract. +icons: ["rootstock", "fundamentals"] +--- + +import LessonLayout from "../../../components/LessonLayout"; +import Callout from "../../../components/mdx/Callout"; +import QuizStatusChecker from "../../../components/mdx/QuizStatusChecker"; +import Question from "../../../components/mdx/Question"; +import LessonQuestionsModal from "../../../components/mdx/LessonQuestionsModal"; +import LessonInformationalModal from "../../../components/mdx/LessonInformationalModal"; + + + +## About this Lesson + +Congratulations on making it to the final lesson of this course on Rootstock. + +If you have completed the prior lessons then you will now have: + +- An understanding of the fundamentals of the Rootstock +- Hands-on knowledge of using The Graph to query data from Rootstock +- A deployed smart contract on the Rootstock Testnet + +In this lesson, you will use the smart contract that you deployed in the previous lesson to + +- Build a frontend application using Rainbowkit and wagmi +- Show users the poll options created in the smart contract +- Allow users to sign a transaction after choosing an option + +## Prerequisites + +- A deployed contract on the Rootstock testnet +- **Node.js** (v14.x or later) +- **npm** (v6.x or later) +- A test wallet account in **MetaMask** or any supported wallet provider. + +## Setting up the Project + +To start the setup of the project, clone the below repo. This will include all of the packages you need to build the frontend. + +```bash +git clone https://github.com/rsksmart/Demo-FrontendInteractionSmartContract.git +``` + +This starter kit includes: + +- **NextJS** - A React Library +- **EtherJS** - A JS Library for contract interaction +- **wagmi** - A React library with hooks for contract events +- **viem** - A Typescript library for interacting with smart contracts + +To install these dependencies, run the following commands: + +```bash +cd Demo-FrontendInteractionSmartContract +npm install +``` + +Once that is completed, run the development server by running the `npm run dev` command in the terminal. + +## Locating and Importing the ABI + +Every deployed smart contract has an Application Binary Interface (ABI). ABIs are similar to APIs as they are points where the onchain contract can be interacted with by outside applications. + +The ABI gets created when you compile the smart contract using Hardhat. + +The easiest way to find your ABI is to: + +- Open your project that you created the smart contract +- Open the `artifacts/contracts/Poll.json` file +- Copy the ABI that will look like the one below + +```json +"abi": [ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "AlreadyVoted", + "type": "error" + }, + { + ... + "stateMutability": "view", + "type": "function" + } +] +``` + +Make sure that you get the entire ABI between the two square brackets. + +## Adding ABI to the Index + +Now you will add the ABI to the `index.tsx` file. This will allow your app to interact with parts needed on the frontend. + +In this template, there is already an ABI included. Remove this ABI and replace it with the one you just copied. + +The ABI in `index.ts` should now look like this, not the removal of the quotes: + +```javascript +const contractABI = [ + { + inputs: [], + stateMutability: 'nonpayable', + type: 'constructor' + }, + { + inputs: [], + name: 'AlreadyVoted', + type: 'error' + }, + { + inputs: [], + name: 'InvalidOption', + type: 'error' + }, + { + ... + name: 'voters', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool' + } + ], + stateMutability: 'view', + type: 'function' + } +] +``` + +## Replacing the Contract Address + +Below the ABI section, there is a definition of the contract address. Locate the address of the Poll contract that you deployed. + +If you did not save this information, it can be located in the Hardhat project at `/deployments/Poll.json` + +The contract address is located at the top of this file. + +`const contractAddress = '0x47C77Da13760F4d063d2103a00D2c7b636131eBD'` + +## Creating Functions and States + +Now that you have setup the contract, it's time to start creating the code to interact with it. + +The first step is using the setState hook in React to setup options and the write contract hook in wagmi. This is done by the following code: + +```typescript +const Home: NextPage = () => { + +const [options, setOptions] = useState>([]) + +const { writeContract, isPending: isVoting } = useWriteContract() +``` + +### Loading the Options and Results + +In this app, the user will have the ability to select the option that applies to them in the poll as well as the results of the poll. To do this we need to use the `useReadContract`. + +Next, add this code under the previous code: + +```typescript +const { data: optionsCountData, isLoading: isLoadingOptionsCount } = useReadContract({ + address: contractAddress, + abi: contractABI, + functionName: "getOptionsCount", +}); + +const optionsCount = optionsCountData ? Number(optionsCountData) : 0; + +const { data: optionData, isLoading: isLoadingOption } = useReadContract({ + address: contractAddress, + abi: contractABI, + functionName: "getOption", + args: [BigInt(options.length)], +}) as { data: { id: bigint; name: string; voteCount: bigint } | undefined; isLoading: boolean }; +``` + +This code gets the `functionName` from the contract and passes the arguments that the function expects. + +### Render the list of Options + +Now that the options are loaded, we need to make a condition to show this to the user. + +You can use the useEffect hook in React to do this: + +```typescript +useEffect(() => { + if (optionData && !isLoadingOption) { + setOptions((prevOptions) => [ + ...prevOptions, + { + id: Number(optionData.id), + name: optionData.name, + voteCount: Number(optionData.voteCount), + }, + ]); + } +}, [optionData, isLoadingOption]); +``` + +### Handling the Vote + +Last but not least, lets create a way for the user to vote. This is done by creating a `handleVote` function that takes in the `id` of the option selected and passes it to the vote function. + +Here is the function: + +```typescript +const handleVote = (optionId: number) => { + writeContract({ + address: contractAddress, + abi: contractABI, + functionName: "vote", + args: [BigInt(optionId)], + }); +}; +``` + +## Displaying the UI + +Now it is time to create the UI where the user will make their choice on the poll. + +First, remove any sample code inside of the `
` tag other than the ``. + +This button uses Rainbow Kit to allow users to connect to their wallet so you must keep it. + +Now add this code directly under the `` component: + +```jsx +

+ Developer DAO Membership Poll +

+ +
+

Voting Results

+ {isLoadingOptionsCount ? ( +

Loading options...

+ ) : ( +
    + {options.map((option) => ( +
  • + {`${option.name} (${option.voteCount} votes)`} +
  • + ))} +
+ )} +
+ +
+

Voting Options

+ {isLoadingOptionsCount ? ( +

Loading options...

+ ) : ( +
    + {options.map((option) => ( +
  • + {`${option.name} (${option.voteCount} votes)`} + +
  • + ))} +
+ )} +
+``` + +This code creates a list to show the voting results and one to show the voting options. + +Since this data lives onchain, each of the components has an `isLoadingOptionsCount`condition to account for the time of the component being rendered before the data is read. + +By going back to where your development server is running, typically http://localhost:3000, you should see the below interface: + +![Dapp Screenshot](/assets/tracks/rootstock-101/4/1.png) + +## Testing the App + +Now comes the time to see if the app works! + +To do this: + +- Connect Your Wallet using the Rainbow Wallet button. +- Make sure you have tRBTC to sign the transaction. If not, head over to the faucet to get some. +- Click on any of the `Vote` buttons +- Your wallet should now open up so that you can sign the transaction + +After signing the transaction, you should now see that the `Voting Results` has increased by 1. + +Also, it's great to test trying to double-vote, you should not be able to. + + + +## Conclusion + +Congrats on finishing this lesson and course! This is only the start of your joining of combining the best of Bitcoin and Ethereum with Rootstock. + +To keep you going, here are some additional resources that will be helpful: + +- [Rootstock Developer Portal](https://dev.rootstock.io/?_gl=1*rhhhf5*_gcl_au*MzA2ODIzMDY5LjE3MjQ2NjM0MjU) +- [Rootstock Collective](https://rootstockcollective.xyz/) - DAO rewarding builders and users of Rootstock + + diff --git a/apps/academy/src/pages/tracks/rootstock-101/index.tsx b/apps/academy/src/pages/tracks/rootstock-101/index.tsx new file mode 100644 index 00000000..3af1f77c --- /dev/null +++ b/apps/academy/src/pages/tracks/rootstock-101/index.tsx @@ -0,0 +1,81 @@ +import Link from "next/link"; +import { useRouter } from "next/router"; +import type { ReactElement } from "react"; +import { TrackCard } from "ui"; + +import PageSeoLayout from "@/components/PageSeoLayout"; +import Spinner from "@/components/Spinner"; +import TracksLayout from "@/components/TracksLayout"; +import { api } from "@/utils/api"; + +const Rootstock101TrackPage = () => { + const router = useRouter(); + + const path = router.pathname; + + const { data: allLessonsData } = api.lessons.getLessonsByTrackPath.useQuery( + { trackPath: path }, + { + refetchOnWindowFocus: false, + }, + ); + + return ( +
+ +
+ {allLessonsData !== undefined && allLessonsData.length > 0 ? ( + allLessonsData.map((lesson, idx) => { + const tagsForThisLesson = lesson.tags.map((tag) => tag.tag.tagName); + return ( + + + + ); + }) + ) : ( + + )} +
+
+
+ ); +}; + +Rootstock101TrackPage.getLayout = function getLayout(page: ReactElement) { + return ( + + {page} + + ); +}; + +export default Rootstock101TrackPage;