Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion biome.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"noUnknownAtRules": "off"
},
"security": {
"noDangerouslySetInnerHtml": "info"
"noDangerouslySetInnerHtml": "off"
}
}
},
Expand Down
171 changes: 171 additions & 0 deletions content/contracts/v5.x/learn/building-a-dapp.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
---
title: Building a dapp
description: Building a dapp
---

<Callout type='warn'>
This article is no longer maintained. Read [here](https://forum.openzeppelin.com/t/doubling-down-in-security/2712) for more info.
</Callout>

Decentralized Applications (aka dapps or ÐApps) are web applications backed by Ethereum smart contracts. Instead of using a centralized server or database, these applications rely on the blockchain as a consensus and coordination layer. This leads to potentially unstoppable applications: anyone can spin up a copy of the frontend, and freely connect it to the public Ethereum network.

In this guide, we will see how we can use OpenZeppelin libraries such as [Network JS](network-js::index.mdx) and the [hot-loader](https://github.com/OpenZeppelin/solidity-loader) to build your dapp, as well as how the [Starter Kits](starter-kits::index.mdx) bind all pieces of a dapp together:

* [Connecting to the Network](#connecting-to-the-network)
* [Hot-loading your Contracts](#hot-loading-contracts)
* [Kickstarting a Dapp using Starter Kits](#kickstarting-your-dapp)

If you are in a rush, just skip over to [the last section](#kickstarting-your-dapp)!

## Connecting to the Network

When building a dapp, the first step is to connect to the Ethereum network. This usually involves the following steps:

1. If the user is running Metamask, use the injected web3 provider to access the network, and monitor for network or account changes
2. If not, fall back to a public Infura node

To avoid having to code this logic on every dapp, we built [**OpenZeppelin Network JS**](network-js::index.mdx) to take care of it. It provides a `useWeb3` [React hook](https://reactjs.org/docs/hooks-intro.html) for getting a `web3Context` based on the logic above. This `web3Context` object provides access to a connected `web3` instance, network information, the user accounts, a function to [request account access](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1102.md), and more.

The following snippet is a small React app that connects to the network using Metamask (falling back to Infura if not found), prompts the user for account access, and displays the user balance:

```js
import React, useCallback, useState, useEffect from 'react';
import useWeb3 from '@openzeppelin/network/react';

function App()
// Get web3 context via @openzeppelin/network/react hook
const web3Context = useWeb3(`wss://mainnet.infura.io/ws/v3/${PROJECT_ID`);
const lib: web3, networkId, accounts, providerName = web3Context;

// Methods for requesting accounts access
const requestAuth = (web3Context) => web3Context.requestAuth();
const requestAccess = useCallback(() => requestAuth(web3Context), []);

// Querying account balance
const [balance, setBalance] = useState(0);
const getBalance = useCallback(async () =>
setBalance(
accounts && accounts.length > 0
? (await web3.eth.getBalance(accounts[0]))
: 'Unknown')
, [accounts, web3.eth, web3.utils]);
useEffect(() =>
getBalance();
, [accounts, getBalance, networkId]);

// Show all information to the user
return (
<div>
<div>Network: networkId || 'No connection'</div>
<div>Your address: accounts ? accounts[0] : 'Unknown'</div>

accounts && accounts.length ? (
<div>Accounts access granted</div>
) : !!networkId && providerName !== 'infura' ? (
<button onClick={requestAccess>Request Access</button>
) : (
<div>No accounts access</div>
)}
div>

}
```

In the snippet above, with just the first two lines we get access to the Ethereum network via Metamask and falling back to Infura, as well as information on the network, provider, and even user accounts - all of this through the `useWeb3` hook provided by Network JS.

<Callout>
You can easily [enable meta-transaction support](sending-gasless-transactions.mdx) in your dapp when using Network JS by [adding a `gsn` option](network-js::api.mdx#webcontextoptions) to the React hook.
</Callout>

To learn more, head to our step-by-step tutorial on [Building a Dapp from Scratch](network-js::building-a-dapp-from-scratch.mdx) using the Networks library.

## Hot-loading Contracts

Now that we have a working connection to the Ethereum network, we can start interacting with the contracts we have coded and [deployed using the OpenZeppelin CLI](deploying-and-interacting.mdx).

While we _could_ create a regular `web3` contract instance [from ABI and address](contract-loader::index.mdx), we will instead leverage [contract upgrades](upgrading-smart-contracts.mdx) to hot-load our contracts.

This means that once we deploy and load a contract in our running React dapp, the app will ***automatically pick up any changes to the Solidity code and upgrade the contract*** during development. The [**OpenZeppelin Hot Loader**](https://github.com/OpenZeppelin/solidity-loader) will watch all your Solidity contracts for changes, and automatically recompile and upgrade the contract on the local development network - keeping the same address and state, and just changing the code to the new version you wrote.

<Callout>
The Hot Loader is a [webpack](https://webpack.js.org/) plugin. It will not work if you are using a different bundler for your application.
</Callout>

To set up the hot-loader, you need to install `@openzeppelin/solidity-loader` and `json-loader`, and add the following [rule](https://webpack.js.org/configuration/module/#rule) to your webpack configuration:

```js
test: /\.sol$/,
use: [
loader: 'json-loader' ,

loader: '@openzeppelin/solidity-loader',
options: {
network: 'development',
disabled: false
,
},
],
```

<Callout>
If you are using `create-react-app`, you may need to either eject the application to modify the webpack configuration, or use [`react-app-rewired`](https://github.com/timarney/react-app-rewired).
</Callout>

Then, just load the contract Solidity file from your client code, which will go through the Hot Loader. Whenever you modify your contract source, the loader will automatically upgrade the instance to the latest code for you.

```js
const networkId, lib: web3 = useWeb3Network('http://127.0.0.1:8545');

const Box = require('../../contracts/Box.sol');

const [box, setBox] = useState(undefined);
if (!box && Box && Box.networks && networkId)
const deployedNetwork = Box.networks[networkId.toString()];
if (deployedNetwork) {
setBox(new web3.eth.Contract(Box.abi, deployedNetwork.address));

}
```

For detailed setup instructions, follow our step-by-step tutorial on [Enabling the Hot Loader in a React web application](https://forum.openzeppelin.com/t/building-an-openzeppelin-dapp-with-solidity-hot-loader/1843).

## Kickstarting your Dapp

As you have seen so far, setting up a dapp involves a fair amount of boilerplate and configuration, including creating an OpenZeppelin CLI project, initializing a new webpack client app, configuring network access and loading your contracts.

To kickstart this process, we have built the [**OpenZeppelin Starter Kits**](starter-kits::index.mdx). Starter Kits are preconfigured dapps with several OpenZeppelin libraries, Infura connections, and [Rimble UI components](https://github.com/ConsenSys/rimble-ui), ready to start developing right away.

You can start a new project from a starter kit using the `oz unpack` CLI command:

```console
$ oz unpack starter
✓ Kit downloaded and unpacked
The kit is ready to use.

Quick Start
Run your local blockchain:
> ganache-cli --deterministic
Initialize the OpenZeppelin project:
> openzeppelin init app
Go to the client directory:
> cd client
Run the React app:
> npm run start
Continue in your browser!
More at https://github.com/OpenZeppelin/starter-kit/tree/stable
```

This will unpack a preconfigured React dapp, with a network connection set up to both Metamask and the local node. Each box in the dapp shows the information from each connection, each obtained from a different `web3Context`: network ID, provider, accounts, and balance. On the Metamask side, you need to request access to the user accounts before obtaining them from the plugin.

![OpenZeppelin Starter Kit](/StarterKit.png)

Having this initial setup, you can now modify it to start building your own application on top of it - as you would do in a vanilla [`create-react-app` setup](https://create-react-app.dev/).

If you want to learn more about using Starter Kits, `unpack` the [`tutorial`](starter-kits::tutorial.mdx) kit instead of `starter`: it will guide you through the process of deploying and interacting with contracts from a dapp. You can also check out the [list of all starter kits](starter-kits::list.mdx) available to unpack.

## Next steps

You have taken the first steps towards building a decentralized web-based front-end for your smart contracts. Your next tasks should be:

* [Writing Automated Tests](writing-automated-tests.mdx)
* [Connecting to Public Test Networks](connecting-to-public-test-networks.mdx)
166 changes: 166 additions & 0 deletions content/contracts/v5.x/learn/connecting-to-public-test-networks.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
---
title: Connecting to public test networks
description: Connecting to public test networks
---

After you have [written your contracts](developing-smart-contracts.mdx), and [tried them out locally](deploying-and-interacting.mdx) and [tested them thoroughly](writing-automated-tests.mdx), it’s time to move to a persistent public testing environment, where you and your beta users can start interacting with your application.

We will use **public testing networks** (aka _testnets_) for this, which are networks that operate similar to the main Ethereum network, but where Ether has no value and is free to acquire - making them ideal for testing your contracts at no cost.

In this guide, we will use our beloved [`Box` contract](developing-smart-contracts.mdx#box-contract), and deploy it to a testnet, while learning:

* [What test networks are available](#available-testnets)
* [How to set up your project for working on a testnet](#connecting-a-project-to-a-public-network)
* [How to deploy and interact with your testnet contract instances](#working-on-a-testnet)

Remember that deploying to a public test network is a necessary step when developing an Ethereum project. They provide a safe environment for testing that closely mimics the main network - you don’t want to take out your project for a test drive in a network where mistakes will cost you and your users money!

## Available testnets

There are a number of test networks available for you to choose, each with their own characteristics. The recommended network for testing decentralized applications and smart contracts is Sepolia. (id=11155111)

<Callout>
Each network is identified by a numeric ID. Local networks usually have a large random value, while id=1 is reserved for the main Ethereum network.
</Callout>

## Connecting a project to a public network

To connect our project to a public testnet, we will need to:

1. [Get hold of a testnet node](#accessing-a-testnet-node)
2. [Create a new account](#creating-a-new-account)
3. [Update our configuration file](#configuring-the-network)
4. [Fund our testing account](#funding-the-testnet-account)

### Accessing a testnet node

While you can spin up your own [Ethereum nodes](https://ethereum.org/en/developers/docs/nodes-and-clients/run-a-node/) connected to a testnet, the easiest way to access a testnet is via a public node service such as [Alchemy](https://alchemy.com/) or [Infura](https://infura.io). Alchemy and Infura provide access to public nodes for all testnets and the main network, via both free and paid plans.

<Callout>
We say a node is _public_ when it can be accessed by the general public, and manages no accounts. This means that it can reply to queries and relay signed transactions, but cannot sign transactions on its own.
</Callout>

In this guide we will use Alchemy, though you can use [Infura](https://infura.io/), or another public node provider of your choice.

Head over to [Alchemy](https://dashboard.alchemyapi.io/signup?referral=53fcee38-b894-4d5f-bd65-885d241f8d29) (includes referral code), sign up, and jot down your assigned API key - we will use it later to connect to the network.

### Creating a new account

To send transactions in a testnet, you will need a new Ethereum account. There are many ways to do this: here we will use the `mnemonics` package, which will output a fresh mnemonic (a set of 12 words) we will use to derive our accounts:

```console
$ npx mnemonics
drama film snack motion ...
```

<Callout type='warn'>
Make sure to keep your mnemonic secure. Do not commit secrets to version control. Even if it is just for testing purposes, there are still malicious users out there who will wreak havoc on your testnet deployment for fun!
</Callout>

### Configuring the network

Since we are using public nodes, we will need to sign all our transactions locally. We will configure the network with our mnemonic and an Alchemy endpoint.

<Callout>
This part assumes you have already set up a project. If you haven’t, head over to the guide on [Setting up a Solidity project](developing-smart-contracts.mdx#setting-up-a-solidity-project).
</Callout>

We need to update our configuration file with a new network connection to the testnet. Here we will use Sepolia, but you can use whichever you want:
```diff
// hardhat.config.js
+ const alchemyApiKey, mnemonic = require('./secrets.json');
...
module.exports =
+ networks: {
+ sepolia: {
+ url: `https://eth-sepolia.g.alchemy.com/v2/${alchemyApiKey`,
+ accounts: mnemonic: mnemonic ,
+ },
+ },
...
};
```

<Callout>
See the Hardhat [networks configuration](https://hardhat.org/config/#json-rpc-based-networks) documentation for information on configuration options.
</Callout>
Note in the first line that we are loading the project id and mnemonic from a `secrets.json` file, which should look like the following, but using your own values. Make sure to `.gitignore` it to ensure you don’t commit secrets to version control!

```json

"mnemonic": "drama film snack motion ...",
"alchemyApiKey": "JPV2..."

```

<Callout>
Instead of a `secrets.json` file, you can use whatever secret-management solution you like for your project. A popular and simple option is to use [`dotenv`](https://github.com/motdotla/dotenv) for injecting secrets as environment variables.
</Callout>

We can now test out that this configuration is working by listing the accounts we have available for the Sepolia network. Remember that yours will be different, as they depend on the mnemonic you used.

```console
$ npx hardhat console --network sepolia
Welcome to Node.js v20.17.0.
Type ".help" for more information.
> accounts = (await ethers.getSigners()).map(signer => signer.address)
[
'0x6B1c3A2f2160a7Cb2ebc7Fc861b8dB71476C30E7',
'0xC1310ade58A75E6d4fCb8238f9559188Ea3808f9',
...
]
```
We can also test the connection to the node, by querying our account balance.
```console
> (await ethers.provider.getBalance(accounts[0])).toString()
'0'
```

Empty! This points to our next task: getting testnet funds so that we can send transactions.

### Funding the testnet account

Most public testnets have a faucet: a site that will provide you with a small amount of test Ether for free. If you are on Sepolia, head to [Alchemy’s free Sepolia faucet](https://www.alchemy.com/faucets/ethereum-sepolia), [Infura’s free Sepolia faucet](https://www.infura.io/faucet), or [Google’s free Sepolia faucet](https://cloud.google.com/application/web3/faucet/ethereum/sepolia) to get free testETH.

Armed with a funded account, let’s deploy our contracts to the testnet!

## Working on a testnet

With a project configured to work on a public testnet, we can now finally [deploy our `Box` contract](:deploying-and-interacting.adoc#deploying-a-smart-contract). The command here is exactly the same as if you were on your [local development network](:deploying-and-interacting.adoc#local-blockchain), though it will take a few seconds to run as new blocks are mined.

```console
$ npx hardhat run --network sepolia scripts/deploy.js
Deploying Box...
Box deployed to: 0x1b99CCaCea0e4046db618770dEF72180F8138641
```

That’s it! Your `Box` contract instance will be forever stored in the testnet, and publicly accessible to anyone.

You can see your contract on a block explorer such as [Etherscan](https://etherscan.io/). Remember to access the explorer on the testnet where you deployed your contract, such as [sepolia.etherscan.io](https://sepolia.etherscan.io) for Sepolia.

<Callout>
You can check out the contract we deployed in the example above, along with all transactions sent to it, [here](https://sepolia.etherscan.io/address/0x1b99CCaCea0e4046db618770dEF72180F8138641).
</Callout>

You can also interact with your instance as you regularly would, either using the [console](:deploying-and-interacting.adoc#interacting-from-the-console), or [programmatically](:deploying-and-interacting.adoc#interacting-programatically).

```console
$ npx hardhat console --network sepolia
Welcome to Node.js v20.17.0.
Type ".help" for more information.
> const Box = await ethers.getContractFactory('Box');
undefined
> const box = await Box.attach('0x1b99CCaCea0e4046db618770dEF72180F8138641');
undefined
> await box.store(42);
{
hash: '0x330e331d30ee83f96552d82b7fdfa6156f9f97d549a612eeef7283d18b31d107',
...
> (await box.retrieve()).toString()
'42'
```
Keep in mind that every transaction will cost some gas, so you will eventually need to top up your account with more funds.

## Next steps

After thoroughly testing your application on a public testnet, you are ready for the last step on the development journey: [deploying your application in production](preparing-for-mainnet.mdx).
Loading