diff --git a/languages/en/01_QuickStart/Web3.tsx b/languages/en/01_QuickStart/Web3.tsx
new file mode 100644
index 0000000..5c9152b
--- /dev/null
+++ b/languages/en/01_QuickStart/Web3.tsx
@@ -0,0 +1,20 @@
+import { http } from "wagmi";
+import { Mainnet, WagmiWeb3ConfigProvider } from '@ant-design/web3-wagmi';
+import { Address, NFTCard} from "@ant-design/web3";
+
+export default function Web3() {
+ return (
+
+
+
+
+ );
+}
diff --git a/languages/en/01_QuickStart/img/add-render.png b/languages/en/01_QuickStart/img/add-render.png
new file mode 100644
index 0000000..468b7d4
Binary files /dev/null and b/languages/en/01_QuickStart/img/add-render.png differ
diff --git a/languages/en/01_QuickStart/img/init-next.png b/languages/en/01_QuickStart/img/init-next.png
new file mode 100644
index 0000000..f9f9138
Binary files /dev/null and b/languages/en/01_QuickStart/img/init-next.png differ
diff --git a/languages/en/01_QuickStart/img/next-init-page.png b/languages/en/01_QuickStart/img/next-init-page.png
new file mode 100644
index 0000000..70fb5ab
Binary files /dev/null and b/languages/en/01_QuickStart/img/next-init-page.png differ
diff --git a/languages/en/01_QuickStart/img/nft-card.png b/languages/en/01_QuickStart/img/nft-card.png
new file mode 100644
index 0000000..a4c129c
Binary files /dev/null and b/languages/en/01_QuickStart/img/nft-card.png differ
diff --git a/languages/en/01_QuickStart/img/vite-page.png b/languages/en/01_QuickStart/img/vite-page.png
new file mode 100644
index 0000000..dd8b6ee
Binary files /dev/null and b/languages/en/01_QuickStart/img/vite-page.png differ
diff --git a/languages/en/01_QuickStart/readme.md b/languages/en/01_QuickStart/readme.md
new file mode 100644
index 0000000..47ca901
--- /dev/null
+++ b/languages/en/01_QuickStart/readme.md
@@ -0,0 +1,228 @@
+This course is mainly aimed at students with a certain foundation in front-end development, helping you move from Web2 to Web3 and acquire the R&D capabilities of DApp (decentralized applications).
+
+The course will be based on Ant Design Web3 , so you can get started more easily. Of course, this will not affect your understanding of the basic concepts. We will explain the relevant concepts in the course to ensure that you can master the basic knowledge of DApp development after completing the course.
+
+This course has certain prerequisites, requiring you to have a basic understanding of React front-end development. If you are not familiar with [React](https://react.dev/), you can study the [React official documentation first.](https://react.dev/learn)
+
+---
+
+## Initialize a React project
+
+We will initialize based on [React](https://react.dev/) + [Vite](https://vite.dev/) + [TypeScript](https://www.typescriptlang.org/) our projects. Of course, if you are more familiar with other front-end frameworks such as [umi](https://umijs.org/), you can also use the framework you are familiar with. You can still refer to this tutorial, but for non-professional front-end developers, we recommend following our tutorial step by step to avoid problems caused by some framework differences.
+
+Before starting, please make sure you have [Node.js](https://nodejs.org/) installed and the version is greater than 20.0.0. The tutorial will be written based on the latest Node.js version. If you are using an older version of Node.js, it may also work, but when you encounter problems, you can try upgrading the Node.js version.
+
+After the installation is complete, you can check whether Node.js and its own `npm` and `npx` are installed successfully through the following commands:
+
+```bash
+node -v # => v20.0.0+
+npm -v # => 10.0.0+
+npx -v # => 10.0.0+
+```
+
+Next, let's refer to the [Vite official documentation](https://vite.dev/guide/) to create a new project:
+
+``` bash
+npm create vite@latest
+```
+
+Please follow the prompts to create a new project. We will name it as follows `dApp_test`. For technology stack:
+
+- Select the `React` framework
+- Select the `TypeScript` variant
+
+Open the new folder you just created with:
+```
+cd dApp_test
+```
+Initiate the folder to open in VS Code with:
+```
+code .
+```
+
+Open your VS Code terminal and install the dependencies:
+```
+npm install
+```
+
+## Install dApp dependencies and start the project
+
+After the creation is completed, enter the project directory to install the dependencies:
+
+```base
+cd ant-design-web3-demo
+npm i
+```
+
+After the installation is complete, execute `npm run dev` to start the Project. You can visit the browser `http://localhost:5173` to check whether the project has started successfully.
+
+![img2](./img/vite-page.png)
+
+## Add Ant Design Web3
+
+Next, we install the basic components of [Ant Design](https://ant.design/) and [Ant Design Web3](https://web3.ant.design/) and other dependencies into the project:
+
+```bash
+npm i antd @ant-design/web3 @ant-design/web3-wagmi wagmi @tanstack/react-query --save
+```
+
+- `@ant-design/web3` is a UI component library that connects to different blockchains through different [adapters](../guide/adapter.zh-CN.md). In this course, we are mainly based on [Ethereum](https://ethereum.org/zh/). Correspondingly, we will also use [Ethereum adapter](../../packages/web3/src/wagmi/index.zh-CN.md) to implement the course requirements.
+
+- [wagmi](https://wagmi.sh/) is an open source React Hooks library that serves Ethereum and relies on `@tanstack/react-query`. The adapter `@ant-design/web3-wagmi` of Ant Design Web3 is implemented based on it. In the later part of this course, if there is no special instructions, the adapter mentioned refers to `@ant-design/web3-wagmi` .
+
+
+After installation, add Vite support pacakages:
+```bash
+npm install vite-plugin-imp -D
+```
+
+Then copy the code below and paste it in vite.config.ts (or vite.config.js if you selected javascript during Vite installation)
+
+```bash
+import { defineConfig } from 'vite';
+import react from '@vitejs/plugin-react';
+import vitePluginImp from 'vite-plugin-imp';
+
+export default defineConfig({
+ plugins: [
+ react(),
+ vitePluginImp({
+ libList: [
+ {
+ libName: 'antd',
+ style: (name) => `antd/es/${name}/style`,
+ },
+ ],
+ }),
+ ],
+});
+```
+
+Afterwards, in `src`, create a `components` folder and create `Web3.jsx` file in it. Paste the following content in it:
+
+```tsx | pure
+import { Address } from "@ant-design/web3";
+
+export default function Web3() {
+ return (
+
+ );
+}
+```
+
+Then install:
+
+```bash
+npm install react-router-dom
+```
+
+In App.tsx, set up a routing path:
+
+```bash
+import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
+import Web3 from './components/Web3';
+
+function App() {
+ return (
+
+
+ Welcome to my dApp testing ground} />
+ } />
+
+
+ );
+}
+
+export default App;
+```
+
+Now, you can go to http://localhost:5173/Web3 to see the address renderers.
+
+![img3](./img/add-render.png)
+
+
+## Adapter Configuration
+
+The adapter configuration closely follows the guidelines outlined in the [official wagmi documentation](https://wagmi.sh/core/getting-started). For real-world projects, you will generally need to set up the JSON RPC endpoint and configure various wallets. This course begins with the most basic setup and will progressively help you understand the necessary configurations for your specific project.
+
+To start, open the `src/components/Web3.tsx` file and import the components or modules required for the configuration process.
+
+Starting with:
+```bash
+import { createConfig, http } from 'wagmi';
+import { mainnet } from 'wagmi/chains';
+import { WagmiWeb3ConfigProvider } from '@ant-design/web3-wagmi';
+import { Address } from "@ant-design/web3";
+
+export default function Web3() {
+ return (
+
+ );
+};
+```
+
+The content provided explains the following components and concepts related to the wagmi library and Ant Design Web3:
+
+- **[createConfig](https://wagmi.sh/react/config):** This is a method from the wagmi library used to set up a configuration for your application.
+- **HTTP Transport:** This method, part of the wagmi library, sets up an [HTTP JSON RPC](https://wagmi.sh/core/api/transports/http) connection. This connection allows you to interact with Ethereum or other compatible blockchains using HTTP requests.
+- **[Mainnet and Other Networks](https://wagmi.sh/react/chains):** The term "mainnet" refers to the Ethereum mainnet. Besides the mainnet, there are test networks like `sepolia`, and other public chains compatible with the Ethereum Virtual Machine (EVM), such as `bsc` (Binance Smart Chain) and `base`. These chains include both Layer 1 (L1) chains like Ethereum and Layer 2 (L2) solutions, although details on L2 chains are not covered in this section.
+- **[WagmiWeb3ConfigProvider](https://web3.ant.design/zh-CN/components/wagmi#wagmiweb3configproviderprops):** This is a component in Ant Design Web3 that acts as a provider for receiving configurations from the wagmi library.
+
+Next, you'll need to proceed with setting up your configuration.
+
+```bash
+import { createConfig, http } from "wagmi";
+import { mainnet } from "wagmi/chains";
+import { WagmiWeb3ConfigProvider } from "@ant-design/web3-wagmi";
+import { Address } from "@ant-design/web3";
+
+const config = createConfig({
+ chains: [mainnet],
+ transports: {
+ [mainnet.id]: http(),
+ },
+ });
+
+export default function Web3() {
+ return (
+
+
+
+ );
+};
+```
+
+With the basic configuration for wagmi now complete, we can proceed to use Ant Design Web3 components to access data from the blockchain.
+
+As an example, let's explore how to use the NFTCard component from Ant-Design
+
+```bash
+import { createConfig, http } from "wagmi";
+import { mainnet } from "wagmi/chains";
+import { WagmiWeb3ConfigProvider } from "@ant-design/web3-wagmi";
+import { Address } from "@ant-design/web3";
+import { Address, NFTCard } from "@ant-design/web3";
+
+const config = createConfig({
+ chains: [mainnet],
+ transports: {
+ [mainnet.id]: http(),
+ },
+});
+
+export default function Web3() {
+ return (
+
+
+
+
+ );
+};
+```
+The `NFTCard` component fetches the NFT data for tokenId 641 from the contract located at [0xEcd0D12E21805803f70de03B72B1C162dB0898d9](https://etherscan.io/address/0xEcd0D12E21805803f70de03B72B1C162dB0898d9) and displays it on the page.
+
+Here’s what it should look like:
+
+![img3](./img/nft-card.png)
+
+If the NFT doesn't display, please check your network connection. If you can see the NFT image rendered successfully, you've completed this lesson! Congratulations!
diff --git a/languages/en/02_NodeServices/Web3.tsx b/languages/en/02_NodeServices/Web3.tsx
new file mode 100644
index 0000000..9b97d52
--- /dev/null
+++ b/languages/en/02_NodeServices/Web3.tsx
@@ -0,0 +1,20 @@
+import { http } from "wagmi";
+import { Mainnet, WagmiWeb3ConfigProvider } from '@ant-design/web3-wagmi';
+import { Address, NFTCard} from "@ant-design/web3";
+
+export default function Web3() {
+ return (
+
+
+
+
+ );
+}
diff --git a/languages/en/02_NodeServices/img/faucet.png b/languages/en/02_NodeServices/img/faucet.png
new file mode 100644
index 0000000..c45eade
Binary files /dev/null and b/languages/en/02_NodeServices/img/faucet.png differ
diff --git a/languages/en/02_NodeServices/img/zan-service.png b/languages/en/02_NodeServices/img/zan-service.png
new file mode 100644
index 0000000..b9296f2
Binary files /dev/null and b/languages/en/02_NodeServices/img/zan-service.png differ
diff --git a/languages/en/02_NodeServices/readme.md b/languages/en/02_NodeServices/readme.md
new file mode 100644
index 0000000..47e7dda
--- /dev/null
+++ b/languages/en/02_NodeServices/readme.md
@@ -0,0 +1,45 @@
+## Node Service: A Key Element in DApp Development
+
+In this session, we'll explore the concept of node services and demonstrate how to set them up in your project. We'll also guide you on obtaining Sepolia testnet ETH through a faucet.
+
+## Understanding Node Services
+
+Node services are fundamental to DApp development. They function as services within the blockchain network, enabling interactions with the blockchain. In DApp development, node services are essential for retrieving blockchain data, sending transactions, and performing other key operations.
+
+On the Ethereum network, platforms like [ZAN](https://zan.top?chInfo=wtf), [Infura](https://infura.io/), and [Alchemy](https://www.alchemy.com/) provide access to these node services. While all these platforms offer free node services, they also provide premium options for enhanced performance if your application demands it.
+
+## Setting Up a Node Service
+
+We'll use [ZAN's node service](https://zan.top/home/node-service?chInfo=wtf) as an example to illustrate how to configure a node service.
+
+Start by registering and logging in at [https://zan.top](https://zan.top?chInfo=wtf). Once logged in, go to the node service console at [https://zan.top/service/apikeys](https://zan.top/service/apikeys?chInfo=wtf) to generate an API Key. Each API Key includes a default free quota, adequate for small-scale projects. For larger, production-level projects, you may need to purchase additional node services based on your specific requirements.
+
+After creating your API Key, you will see a screen similar to this:
+
+![](./img/zan-service.png)
+
+Copy the Ethereum mainnet node service address displayed, and integrate it into wagmi's `http()` method as follows:
+
+```bash
+const config = createConfig({
+ chains: [mainnet],
+ transports: {
+- [mainnet.id]: http(),
++ [mainnet.id]: http('https://api.zan.top/node/v1/eth/mainnet/{YourZANApiKey}'),
+ },
+});
+```
+
+In the code above, replace `YourZANApiKey` with your personal key. To protect your key from unauthorized use in real projects, consider placing it in a backend service, which will then handle calls to the node service. Alternatively, you can configure a domain whitelist in ZAN's console to minimize the risk of misuse. However, for this tutorial, you can use `http()` directly and leverage wagmi's built-in experimental node service.
+
+If you're using node services from Infura or Alchemy, you can also configure their service addresses in wagmi's `http()` method.
+
+## Getting Testnet ETH from a Faucet
+
+In addition to node services, testnet ETH is crucial for development. You can typically obtain it from faucet services, which are online platforms that provide small amounts of free cryptocurrency for testing in a development environment. These services are often offered by testnet administrators, developer communities, or node service providers.
+
+For instance, you can acquire Sepolia testnet ETH for testing through [ZAN's faucet service](https://zan.top/faucet?chInfo=wtf).
+
+![faucet](./img/faucet.png)
+
+Please claim a suitable amount of Sepolia testnet ETH from the faucet webpage shown above, as it may be needed in future lessons.
diff --git a/languages/en/03_ConnectWallet/Web3.tsx b/languages/en/03_ConnectWallet/Web3.tsx
new file mode 100644
index 0000000..5f6c679
--- /dev/null
+++ b/languages/en/03_ConnectWallet/Web3.tsx
@@ -0,0 +1,32 @@
+import { createConfig, http } from "wagmi";
+import { mainnet } from "wagmi/chains";
+ import { WagmiWeb3ConfigProvider, MetaMask } from "@ant-design/web3-wagmi";
+ import { Address, NFTCard, Connector, ConnectButton } from "@ant-design/web3";
+ import { injected } from "wagmi/connectors";
+
+const config = createConfig({
+ chains: [mainnet],
+ transports: {
+ [mainnet.id]: http(),
+ },
+ connectors: [
+ injected({
+ target: "metaMask",
+ }),
+ ],
+});
+
+export default function Web3() {
+ return (
+
+
+
+
+
+
+
+ );
+};
diff --git a/languages/en/03_ConnectWallet/img/connect.png b/languages/en/03_ConnectWallet/img/connect.png
new file mode 100644
index 0000000..0514bf6
Binary files /dev/null and b/languages/en/03_ConnectWallet/img/connect.png differ
diff --git a/languages/en/03_ConnectWallet/readme.md b/languages/en/03_ConnectWallet/readme.md
new file mode 100644
index 0000000..64b84d0
--- /dev/null
+++ b/languages/en/03_ConnectWallet/readme.md
@@ -0,0 +1,53 @@
+Connecting a wallet is a vital interaction in any decentralized application (DApp). In this session, we will walk you through the process of implementing a wallet connection feature using [wagmi](https://wagmi.sh) and [Ant Design Web3](https://web3.ant.design).
+
+## How Wallets Connect in DApps
+
+For DApps, connecting to a wallet is essential to obtain the user's wallet address and to execute user-authorized actions like sending transactions or signing messages. In the Ethereum ecosystem, there are typically three ways to connect a wallet:
+
+1. Using a browser extension.
+2. Accessing the DApp through a wallet app.
+3. Employing the WalletConnect protocol.
+
+The first two methods rely on interfaces that the wallet injects into the browser's runtime environment, whereas WalletConnect uses server-side relays. Wallet injection interfaces can be implemented in two ways: via [EIP-1193](https://eips.ethereum.org/EIPS/eip-1193) or [EIP-6963](https://eips.ethereum.org/EIPS/eip-6963). EIP-1193 is an earlier, relatively straightforward protocol; we will begin by using it to attempt a wallet connection.
+
+## Setting Up the Wallet
+
+Let's use [MetaMask](https://metamask.io/) as an example to illustrate how to connect to a wallet.
+
+```bash
+import { createConfig, http } from "wagmi";
+import { mainnet } from "wagmi/chains";
+ import { WagmiWeb3ConfigProvider, MetaMask } from "@ant-design/web3-wagmi";
+ import { Address, NFTCard, Connector, ConnectButton } from "@ant-design/web3";
+ import { injected } from "wagmi/connectors";
+
+const config = createConfig({
+ chains: [mainnet],
+ transports: {
+ [mainnet.id]: http(),
+ },
+ connectors: [
+ injected({
+ target: "metaMask",
+ }),
+ ],
+});
+
+export default function Web3() {
+ return (
+
+
+
+
+
+
+
+ );
+};
+```
+When you click on 'Connect Wallet' on your frontend, you should see a pop up like this:
+
+![](./img/connect.png)
diff --git a/languages/en/04_CallContract/Web3.tsx b/languages/en/04_CallContract/Web3.tsx
new file mode 100644
index 0000000..03d34d9
--- /dev/null
+++ b/languages/en/04_CallContract/Web3.tsx
@@ -0,0 +1,104 @@
+import {
+ Address,
+ ConnectButton,
+ Connector,
+ NFTCard,
+ useAccount,
+} from "@ant-design/web3";
+import { MetaMask, WagmiWeb3ConfigProvider } from "@ant-design/web3-wagmi";
+import { Button, message } from "antd";
+import { parseEther } from "viem";
+import { createConfig, http, useReadContract, useWriteContract } from "wagmi";
+import { mainnet } from "wagmi/chains";
+import { injected } from "wagmi/connectors";
+
+const config = createConfig({
+ chains: [mainnet],
+ transports: {
+ [mainnet.id]: http(),
+ },
+ connectors: [
+ injected({
+ target: "metaMask",
+ }),
+ ],
+});
+
+const CallTest = () => {
+ const { account } = useAccount();
+ const result = useReadContract({
+ abi: [
+ {
+ type: "function",
+ name: "balanceOf",
+ stateMutability: "view",
+ inputs: [{ name: "account", type: "address" }],
+ outputs: [{ type: "uint256" }],
+ },
+ ],
+ // Sepolia test contract 0x418325c3979b7f8a17678ec2463a74355bdbe72c
+ address: "0xEcd0D12E21805803f70de03B72B1C162dB0898d9",
+ functionName: "balanceOf",
+ args: [account?.address as `0x${string}`],
+ });
+ const { writeContract } = useWriteContract();
+
+ return (
+
+ {result.data?.toString()}
+
+
+ );
+};
+
+export default function Web3() {
+ return (
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/languages/en/04_CallContract/img/call-contract.png b/languages/en/04_CallContract/img/call-contract.png
new file mode 100644
index 0000000..f21a826
Binary files /dev/null and b/languages/en/04_CallContract/img/call-contract.png differ
diff --git a/languages/en/04_CallContract/img/metamask.png b/languages/en/04_CallContract/img/metamask.png
new file mode 100644
index 0000000..6310562
Binary files /dev/null and b/languages/en/04_CallContract/img/metamask.png differ
diff --git a/languages/en/04_CallContract/img/no-gas.png b/languages/en/04_CallContract/img/no-gas.png
new file mode 100644
index 0000000..067e458
Binary files /dev/null and b/languages/en/04_CallContract/img/no-gas.png differ
diff --git a/languages/en/04_CallContract/img/zan-api-doc.png b/languages/en/04_CallContract/img/zan-api-doc.png
new file mode 100644
index 0000000..dc18437
Binary files /dev/null and b/languages/en/04_CallContract/img/zan-api-doc.png differ
diff --git a/languages/en/04_CallContract/readme.md b/languages/en/04_CallContract/readme.md
new file mode 100644
index 0000000..224f780
--- /dev/null
+++ b/languages/en/04_CallContract/readme.md
@@ -0,0 +1,270 @@
+The frontend of a decentralized application (DApp) is distinct from that of a traditional app because it needs to communicate with the blockchain. This communication is primarily done through smart contracts. In this session, we will explore how to interact with smart contracts.
+
+## How a DApp Interacts with Smart Contracts
+
+Using Ethereum as an example, after a smart contract is deployed on the blockchain, we can call its methods by creating an Ethereum transaction, as long as we have the contract's Application Binary Interface (ABI) and its address.
+
+> The ABI is a standard that defines the interface of a smart contract, detailing its functions and parameters. The contract address, sometimes referred to as a "hash," is the unique identifier of the contract on the blockchain. Both the ABI and the address are available when you deploy the smart contract.
+
+A DApp can usually interact with contract methods in two main ways: by using a wallet plugin or by directly connecting through node Remote Procedure Call (RPC). We will focus primarily on the first method—using a wallet plugin.
+
+### Using MetaMask Wallet
+
+[MetaMask](https://metamask.io/) is the most popular wallet plugin in the Ethereum ecosystem. It allows users to manage their Ethereum assets directly from their browser and acts as a bridge for DApps to interact with the Ethereum network. If you haven't used it before, you can [download](https://metamask.io/download/) and install it from their website, and follow the official setup guide. Alternatively, you can also use other wallets like [TokenPocket](https://www.tokenpocket.pro/) or [imToken](https://token.im/).
+
+![metamask](./img/metamask.png)
+
+Once MetaMask is installed, you'll see its icon in the top right corner of your browser. MetaMask also injects a `window.ethereum` object into every webpage, which serves as an interface for DApps to communicate with the Ethereum network. For example, you can send an `eth_chainId` RPC request to find out the current network's ID.
+
+```bash
+await window.ethereum.request({ method: "eth_chainId" }); // 0x1 represents the Ethereum mainnet
+```
+
+We can also get the account address and other information of the current wallet through the following code:
+
+```bash
+async function getAccount() {
+ const accounts = await window.ethereum
+ .request({ method: "eth_requestAccounts" })
+ .catch((err) => {
+ if (err.code === 4001) {
+ // EIP-1193 userRejectedRequest error
+ // If this happens, the user rejected the connection request.
+ console.log("Please connect to MetaMask.");
+ } else {
+ console.error(err);
+ }
+ });
+ const account = accounts[0];
+ return account;
+}
+
+await getAccount(); // your account address
+```
+
+For detailed information on wallet RPC and API, please refer to the [official MetaMask documentation](https://docs.metamask.io/guide/rpc-api.html#other-rpc-methods).
+
+### Accessing Blockchain Data via Node RPC
+
+As we have previously learned, a blockchain is a decentralized network that allows us to access its data by connecting to one of its nodes. The Ethereum network, for example, consists of many nodes. We can obtain RPC interfaces to interact with the network using services provided by companies like [ZAN](https://zan.top/) and [Infura](https://infura.io/).
+
+The [zan.top documentation](https://docs.zan.top/reference/eth-accounts) offers straightforward methods to test RPCs and demonstrates how to invoke smart contracts through RPC calls.
+
+![zan](./img/zan-api-doc.png)
+
+## Coding a DApp: Implementing the Necessary Steps
+
+When developing a decentralized application (DApp) and you need to call a contract method, the process generally involves the following steps:
+
+1. Constructing transaction data
+2. Authorizing the wallet to add a signature to the transaction data
+3. Sending the signed transaction data to the blockchain network using node services
+
+> Note: For read-only contract methods, since you're not writing data to the blockchain, signing the transaction isn't necessary. You can simply read data directly from the blockchain using node services.
+
+### Executing Read-Only Contract Methods
+
+
+Once the node service is configured, we can begin interacting with the contract. To read contract data, we utilize the [useReadContract](https://wagmi.sh/react/api/hooks/useReadContract) hook provided by wagmi. Below is a sample code snippet demonstrating how to use it:
+
+```bash
+import { createConfig, http, useAccount, useReadContract } from "wagmi";
+import { mainnet } from "wagmi/chains";
+ import { WagmiWeb3ConfigProvider, MetaMask } from "@ant-design/web3-wagmi";
+ import { Address, NFTCard, Connector, ConnectButton } from "@ant-design/web3";
+ import { injected } from "wagmi/connectors";
+
+const config = createConfig({
+ chains: [mainnet],
+ transports: {
+ [mainnet.id]: http(),
+ },
+ connectors: [
+ injected({
+ target: "metaMask",
+ }),
+ ],
+});
+
+const CallTest =() => {
+ const {account} = useAccount();
+ const result = useReadContract({
+ abi:[
+ {
+ type:'function',
+ name: 'balanceOf',
+ stateMutability: 'view',
+ inputs: [{name: 'account', type: 'address'}],
+ outputs: [{type: 'uint256'}],
+ },
+ ],
+ address: '0xEcd0D12E21805803f70de03B72B1C162dB0898d9',
+ functionName: 'balanceOf',
+ args: [account?.address as `0x${string}`],
+ });
+ return (
+
{result.data?.toString()}
+ );
+}
+
+export default function Web3() {
+ return (
+
+
+
+
+
+
+
+
+ );
+};
+```
+
+To implement the `balanceOf` method for calling a contract, as shown in the code above, we have created a new `CallTest` component and included it within the `WagmiWeb3ConfigProvider`. This setup is necessary because the `useReadContract` hook must be used inside the `WagmiWeb3ConfigProvider` to function properly. As a result, we cannot directly use `useReadContract` immediately after the line `export default function Web3() {`.
+
+In practice, the `WagmiWeb3ConfigProvider` should be placed at the highest level of your component hierarchy. This placement ensures that all components within your project have access to the necessary Hooks.
+
+The `balanceOf` method is used to find out how many NFTs a specific address holds from this contract. To achieve this, we also need to use the `useAccount` Hook from `@ant-design/web3` to obtain the address of the currently connected account. We then pass this account address as an argument to the `balanceOf` method to find out how many NFTs are linked to the current account. If everything is set up correctly and the account has no NFTs, you should see a result of `0`.
+
+In the code, the `abi` field specifies the method types, which enables wagmi to manage method inputs and outputs by converting JavaScript objects into blockchain transaction data. Typically, the `abi` is automatically generated from the contract code, which we will discuss in more detail in the next chapter.
+
+### Writing Methods for Contract Interaction
+
+Simply reading from a contract is not sufficient for a fully functional DApp, which will also involve writing data to smart contracts. Writing data to smart contracts typically involves executing blockchain methods that alter contract data.
+
+Next, we will attempt to call the [mint](https://etherscan.io/address/0xEcd0D12E21805803f70de03B72B1C162dB0898d9#writeContract#F6) method used in a contract from a subsequent course. The `mint` method is not part of the ERC721 standard; it is specifically defined by this contract. Invoking the `mint` method requires using GAS and incurs at least a fee of `0.01ETH` to acquire an NFT.
+
+Here are the necessary code changes:
+
+```bash
+import {
+ Address,
+ ConnectButton,
+ Connector,
+ NFTCard,
+ useAccount,
+ } from "@ant-design/web3";
+ import { MetaMask, WagmiWeb3ConfigProvider } from "@ant-design/web3-wagmi";
+ import { Button, message } from "antd";
+ import { parseEther } from "viem";
+ import { createConfig, http, useReadContract, useWriteContract } from "wagmi";
+ import { mainnet } from "wagmi/chains";
+ import { injected } from "wagmi/connectors";
+
+ const config = createConfig({
+ chains: [mainnet],
+ transports: {
+ [mainnet.id]: http(),
+ },
+ connectors: [
+ injected({
+ target: "metaMask",
+ }),
+ ],
+ });
+
+ const CallTest = () => {
+ const { account } = useAccount();
+ const result = useReadContract({
+ abi: [
+ {
+ type: "function",
+ name: "balanceOf",
+ stateMutability: "view",
+ inputs: [{ name: "account", type: "address" }],
+ outputs: [{ type: "uint256" }],
+ },
+ ],
+ // Sepolia test contract 0x418325c3979b7f8a17678ec2463a74355bdbe72c
+ address: "0xEcd0D12E21805803f70de03B72B1C162dB0898d9",
+ functionName: "balanceOf",
+ args: [account?.address as `0x${string}`],
+ });
+ const { writeContract } = useWriteContract();
+
+ return (
+
+ {result.data?.toString()}
+
+
+ );
+ };
+
+ export default function Web3() {
+ return (
+
+
+
+
+
+
+
+
+ );
+ }
+```
+
+"In the code above, we utilized the `viem` library, which `wagmi` depends on. To use it in your project, you'll need to install the library, commonly done through a package manager like npm."
+
+```bash
+npm i viem --save
+```
+
+This code snippet is designed to trigger the contract's `mint` method with the parameter `1` when the `mint` button is clicked, signifying the minting of one NFT. Each NFT requires a fee of `0.01 ETH`, so we need to ensure this amount is included with the transaction to successfully mint the NFT. To accomplish this, we specify `value: parseEther("0.01")` in the contract call.
+
+In Ethereum, contracts cannot automatically withdraw ETH from the caller's account. Therefore, when making a call, it's crucial to include the ETH payment manually, which is an important aspect of contract security.
+
+The system will notify you of the outcome of your contract call, whether successful or unsuccessful. If no account is connected, an error message will alert you to this issue. To proceed, you must first connect your account by clicking the connect button we set up in the previous lesson.
+
+If your account lacks sufficient GAS, an error message like the one shown below will pop up:
+
+![](./img/no-gas.png)
+
+If you have enough ETH, a window requesting authorization, similar to the image below, will appear:
+
+![](./img/call-contract.png)
+
+Choosing **Reject** will cancel the contract call, ensuring no ETH is deducted from your account. In the next chapter, we'll guide you through deploying a test contract, allowing you to experience the entire process in a test setting. However, if you decide to click confirm, the contract call will proceed, your ETH will be used, and you'll receive an NFT.
diff --git a/languages/en/05_Events/Web3.tsx b/languages/en/05_Events/Web3.tsx
new file mode 100644
index 0000000..f4db3c1
--- /dev/null
+++ b/languages/en/05_Events/Web3.tsx
@@ -0,0 +1,139 @@
+import {
+ Address,
+ ConnectButton,
+ Connector,
+ NFTCard,
+ useAccount,
+} from "@ant-design/web3";
+import { MetaMask, WagmiWeb3ConfigProvider } from "@ant-design/web3-wagmi";
+import { Button, message } from "antd";
+import { parseEther } from "viem";
+import {
+ createConfig,
+ http,
+ useReadContract,
+ useWriteContract,
+ useWatchContractEvent,
+} from "wagmi";
+import { mainnet } from "wagmi/chains";
+import { injected } from "wagmi/connectors";
+
+const config = createConfig({
+ chains: [mainnet],
+ transports: {
+ [mainnet.id]: http(),
+ },
+ connectors: [
+ injected({
+ target: "metaMask",
+ }),
+ ],
+});
+
+const CallTest = () => {
+ const { account } = useAccount();
+ const result = useReadContract({
+ abi: [
+ {
+ type: "function",
+ name: "balanceOf",
+ stateMutability: "view",
+ inputs: [{ name: "account", type: "address" }],
+ outputs: [{ type: "uint256" }],
+ },
+ ],
+ // Sepolia test contract 0x418325c3979b7f8a17678ec2463a74355bdbe72c
+ address: "0xEcd0D12E21805803f70de03B72B1C162dB0898d9",
+ functionName: "balanceOf",
+ args: [account?.address as `0x${string}`],
+ });
+ const { writeContract } = useWriteContract();
+
+ useWatchContractEvent({
+ address: "0xEcd0D12E21805803f70de03B72B1C162dB0898d9",
+ abi: [
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: false,
+ internalType: "address",
+ name: "minter",
+ type: "address",
+ },
+ {
+ indexed: false,
+ internalType: "uint256",
+ name: "amount",
+ type: "uint256",
+ },
+ ],
+ name: "Minted",
+ type: "event",
+ },
+ ],
+ eventName: "Minted",
+ onLogs() {
+ message.success("new minted!");
+ },
+ });
+
+ return (
+
+ {result.data?.toString()}
+
+
+ );
+};
+
+export default function Web3() {
+ return (
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/languages/en/05_Events/readme.md b/languages/en/05_Events/readme.md
new file mode 100644
index 0000000..8ecdf37
--- /dev/null
+++ b/languages/en/05_Events/readme.md
@@ -0,0 +1,92 @@
+This section will explain how to monitor contract events within a DApp and update its interface in real-time.
+
+## Introduction
+
+In blockchain smart contracts, the concept of events is somewhat distinct from how events are understood in traditional application development. Blockchain lacks a built-in messaging system to send events to applications. Instead, events are essentially abstractions of logs generated on the Ethereum Virtual Machine (EVM).
+
+Compared to direct state changes in smart contracts, these log-based events are more cost-effective, serving as an economical method for data storage. An event typically uses about 2000 gas, while storing a new variable on the blockchain requires at least 20,000 gas. Consequently, events are often used in smart contracts to document significant state changes. Moreover, by leveraging the RPC interfaces provided by node services, the frontend of a DApp can listen to these contract events and update the interface in near real-time.
+
+## How to Add Events in Smart Contracts
+
+In smart contracts, events are defined with the `event` keyword and activated using the `emit` command. Here's a straightforward example to illustrate this concept. We will offer a more detailed explanation in our upcoming contract development courses.
+
+```diff
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.20;
+
+import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
+import "@openzeppelin/contracts/access/Ownable.sol";
+
+contract MyToken is ERC721, Ownable {
+ uint256 private _nextTokenId = 0;
++ event Minted(address minter, uint256 amount);
+ constructor()
+ ERC721("MyToken", "MTK")
+ Ownable(msg.sender)
+ {}
+ function mint(uint256 quantity) public payable {
+ require(quantity == 1, "quantity must be 1");
+ require(msg.value == 0.01 ether, "must pay 0.01 ether");
+ uint256 tokenId = _nextTokenId++;
+ _mint(msg.sender, tokenId);
++ emit Minted(msg.sender, quantity);
+ }
+}
+```
+
+## How to Listen to Events in a DApp
+
+To listen to contract events in a DApp's front-end, we can utilize the RPC interface provided by the node service. For this example, we'll continue using `wagmi` for our development needs.
+
+We'll start by implementing the [useWatchContractEvent](https://wagmi.sh/react/api/hooks/useWatchContractEvent#abi) hook.
+
+```diff
+import {
+ createConfig,
+ http,
+ useReadContract,
+ useWriteContract,
++ useWatchContractEvent,
+} from "wagmi";
+```
+
+To monitor contract events, use `useWatchContractEvent`.
+
+```ts
+useWatchContractEvent({
+ address: "0xEcd0D12E21805803f70de03B72B1C162dB0898d9",
+ abi: [
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: false,
+ internalType: "address",
+ name: "minter",
+ type: "address",
+ },
+ {
+ indexed: false,
+ internalType: "uint256",
+ name: "amount",
+ type: "uint256",
+ },
+ ],
+ name: "Minted",
+ type: "event",
+ },
+ ],
+ eventName: "Minted",
+ onLogs() {
+ message.success("new minted!");
+ },
+});
+```
+
+When using an SDK like wagmi, the approach is quite similar to traditional front-end development. You start by entering the contract address and the event you wish to monitor. Then, you can handle these events through the `onLogs` callback function.
+
+It's important to note that the code provided here is for demonstration purposes only and may not encompass all practical applications. In a real-world scenario, you would need to determine the specific details of the event within `onLogs` and update your page accordingly.
+
+Typically, the `abi` parameter is generated automatically when compiling the contract. Instead of manually writing it each time, it's best to maintain a complete version within your project and use it as needed.
+
+Since events only occur after a contract is invoked, we can't debug this right now. You should continue with the course material and return to test this once you're able to deploy a test contract in a testing environment.
diff --git a/languages/en/06_Vite + Vercel/img/createnew.png b/languages/en/06_Vite + Vercel/img/createnew.png
new file mode 100644
index 0000000..eeebd8b
Binary files /dev/null and b/languages/en/06_Vite + Vercel/img/createnew.png differ
diff --git a/languages/en/06_Vite + Vercel/img/deploy.png b/languages/en/06_Vite + Vercel/img/deploy.png
new file mode 100644
index 0000000..57db47e
Binary files /dev/null and b/languages/en/06_Vite + Vercel/img/deploy.png differ
diff --git a/languages/en/06_Vite + Vercel/img/domain.png b/languages/en/06_Vite + Vercel/img/domain.png
new file mode 100644
index 0000000..3edaa49
Binary files /dev/null and b/languages/en/06_Vite + Vercel/img/domain.png differ
diff --git a/languages/en/06_Vite + Vercel/img/editroot.png b/languages/en/06_Vite + Vercel/img/editroot.png
new file mode 100644
index 0000000..36f6420
Binary files /dev/null and b/languages/en/06_Vite + Vercel/img/editroot.png differ
diff --git a/languages/en/06_Vite + Vercel/img/import.png b/languages/en/06_Vite + Vercel/img/import.png
new file mode 100644
index 0000000..038b901
Binary files /dev/null and b/languages/en/06_Vite + Vercel/img/import.png differ
diff --git a/languages/en/06_Vite + Vercel/img/vitebuild.png b/languages/en/06_Vite + Vercel/img/vitebuild.png
new file mode 100644
index 0000000..3f0574d
Binary files /dev/null and b/languages/en/06_Vite + Vercel/img/vitebuild.png differ
diff --git a/languages/en/06_Vite + Vercel/readme.md b/languages/en/06_Vite + Vercel/readme.md
new file mode 100644
index 0000000..93a9303
--- /dev/null
+++ b/languages/en/06_Vite + Vercel/readme.md
@@ -0,0 +1,62 @@
+This section offers a brief introduction on Vite and the basics of front-end deployment. If you're already experienced in front-end development, feel free to skip ahead.
+
+[Vite](https://vite.dev/) is a lightweight framework that facilitates the rapid development of React applications. Once we build our application using React and Vite, the next step is to deploy it on a server, making it accessible to users.
+
+Running `npm run build` in your project (which triggers `vite build` as defined in `package.json`) compiles your application into JavaScript, CSS, and other necessary files. These are designed to run in a browser, which natively supports JavaScript but not React. Therefore, we must compile the React code into browser-executable files. Furthermore, these JavaScript files can also be executed in a Node.js environment on the server, enabling server-side logic. While our previous courses haven't covered this, future courses will explore server-side functionalities, including tasks like signature verification.
+
+If the execution is successful, you can see the following effect:
+
+![build next](./img/vitebuild.png)
+
+After completing the build, you can deploy your application locally by running `npm run dev`. If you're deploying to your own server, the steps are similar: install the necessary dependencies, build your application, and then start it using either `npm run dev`.
+
+If you're using a different front-end framework like [Next.js](https://nextjs.org/), the process remains largely the same: you'll typically need to build the project and then start the service.
+
+When your build results in static resources meant for a browser, you can use a tool like Nginx or Express to set up a web server to host these files. Alternatively, [GitHub Pages](https://pages.github.com/) is an excellent option for hosting static files, as it is specifically designed for serving static content.
+
+## Deploying with Vercel
+
+This section will guide you through deploying your application using [Vercel](https://vercel.com/). Vercel supports Vite, offering a straightforward and efficient deployment process. To get started, simply upload your code to a hosting service like GitHub or GitLab, and then select your repository on Vercel. The platform will automatically handle the building and deployment of your application.
+
+Begin by pushing your code to your GitHub account. Alternatively, you can fork the following repository: [https://github.com/WTFAcademy/WTF-Dapp](https://github.com/WTFAcademy/WTF-Dapp). The complete code is available in the [demo](../demo/) folder.
+
+After logging in to Vercel, create a new project in its console:
+
+![createnew](./img/createnew.png)
+
+Next, select and import your Github project. During this process, you may need to authorize Vercel to access your Github repository:
+
+![import](./img/import.png)
+
+Select the root directory of your Next.js project and then click Deploy.
+
+![editroot](./img/editroot.png)
+
+From this point forward, everything will be handled automatically:
+
+![deploy](./img/deploy.png)
+
+You can view the final demo of this tutorial at [https://wtf-dapp.vercel.app/web3](https://wtf-dapp.vercel.app/web3).
+
+Note: When you deploy using Vite, remember to add the `redirect` logic which routes the `/web3` page.
+
+In the root of your folder, create a vercel.json file and add the code below:
+
+```
+{
+ "rewrites": [
+ {
+ "source": "/(.*)",
+ "destination": "/index.html"
+ }
+ ]
+}
+```
+
+## Custom Domain Name
+
+Vercel provides a default domain name for your projects, but it's common to use a custom domain name for a more professional appearance. To do this, locate your project in the Vercel console and navigate to the Domains section to add your custom domain.
+
+![domain](./img/domain.png)
+
+Once you've added your domain, follow the provided instructions to set up a CNAME record with your domain provider. This will ensure that your domain directs traffic to Vercel's servers.
diff --git a/languages/en/07_ContractDev/MyToken.sol b/languages/en/07_ContractDev/MyToken.sol
new file mode 100644
index 0000000..3b11acc
--- /dev/null
+++ b/languages/en/07_ContractDev/MyToken.sol
@@ -0,0 +1,21 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.20;
+
+import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
+import "@openzeppelin/contracts/access/Ownable.sol";
+
+contract MyToken is ERC721, Ownable {
+ uint256 private _nextTokenId = 0;
+
+ constructor()
+ ERC721("MyToken", "MTK")
+ Ownable(msg.sender)
+ {}
+
+ function mint(uint256 quantity) public payable {
+ require(quantity == 1, "quantity must be 1");
+ require(msg.value == 0.01 ether, "must pay 0.01 ether");
+ uint256 tokenId = _nextTokenId++;
+ _mint(msg.sender, tokenId);
+ }
+}
diff --git a/languages/en/07_ContractDev/img/chai.png b/languages/en/07_ContractDev/img/chai.png
new file mode 100644
index 0000000..d1090b7
Binary files /dev/null and b/languages/en/07_ContractDev/img/chai.png differ
diff --git a/languages/en/07_ContractDev/img/create.png b/languages/en/07_ContractDev/img/create.png
new file mode 100644
index 0000000..abccedc
Binary files /dev/null and b/languages/en/07_ContractDev/img/create.png differ
diff --git a/languages/en/07_ContractDev/img/generate.png b/languages/en/07_ContractDev/img/generate.png
new file mode 100644
index 0000000..2b7b872
Binary files /dev/null and b/languages/en/07_ContractDev/img/generate.png differ
diff --git a/languages/en/07_ContractDev/img/initCode.png b/languages/en/07_ContractDev/img/initCode.png
new file mode 100644
index 0000000..ff90d75
Binary files /dev/null and b/languages/en/07_ContractDev/img/initCode.png differ
diff --git a/languages/en/07_ContractDev/img/mintable.png b/languages/en/07_ContractDev/img/mintable.png
new file mode 100644
index 0000000..edb962c
Binary files /dev/null and b/languages/en/07_ContractDev/img/mintable.png differ
diff --git a/languages/en/07_ContractDev/img/more.png b/languages/en/07_ContractDev/img/more.png
new file mode 100644
index 0000000..fd84f16
Binary files /dev/null and b/languages/en/07_ContractDev/img/more.png differ
diff --git a/languages/en/07_ContractDev/img/remix.png b/languages/en/07_ContractDev/img/remix.png
new file mode 100644
index 0000000..7b8bdfc
Binary files /dev/null and b/languages/en/07_ContractDev/img/remix.png differ
diff --git a/languages/en/07_ContractDev/img/run.png b/languages/en/07_ContractDev/img/run.png
new file mode 100644
index 0000000..66bc5b4
Binary files /dev/null and b/languages/en/07_ContractDev/img/run.png differ
diff --git a/languages/en/07_ContractDev/img/slide.png b/languages/en/07_ContractDev/img/slide.png
new file mode 100644
index 0000000..d8b831f
Binary files /dev/null and b/languages/en/07_ContractDev/img/slide.png differ
diff --git a/languages/en/07_ContractDev/img/unitTest.png b/languages/en/07_ContractDev/img/unitTest.png
new file mode 100644
index 0000000..1802c49
Binary files /dev/null and b/languages/en/07_ContractDev/img/unitTest.png differ
diff --git a/languages/en/07_ContractDev/img/unitTest1.png b/languages/en/07_ContractDev/img/unitTest1.png
new file mode 100644
index 0000000..239175e
Binary files /dev/null and b/languages/en/07_ContractDev/img/unitTest1.png differ
diff --git a/languages/en/07_ContractDev/readme.md b/languages/en/07_ContractDev/readme.md
new file mode 100644
index 0000000..ea08458
--- /dev/null
+++ b/languages/en/07_ContractDev/readme.md
@@ -0,0 +1,217 @@
+# This session will provide a brief overview of developing and testing smart contracts.
+
+In this tutorial, we will be using `Remix` to execute `Solidity` smart contracts.
+
+`Remix` is the officially recommended integrated development environment (IDE) for Ethereum smart contracts, particularly suited for beginners. It provides an intuitive interface that lets you write, compile, and deploy smart contracts directly in your web browser, eliminating the need for local installations. In more advanced courses, we'll also cover [local development and testing environments for contracts](../14_LocalDev/readme.md).
+
+`Solidity` is a high-level programming language specifically designed for creating `smart contracts`. Drawing influences from `C++`, `Python`, and `JavaScript`, it is tailored to run on the Ethereum Virtual Machine (`EVM`). Solidity is a statically typed language that supports features like inheritance, libraries, and complex user-defined types.
+
+## Getting Started with Contracts
+
+### Interface Overview
+
+When you first access [Remix](https://remix.ethereum.org), you'll be greeted with an interface similar to the one shown below:
+
+![](./img/remix.png)
+
+The `Remix` interface includes three main panels and a terminal, as depicted in the image.
+
+1. Icon Panel - Allows you to modify which plugins are displayed in the side panel by clicking.
+2. Side Panel - This area contains the interface for most plugins, though some are excluded.
+3. Main Panel - Used for editing files and tabs, and it displays certain comprehensive tool pages.
+4. Terminal - Displays transaction receipts and various logs for your activities.
+
+### Sidebar Icons Overview
+
+![](./img/slide.png)
+
+Below is a quick overview of the sidebar icons, which will be explained more thoroughly as we proceed:
+
+- **Home:** Always brings you back to the main page, even if it's been closed.
+- **File Explorer:** Used for managing workspaces and files.
+- **Search:** A tool for conducting a global search.
+- **Solidity Compiler:** The interface for compiling contracts. It initially shows basic compiler settings and has an `Advanced Configurations` button for more options.
+- **Deploy & Run:** Facilitates sending transactions to the current environment. (Note: The "environment" refers to the blockchain network or simulation environment you are interacting with.)
+- **Debugger:** Displays the state of contracts during transaction debugging.
+- **Plugin Manager:** A hub where you can install various plugins.
+- **Settings:** Offers basic settings like `language`, `theme`, `GitHub access token`, and general application settings.
+
+### Managing Workspaces and Files
+
+In **Remix**, **WORKSPACES** are specialized folders that separate projects. Files from one workspace cannot be accessed by another.
+
+As shown in the image below, clicking on icon 1 allows you to switch between different workspaces. Icon 2 provides additional workspace management options such as `Create`, `Clone`, `Rename`, `Download`, and `Delete`.
+
+![](./img/createBtn.png) ![](./img/more.png)
+
+### Creating a Workspace
+
+For this tutorial, we'll demonstrate how to create a new workspace using the `Create` button. When you click on `Create`, a `Create Workspace` window appears, offering the following templates:
+
+- Basic
+- Blank
+- OpenZeppelin ERC20
+- OpenZeppelin ERC721
+- OpenZeppelin ERC1155
+- 0xProject ERC20
+- Gnosis MultiSig
+
+When you select the `ERC721` template from the `OpenZeppelin` library, you can add optional features like `Mintable`.
+
+> **ERC721** is a standard for non-fungible tokens on Ethereum, proposed in January 2018 by William Entriken, Dieter Shirley, Jacob Evans, and Nastassia Sachs.
+> **OpenZeppelin** is a library designed for secure smart contract development, offering standard implementations for many common contract types.
+
+![](./img/create.png) ![](./img/mintable.png)
+
+Selecting `Mintable` adds the `Mint` method to the template contract. After making your selection, click `OK`. Your new workspace will then be created, as shown below:
+
+![](./img/initCode.png)
+
+The `.deps` directory houses the `@openzeppelin` npm package we've installed. This package provides the contract templates we reference in our contracts, as well as toolkits that these templates utilize.
+The `contracts` directory is where we save our custom-written contract files.
+The `scripts` folder contains scripts that are automatically generated for contract deployment. By executing the JavaScript files here, you can deploy contracts.
+The `tests` directory includes some automatically generated test files for validation.
+
+The `ERC721` contract template from `@openzeppelin` is found in `contracts/MyToken.sol`. Let's go over the contents of this contract briefly.
+
+1. The first line is a comment indicating the software license used for this code, which is the `MIT license`. If the license is not specified, you'll see a warning during compilation, although the program will still run. In Solidity, comments are written with `//` followed by the comment text, which is not executed by the program.
+2. The second line specifies the version of `Solidity` that this source file is compatible with. It indicates that the file will not compile with versions below `0.8.20` or versions `0.9.0` and above, with the caret (`^`) indicating the upper limit. Solidity statements conclude with a semicolon (`;`).
+3. Lines 4-5 import external Solidity files, incorporating them as part of the same Solidity contract as the original file.
+4. Line 7 declares a contract named `MyToken`, using `is` to denote inheritance from the imported `ERC721` and `Ownable` contracts.
+5. Lines 8-10 detail the constructor where parameters required by the inherited contracts are provided: the `ERC721` contract receives the token's `name` and `symbol`, while the `Ownable` contract receives the owner's address.
+6. Lines 13-15 define a public method named `safeMint`, which requires an address-type parameter `to` and a uint256-type parameter `tokenId`. This method calls a private method `_safeMint()` from ERC721.sol, passing the `to` and `tokenId` parameters.
+
+Next, we will add some custom features to our contract template.
+
+## Contract Development
+
+Let's delve into writing additional contract functionality and testing compilation.
+
+In the upcoming code, we'll implement a new mint method to replace the default `safeMint`. This new mint method will align with the methods used in previous chapters, allowing us to substitute this new contract for course contracts once deployed.
+
+Key changes include:
+
+1. Setting the initialOwner to be the contract issuer, simplifying deployment by eliminating the need to specify an initialOwner.
+2. Defining a private uint256 variable named `_nextTokenId`, which tracks the current progress and increments with each new NFT minted.
+3. Requiring a uint256 parameter named `quantity` in the mint method, which specifies the number of NFTs to mint; for simplicity, the logic will restrict minting to one at a time.
+4. Removing the `onlyOwner` modifier so anyone can call the mint method.
+5. Adding the `payable` modifier so callers can transfer funds to the contract when invoking the mint method.
+6. Changing `_safeMint` to `_mint`, primarily to avoid errors when testing via Remix contract calls later, and altering 'to' to `msg.sender`, indicating that NFTs are minted to the address of the transaction initiator.
+
+The code is as follows:
+
+```diff
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.20;
+
+import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
+import "@openzeppelin/contracts/access/Ownable.sol";
+
+contract MyToken is ERC721, Ownable {
++ uint256 private _nextTokenId = 0;
+
+- constructor(address initialOwner)
++ constructor()
+ ERC721("MyToken", "MTK")
+- Ownable(initialOwner)
++ Ownable(msg.sender)
+ {}
+
+- function safeMint(address to, uint256 tokenId) public onlyOwner {
++ function mint(uint256 quantity) public payable {
++ require(quantity == 1, "quantity must be 1");
++ require(msg.value == 0.01 ether, "must pay 0.01 ether");
++ uint256 tokenId = _nextTokenId++;
+- _safeMint(to, tokenId);
++ _mint(msg.sender, tokenId);
+ }
+}
+```
+> In Solidity, `private` methods and variables are accessible only within the contract itself. In contrast, `public` methods and variables can be accessed by anyone.
+
+## Testing Contracts
+
+1. Unit Testing Plugin
+
+To get started with unit testing in Remix, click the `Plugin manager` icon located at the bottom left of the interface. In the search bar, type `unit` to find the `SOLIDITY UNIT TESTING` plugin. Click `Activate` to install and enable it, as shown below:
+
+![](./img/unitTest.png)
+
+Once the installation is complete, you'll see a `Solidity unit testing` icon in the left sidebar. Click it to open the plugin in the side panel. The plugin interface should appear as follows:
+
+![](./img/unitTest1.png)
+
+2. Unit Test File
+
+Remix includes a built-in `assert` library for testing, which you can read more about in the [documentation](https://remix-ide.readthedocs.io/en/latest/assert_library.html). Additionally, Remix supports specific functions in test files to organize your tests. These functions include:
+
+- `beforeEach()`: Executes before each individual test.
+- `beforeAll()`: Executes once before all tests.
+- `afterEach()`: Executes after each individual test.
+- `afterAll()`: Executes once after all tests are completed.
+
+You'll find the unit test file for our contract in the `tests/MyToken_test.sol` directory. Our template contract automatically created this test contract. If you start with a new, empty folder, click the `Generate` button to create a test file, as shown below:
+
+![](./img/generate.png)
+
+Next, open the test file `tests/MyToken_test.sol` in the `File explorer` and add the following test code:
+
+1. `remix_tests.sol` is automatically included by Remix.
+2. `remix_accounts.sol` generates test account addresses.
+3. `../contracts/MyToken.sol` imports the contract file you previously wrote.
+4. In `beforeAll()`, create an instance of the `MyToken` contract, named `s`, and save one of the test addresses from `TestsAccounts.getAccount(0)` as `acc0`.
+5. The function `testTokenNameAndSymbol()` checks that after the contract is instantiated, the `name()` method returns `MyToken`, and the `symbol()` method returns `MTK`.
+6. Define a function called `testMint()`, invoke the `mint(1)` method, and verify that after minting, the `balanceOf()` method returns 1.
+
+Here is the code for the file `tests/MyToken_test.sol`:
+
+```solidity
+// SPDX-License-Identifier: GPL-3.0
+
+pragma solidity >=0.8.0 <0.9.0;
+import "remix_tests.sol";
+import "remix_accounts.sol";
+import "../contracts/MyToken.sol";
+
+contract MyTokenTest {
+ MyToken s;
+ function beforeAll () public {
+ s = new MyToken();
+ }
+
+ function testTokenNameAndSymbol () public {
+ Assert.equal(s.name(), "MyToken", "token name did not match");
+ Assert.equal(s.symbol(), "MTK", "token symbol did not match");
+ }
+ /// #value: 10000000000000000
+ function testMint() public payable {
+ s.mint{value: msg.value}(1);
+ Assert.equal(s.balanceOf(address(this)), 1, "balance did not match");
+ }
+}
+```
+
+In Remix, unit testing is conducted by invoking the contract you wish to test from within another contract dedicated to testing. For further information, refer to the [Remix Unit Testing Plugin Documentation](https://remix-ide.readthedocs.io/en/latest/unittesting.html).
+
+### Running Unit Tests
+
+Once your test scripts are ready, select the relevant file and click `Run` to initiate the tests. The tests will execute in an isolated environment. After the execution concludes, a test summary will appear, as illustrated below:
+
+![](./img/run.png)
+
+This completes the unit testing process for your contract.
+
+If you prefer using Chai and Mocha for testing, Remix accommodates these tools too.
+
+> Chai is a BDD/TDD assertion library for Node.js and browsers, compatible with any JavaScript testing framework. Mocha is a comprehensive JavaScript test framework that simplifies asynchronous testing on Node.js and in the browser.
+
+To use these tools, create a `js` file in your workspace, ideally in the `scripts` folder. Right-click to create the file, write your test code within it, and then click `Run`. It should resemble this setup:
+
+![](./img/chai.png)
+
+Click `Run` to execute the tests, and the results will display in the terminal.
+
+This example demonstrates a practical testing strategy. If you're comfortable with this method, Remix fully supports it.
+
+Next, we'll compile and deploy our contract files on the blockchain.
+
diff --git a/languages/en/08_ContractDeploy/Web3.tsx b/languages/en/08_ContractDeploy/Web3.tsx
new file mode 100644
index 0000000..fd79a0b
--- /dev/null
+++ b/languages/en/08_ContractDeploy/Web3.tsx
@@ -0,0 +1,113 @@
+import {
+ Address,
+ ConnectButton,
+ Connector,
+ NFTCard,
+ useAccount,
+} from "@ant-design/web3";
+import {
+ Sepolia,
+ MetaMask,
+ WagmiWeb3ConfigProvider,
+} from "@ant-design/web3-wagmi";
+import { Button, message } from "antd";
+import { parseEther } from "viem";
+import { createConfig, http, useReadContract, useWriteContract } from "wagmi";
+import { sepolia, mainnet } from "wagmi/chains";
+import { injected } from "wagmi/connectors";
+
+const config = createConfig({
+ chains: [mainnet, sepolia],
+ transports: {
+ [mainnet.id]: http(),
+ [sepolia.id]: http(),
+ },
+ connectors: [
+ injected({
+ target: "metaMask",
+ }),
+ ],
+});
+
+const CallTest = () => {
+ const { account } = useAccount();
+ const result = useReadContract({
+ abi: [
+ {
+ type: "function",
+ name: "balanceOf",
+ stateMutability: "view",
+ inputs: [{ name: "account", type: "address" }],
+ outputs: [{ type: "uint256" }],
+ },
+ ],
+ // Sepolia test contract 0x418325c3979b7f8a17678ec2463a74355bdbe72c
+ address: "0xEcd0D12E21805803f70de03B72B1C162dB0898d9",
+ functionName: "balanceOf",
+ args: [account?.address as `0x${string}`],
+ });
+ const { writeContract } = useWriteContract();
+
+ return (
+
+ {result.data?.toString()}
+
+
+ );
+};
+
+export default function Web3() {
+ return (
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/languages/en/08_ContractDeploy/img/call-in-ide.png b/languages/en/08_ContractDeploy/img/call-in-ide.png
new file mode 100644
index 0000000..e630286
Binary files /dev/null and b/languages/en/08_ContractDeploy/img/call-in-ide.png differ
diff --git a/languages/en/08_ContractDeploy/img/changeNode.png b/languages/en/08_ContractDeploy/img/changeNode.png
new file mode 100644
index 0000000..a6fa66e
Binary files /dev/null and b/languages/en/08_ContractDeploy/img/changeNode.png differ
diff --git a/languages/en/08_ContractDeploy/img/compile.png b/languages/en/08_ContractDeploy/img/compile.png
new file mode 100644
index 0000000..1355bef
Binary files /dev/null and b/languages/en/08_ContractDeploy/img/compile.png differ
diff --git a/languages/en/08_ContractDeploy/img/connect1.png b/languages/en/08_ContractDeploy/img/connect1.png
new file mode 100644
index 0000000..a293421
Binary files /dev/null and b/languages/en/08_ContractDeploy/img/connect1.png differ
diff --git a/languages/en/08_ContractDeploy/img/connect2.png b/languages/en/08_ContractDeploy/img/connect2.png
new file mode 100644
index 0000000..1eec2f6
Binary files /dev/null and b/languages/en/08_ContractDeploy/img/connect2.png differ
diff --git a/languages/en/08_ContractDeploy/img/copyABI.png b/languages/en/08_ContractDeploy/img/copyABI.png
new file mode 100644
index 0000000..4249e80
Binary files /dev/null and b/languages/en/08_ContractDeploy/img/copyABI.png differ
diff --git a/languages/en/08_ContractDeploy/img/json.png b/languages/en/08_ContractDeploy/img/json.png
new file mode 100644
index 0000000..97c0785
Binary files /dev/null and b/languages/en/08_ContractDeploy/img/json.png differ
diff --git a/languages/en/08_ContractDeploy/img/mint-test-net.png b/languages/en/08_ContractDeploy/img/mint-test-net.png
new file mode 100644
index 0000000..b506cc5
Binary files /dev/null and b/languages/en/08_ContractDeploy/img/mint-test-net.png differ
diff --git a/languages/en/08_ContractDeploy/img/sendTrans.png b/languages/en/08_ContractDeploy/img/sendTrans.png
new file mode 100644
index 0000000..9220ca5
Binary files /dev/null and b/languages/en/08_ContractDeploy/img/sendTrans.png differ
diff --git a/languages/en/08_ContractDeploy/img/transInfo.png b/languages/en/08_ContractDeploy/img/transInfo.png
new file mode 100644
index 0000000..1595f53
Binary files /dev/null and b/languages/en/08_ContractDeploy/img/transInfo.png differ
diff --git a/languages/en/08_ContractDeploy/readme.md b/languages/en/08_ContractDeploy/readme.md
new file mode 100644
index 0000000..ab7b2cd
--- /dev/null
+++ b/languages/en/08_ContractDeploy/readme.md
@@ -0,0 +1,209 @@
+## Compilation
+
+To begin compiling, click on the `Solidity Compiler` icon in the icon panel. You'll see the basic configuration options for the compiler. For more options, click the `Advanced Configurations` button. In the advanced menu, you can modify the EVM version, enable optimization, and set an estimate for the number of times the bytecode will be executed during the contract's lifecycle (the default is 200). For detailed information on contract optimization, refer to the [Solidity documentation on Optimizer](https://docs.soliditylang.org/en/latest/using-the-compiler.html#optimizer-options).
+
+To compile a file, open it in the `File explorer`. If multiple files are open, make sure the one you wish to compile is selected in the editor.
+
+You can compile a file in three ways:
+
+- Press `Control/Command + S`
+- Right-click the file in the `File explorer` and select the compile option
+- Click the `Compile` button
+
+![](./img/compile.png)
+
+Once compilation finishes, a green checkmark will appear beside the `Solidity Compiler` icon, as shown above. `Remix` will then generate three JSON files for each compiled contract, which you can view in the `File explorer` plugin:
+
+1. `artifacts/.json`: Contains links for `libraries`, `bytecode`, deployed `bytecode`, `gas estimation`, `identifiers`, and `ABI`. It helps in associating library addresses with files.
+2. `artifacts/.json`: Contains metadata from Solidity's compilation output.
+3. `artifacts/build-info/.json`: Includes information about the `solc` compiler version, compiler input, and output.
+
+As illustrated below:
+
+![](./img/json.png)
+
+> The `ABI` is a JSON array that describes a contract's interface.
+
+You can copy and export the ABI by clicking on it:
+
+![](./img/copyABI.png)
+
+## Deployment
+
+To send transactions to the current `ENVIRONMENT`, click `Deploy & Run`.
+
+Next, we will deploy a contract to the Sepolia testnet using MetaMask. First, switch MetaMask to the Sepolia testnet (or any other testnet you are comfortable with).
+
+From the `ENVIRONMENT` dropdown menu, select `Injected Provider - MetaMask`.
+
+A MetaMask popup will then appear, prompting you to connect it with Remix. Once connected, the Remix side panel will display the connected network and account. Remember that deploying on a testnet requires testnet tokens, which can be obtained from online testnet faucets.
+
+
+
+
+
+After connecting your wallet, you can proceed to deploy the contract. For a simple ERC721 smart contract, the default Gas Limit of 3 million in Remix is adequate; there is no need to specify any additional value for deployment. Follow these steps to deploy your contract:
+
+- Ensure that `ENVIRONMENT` is set to Injected Provider - MetaMask
+- Verify the connected account is correct for deployment
+- Use the default GAS LIMIT: 3000000
+- Set VALUE to 0
+- Ensure `MyToken.sol` is the selected contract
+- Click Deploy
+- Click Transact to initiate the deployment
+- Confirm the deployment in the MetaMask popup
+
+![](./img/sendTrans.png)
+
+Once the transaction is deployed, details will appear in the Remix terminal. By default, deployed contracts appear collapsed under the Deployed Contracts section in the side panel; click the small arrow to expand them.
+
+![](./img/transInfo.png)
+
+Try expanding and calling the mint function as shown below:
+
+![](./img/call-in-ide.png)
+
+This action will also trigger a MetaMask transaction confirmation popup. Click confirm to initiate the transaction, similar to how transactions are initiated in a DApp.
+
+Congratulations, you have successfully deployed the contract on the testnet.
+
+## Integrating with DApp
+
+In the previously developed DApp, there's already a page that allows you to interact with contracts. Simply enter the contract address into the DApp to call the contract.
+
+Apart from setting up the address, we also need to switch to the test network. The code for this is shown below:
+
+```diff
+import { createConfig, http, useReadContract, useWriteContract } from "wagmi";
+- import { mainnet } from "wagmi/chains";
++ import { mainnet, sepolia } from "wagmi/chains";
+import {
+ WagmiWeb3ConfigProvider,
+ MetaMask,
++ Sepolia,
+} from "@ant-design/web3-wagmi";
+import {
+ Address,
+ NFTCard,
+ Connector,
+ ConnectButton,
+ useAccount,
+} from "@ant-design/web3";
+import { injected } from "wagmi/connectors";
+import { Button, message } from "antd";
+import { parseEther } from "viem";
+
+const config = createConfig({
+- chains: [mainnet],
++ chains: [mainnet, sepolia],
+ transports: {
+ [mainnet.id]: http(),
++ [sepolia.id]: http(),
+ },
+ connectors: [
+ injected({
+ target: "metaMask",
+ }),
+ ],
+});
+
+const CallTest = () => {
+ const { account } = useAccount();
+ const result = useReadContract({
+ abi: [
+ {
+ type: "function",
+ name: "balanceOf",
+ stateMutability: "view",
+ inputs: [{ name: "account", type: "address" }],
+ outputs: [{ type: "uint256" }],
+ },
+ ],
+- address: "0xEcd0D12E21805803f70de03B72B1C162dB0898d9",
++ address: "0x418325c3979b7f8a17678ec2463a74355bdbe72c", // use your own contract address
+ functionName: "balanceOf",
+ args: [account?.address as `0x${string}`],
+ });
+ const { writeContract } = useWriteContract();
+
+ return (
+
+ {result.data?.toString()}
+
+
+ );
+};
+
+export default function Web3() {
+ return (
+
+
+
+
+
+
+
+
+ );
+};
+
+```
+Switch to the Sepolia test network on the DApp page. Click the `mint` button, and if the transaction is successful, a MetaMask confirmation window will appear:
+
+![](./img/mint-test-net.png)
+
+After the transaction is confirmed, refresh the page. You should see that the `balanceOf` value has changed to `1`, indicating a successful NFT mint. Ideally, a well-designed DApp would incorporate smart contract events, listen for these events on the frontend, and automatically update the display. However, event handling is not covered in this introductory course.
+
+## Complete Example
+
+Here is the full example for this course:
+
+
+
+You can also check the source code on GitHub: [https://github.com/ant-design/ant-design-web3-demo](https://github.com/ant-design/ant-design-web3-demo).
+
+This concludes our guide on deploying and interacting with the contract. We hope you found this tutorial helpful. Thank you! 🎉
diff --git a/languages/en/09_EIP1193/Web3.tsx b/languages/en/09_EIP1193/Web3.tsx
new file mode 100644
index 0000000..a29067f
--- /dev/null
+++ b/languages/en/09_EIP1193/Web3.tsx
@@ -0,0 +1,145 @@
+import {
+ Address,
+ ConnectButton,
+ Connector,
+ NFTCard,
+ useAccount,
+} from "@ant-design/web3";
+import { MetaMask, WagmiWeb3ConfigProvider } from "@ant-design/web3-wagmi";
+import { Button, message } from "antd";
+import { parseEther } from "viem";
+import {
+ createConfig,
+ http,
+ useReadContract,
+ useWriteContract,
+ useWatchContractEvent,
+} from "wagmi";
+import { mainnet } from "wagmi/chains";
+import { injected } from "wagmi/connectors";
+
+const config = createConfig({
+ chains: [mainnet],
+ transports: {
+ [mainnet.id]: http(),
+ },
+ connectors: [
+ injected({
+ target: "metaMask",
+ }),
+ ],
+});
+
+const CallTest = () => {
+ const { account } = useAccount();
+ const result = useReadContract({
+ abi: [
+ {
+ type: "function",
+ name: "balanceOf",
+ stateMutability: "view",
+ inputs: [{ name: "account", type: "address" }],
+ outputs: [{ type: "uint256" }],
+ },
+ ],
+ // Sepolia test contract 0x418325c3979b7f8a17678ec2463a74355bdbe72c
+ address: "0xEcd0D12E21805803f70de03B72B1C162dB0898d9",
+ functionName: "balanceOf",
+ args: [account?.address as `0x${string}`],
+ });
+ const { writeContract } = useWriteContract();
+
+ useWatchContractEvent({
+ address: "0xEcd0D12E21805803f70de03B72B1C162dB0898d9",
+ abi: [
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: false,
+ internalType: "address",
+ name: "minter",
+ type: "address",
+ },
+ {
+ indexed: false,
+ internalType: "uint256",
+ name: "amount",
+ type: "uint256",
+ },
+ ],
+ name: "Minted",
+ type: "event",
+ },
+ ],
+ eventName: "Minted",
+ onLogs() {
+ message.success("new minted!");
+ },
+ });
+
+ return (
+
+ {result.data?.toString()}
+
+
+ );
+};
+
+export default function Web3() {
+ return (
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/languages/en/09_EIP1193/img/demo.png b/languages/en/09_EIP1193/img/demo.png
new file mode 100644
index 0000000..e12a50c
Binary files /dev/null and b/languages/en/09_EIP1193/img/demo.png differ
diff --git a/languages/en/09_EIP1193/readme.md b/languages/en/09_EIP1193/readme.md
new file mode 100644
index 0000000..3748faa
--- /dev/null
+++ b/languages/en/09_EIP1193/readme.md
@@ -0,0 +1,89 @@
+In Lecture 3, [Connect Wallet](./03_ConnectWallet/readme.md), we discussed how to connect a wallet but didn't explore the underlying principles. Two key protocols, EIP1193 and EIP6963, establish the standards for connecting wallets to decentralized applications (DApps). EIP1193 was the first of these standards, though it has some limitations that EIP6963 aims to address and improve upon.
+
+This article will explain the fundamental concepts of these protocols to help you grasp the technical process of connecting wallets.
+
+## EIP1193
+
+The full specification for EIP1193 is available at [https://eips.ethereum.org/EIPS/eip-1193](https://eips.ethereum.org/EIPS/eip-1193). It outlines how DApps can use JavaScript to interact with wallets through a browser. With this specification, wallets can offer specific interfaces, which DApps can then utilize.
+
+The specification is quite straightforward. It describes how the `ethereum` object should appear on the global `window` object within the browser at runtime, detailing its methods and events.
+
+For a DApp, the first step is to check if `window.ethereum` exists. If it does, the DApp can interact with the wallet by calling methods on `window.ethereum`, similar to how other browser APIs like `window.localStorage` are used.
+
+Here's a simple example to retrieve the chain ID, which is a unique identifier for different blockchain networks:
+
+```javascript
+const ethereum = window.ethereum;
+
+ethereum
+ .request({ method: "eth_chainId" })
+ .then((chainId) => {
+ console.log(`hexadecimal string: ${chainId}`);
+ console.log(`decimal number: ${parseInt(chainId, 16)}`);
+ })
+ .catch((error) => {
+ console.error(`Error fetching chainId: ${error.code}: ${error.message}`);
+ });
+```
+
+### Exploring Ethereum Integration with Browser Consoles and EIP Standards
+
+To observe the functionality of Ethereum-related commands, execute the following code in your browser's console:
+
+![Demo of console execution](./img/demo.png)
+
+For extended capabilities, you can adjust the parameters when you call the `request` function. The full list of supported methods is available in the [JSON-RPC API documentation](https://ethereum.org/developers/docs/apis/json-rpc). Keep in mind that some methods might not be supported by all wallets, so it’s important to implement proper error handling. Additionally, certain wallets might offer unique methods or adhere to specific conventions, so checking their documentation is recommended.
+
+When developing a decentralized application (DApp), it is common practice to use libraries like `web3.js`, `ethers`, or `viem` to manage wallet interactions. These libraries provide a simplified interface for communicating with wallets.
+
+### Understanding EIP1193 and its Limitations
+
+EIP1193 outlines a standard for wallet interactions but has a notable limitation. It relies on a single `window.ethereum` object. This poses a problem when users have multiple wallets installed, as they can only select one wallet at a time. This creates competition among wallets for control over `window.ethereum`, which may degrade user experience and stifle healthy competition.
+
+To address this, some wallets have introduced their own objects, such as `window.tokenPocket`, but this approach lacks standardization and complicates DApp development by requiring adaptations for various wallets, thus increasing development effort.
+
+### Introducing EIP6963
+
+EIP6963 was developed to resolve these challenges. The specification can be found at [EIP6963 documentation](https://eips.ethereum.org/EIPS/eip-6963).
+
+Unlike EIP1193, EIP6963 does not rely on the `window.ethereum` object. Instead, it utilizes events dispatched on the `window` object, which allows multiple wallets to coexist without conflict. This standard enables wallets to notify DApps of their presence through event dispatching, allowing users to choose their preferred wallet seamlessly.
+
+From a technical perspective, this involves using the browser's `window.addEventListener` to listen for wallet-related events and `window.dispatchEvent` to send out notifications. All related messages are prefixed with `eip6963:`, and detailed message definitions can be found in the EIP6963 specification.
+
+For developers, leveraging community libraries can simplify the adoption of EIP6963, much like with EIP1193. For example, with the `wagmi` library, you can enable EIP6963 support by configuring the [multiInjectedProviderDiscovery](https://wagmi.sh/core/api/createConfig#multiinjectedproviderdiscovery).
+
+Similarly, when using [Ant Design Web3](https://web3.ant.design/zh-CN/components/wagmi#eip6963), you can configure the `WagmiWeb3ConfigProvider`'s `eip6963` to integrate EIP6963 into your DApp. This setup will automatically detect available wallets and facilitate user connections.
+
+Below is an example update based on a previous course example to illustrate these concepts.
+
+```diff
+export default function Web3() {
+ return (
+
+
+
+
+
+
+
+
+ );
+}
+```
+
+In this setup, `eip6963` is configured to facilitate wallet connections using the EIP6963 protocol, preventing potential conflicts between multiple wallets. Additionally, the `autoAddInjectedWallets` feature has been incorporated to automatically include detected wallets in the Ant Design Web3 UI. This enhancement improves the user experience by allowing users to choose from wallets they have already installed.
+
+## Summary
+
+EIP1193 and EIP6963 both interact with wallets via the browser's JavaScript API. They require wallets to inject objects or send events to the DApp's runtime, which can occur through a browser extension like Chrome or when accessing a DApp through a wallet's built-in browser.
+
+However, there are scenarios where users might not have installed extensions or are using a mobile browser that doesn't support extensions. Additionally, users might need to connect to a DApp with a wallet client installed on a different device. Unfortunately, neither EIP1193 nor EIP6963 fully supports these situations. Therefore, alternative methods, such as WalletConnect, are necessary for connecting wallets. The following section will explain how to use WalletConnect to connect wallets.
diff --git a/languages/en/10_WalletConnect/Web3.tsx b/languages/en/10_WalletConnect/Web3.tsx
new file mode 100644
index 0000000..9ba64db
--- /dev/null
+++ b/languages/en/10_WalletConnect/Web3.tsx
@@ -0,0 +1,153 @@
+import {
+ Address,
+ ConnectButton,
+ Connector,
+ NFTCard,
+ useAccount,
+} from "@ant-design/web3";
+import {
+ MetaMask,
+ WagmiWeb3ConfigProvider,
+ WalletConnect,
+} from "@ant-design/web3-wagmi";
+import { Button, message } from "antd";
+import { parseEther } from "viem";
+import {
+ createConfig,
+ http,
+ useReadContract,
+ useWriteContract,
+ useWatchContractEvent,
+} from "wagmi";
+import { mainnet } from "wagmi/chains";
+import { injected, walletConnect } from "wagmi/connectors";
+
+const config = createConfig({
+ chains: [mainnet],
+ transports: {
+ [mainnet.id]: http(),
+ },
+ connectors: [
+ injected({
+ target: "metaMask",
+ }),
+ walletConnect({
+ projectId: "c07c0051c2055890eade3556618e38a6",
+ showQrModal: false,
+ }),
+ ],
+});
+
+const CallTest = () => {
+ const { account } = useAccount();
+ const result = useReadContract({
+ abi: [
+ {
+ type: "function",
+ name: "balanceOf",
+ stateMutability: "view",
+ inputs: [{ name: "account", type: "address" }],
+ outputs: [{ type: "uint256" }],
+ },
+ ],
+ // Sepolia test contract 0x418325c3979b7f8a17678ec2463a74355bdbe72c
+ address: "0xEcd0D12E21805803f70de03B72B1C162dB0898d9",
+ functionName: "balanceOf",
+ args: [account?.address as `0x${string}`],
+ });
+ const { writeContract } = useWriteContract();
+
+ useWatchContractEvent({
+ address: "0xEcd0D12E21805803f70de03B72B1C162dB0898d9",
+ abi: [
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: false,
+ internalType: "address",
+ name: "minter",
+ type: "address",
+ },
+ {
+ indexed: false,
+ internalType: "uint256",
+ name: "amount",
+ type: "uint256",
+ },
+ ],
+ name: "Minted",
+ type: "event",
+ },
+ ],
+ eventName: "Minted",
+ onLogs() {
+ message.success("new minted!");
+ },
+ });
+
+ return (
+
+ {result.data?.toString()}
+
+
+ );
+};
+
+export default function Web3() {
+ return (
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/languages/en/10_WalletConnect/img/walletconnect.png b/languages/en/10_WalletConnect/img/walletconnect.png
new file mode 100644
index 0000000..85dedb0
Binary files /dev/null and b/languages/en/10_WalletConnect/img/walletconnect.png differ
diff --git a/languages/en/10_WalletConnect/img/walletqrcode.png b/languages/en/10_WalletConnect/img/walletqrcode.png
new file mode 100644
index 0000000..10d27f0
Binary files /dev/null and b/languages/en/10_WalletConnect/img/walletqrcode.png differ
diff --git a/languages/en/10_WalletConnect/readme.md b/languages/en/10_WalletConnect/readme.md
new file mode 100644
index 0000000..a3fbaaf
--- /dev/null
+++ b/languages/en/10_WalletConnect/readme.md
@@ -0,0 +1,132 @@
+This session will guide you on using [WalletConnect](https://walletconnect.com/) for linking mobile app wallets, a key aspect of improving the user experience in decentralized applications (DApps).
+
+---
+
+## Understanding WalletConnect
+
+In earlier lessons, we explored how to connect browser extension wallets within DApps. However, many users prefer to link their mobile wallet apps to a DApp accessed on a computer or connect the wallet on their phone when using a DApp through a mobile browser.
+
+In these scenarios, since users are not on a PC browser, it's not feasible to connect wallets via browser extensions. The WalletConnect protocol addresses this challenge by using server-side relays to establish a connection between the wallet and the DApp.
+
+WalletConnect is an open-source protocol, developed and maintained by the non-profit WalletConnect Foundation. It allows users to connect their mobile wallet applications to DApps without needing any browser extensions.
+
+## How WalletConnect Functions
+
+Let's begin by exploring what the WalletConnect protocol involves. It adheres to the [EIP-1328](https://eips.ethereum.org/EIPS/eip-1328) standard, which defines its framework.
+
+```
+request = "wc" ":" topic [ "@" version ][ "?" parameters ]
+topic = STRING
+version = 1*DIGIT
+parameters = parameter *( "&" parameter )
+parameter = key "=" value
+key = STRING
+value = STRING
+```
+
+This document specifies the parameters for a URI, with distinctions between versions 1.0 and 2.0.
+
+Here's a breakdown of the parameters for version 2.0:
+
+- `symKey`: This is the symmetric key utilized for message encryption through the relay.
+- `methods`: Lists the supported JSON-RPC methods.
+- `relay-protocol`: Indicates the transport protocol employed by the relay messaging service.
+- `relay-data (optional)`: Contains data relevant to the relay messaging protocol, potentially including configuration details required by specific protocols.
+- `expiryTimestamp (optional)`: Specifies when the pairing will expire, generally aligning with the expiration of the symmetric key for the relay service.
+
+For illustration, consider the following example:
+
+```
+wc:02c2d94b12d9fde35a149a3620544892b98ea14d45832c9bbd903af9d57d3ea9@2?expiryTimestamp=1710298160&relay-protocol=irn&symKey=8327616fa992557f5d125fe5397116c73ace7f368ac6183724052b1bcb917414
+```
+
+In this context, `relay-protocol` refers to the protocol being used, which is typically `irn`. This protocol is defined by WalletConnect, as detailed in their [documentation](https://specs.walletconnect.com/2.0/specs/servers/relay/relay-server-rpc). Let's explore how this protocol facilitates wallet connections.
+
+When you connect a wallet to a DApp using WalletConnect, you might notice that the browser sends requests to `wss://relay.walletconnect.com`.
+
+![wallet](./img/walletconnect.png)
+
+These requests use the WebSocket protocol, which allows for two-way communication. The requests are routed through WalletConnect's relay server, establishing a connection between the wallet and the DApp. The address `wss://relay.walletconnect.com` is the default for the `irn` protocol. For those interested, WalletConnect's official documentation offers guidance on setting up your own relay server.
+
+According to [EIP-1328](https://eips.ethereum.org/EIPS/eip-1328), WalletConnect 2.0 is highly flexible because it primarily defines a URI specification. The `irn` protocol, however, specifies additional details like message formats, encryption methods, and transmission protocols. Most wallets currently support the `irn` protocol, making it the standard choice for connections. When we refer to the WalletConnect protocol, we're often including aspects of the `irn` protocol. While the protocol is complex, DApps typically don't need to worry about these intricacies. We'll now look at how to integrate WalletConnect into a DApp.
+
+## How to Use WalletConnect
+
+Here's a guide on using WalletConnect to connect wallets within the DApp for this course.
+
+In wagmi, WalletConnect support is built-in through the [WalletConnect SDK integration](https://wagmi.sh/core/api/connectors/walletConnect). Integration is straightforward when combined with Ant Design Web3's [ConnectModal](https://web3.ant.design/components/connect-modal-cn) component.
+
+First, include `walletConnect` in the wagmi configuration.
+
+```diff
+- import { injected } from "wagmi/connectors";
++ import { injected, walletConnect } from "wagmi/connectors";
+
+const config = createConfig({
+ chains: [mainnet],
+ transports: {
+ [mainnet.id]: http(),
+ },
+ connectors: [
+ injected({
+ target: "metaMask",
+ }),
++ walletConnect({
++ projectId: 'c07c0051c2055890eade3556618e38a6',
++ showQrModal: false,
++ }),
+ ],
+});
+```
+
+For testing purposes, Ant Design Web3 provides a `projectId`. If you're working on a real project, you'll need to obtain your own `projectId` by registering at [https://cloud.walletconnect.com/](https://cloud.walletconnect.com/). The `showQrModal` setting is used to turn off the default ConnectModal popup, preventing multiple popups from appearing.
+
+Once you incorporate `walletConnect`, it's ready for immediate use. Ant Design Web3 automatically checks if the wallet supports WalletConnect. If it does, a QR code will appear in the ConnectModal, allowing users to connect by scanning it, even if the wallet plugin isn't installed on their device.
+
+![walletqrcode](./img/walletqrcode.png)
+
+Additionally, you have the option to add a separate WalletConnect wallet selection. This can be useful for providing users with more connectivity options or for specific project requirements.
+
+```diff
+import {
+ Address,
+ ConnectButton,
+ Connector,
+ NFTCard,
+ useAccount,
+} from "@ant-design/web3";
+import {
+ MetaMask,
+ WagmiWeb3ConfigProvider,
++ WalletConnect,
+} from "@ant-design/web3-wagmi";
+import { Button, message } from "antd";
+import { parseEther } from "viem";
+import { createConfig, http, useReadContract, useWriteContract } from "wagmi";
+import { mainnet } from "wagmi/chains";
+import { injected, walletConnect } from "wagmi/connectors";
+
+//...
+
+export default function Web3() {
+ return (
+
+
+
+
+
+
+
+
+ );
+}
+```
+
+The full code is available in [web3.tsx](./web3.tsx) for cross-examination.
diff --git a/languages/en/11_MultipleChain/Web3.tsx b/languages/en/11_MultipleChain/Web3.tsx
new file mode 100644
index 0000000..9c2b31b
--- /dev/null
+++ b/languages/en/11_MultipleChain/Web3.tsx
@@ -0,0 +1,136 @@
+import { createConfig, http, useReadContract, useWriteContract } from "wagmi";
+import { mainnet, sepolia, polygon } from "wagmi/chains";
+import {
+ WagmiWeb3ConfigProvider,
+ MetaMask,
+ Sepolia,
+ Polygon,
+} from "@ant-design/web3-wagmi";
+import {
+ Address,
+ NFTCard,
+ Connector,
+ ConnectButton,
+ useAccount,
+ useProvider,
+} from "@ant-design/web3";
+import { injected } from "wagmi/connectors";
+import { Button, message } from "antd";
+import { parseEther } from "viem";
+
+const config = createConfig({
+ chains: [mainnet, sepolia, polygon],
+ transports: {
+ [mainnet.id]: http(),
+ [sepolia.id]: http(),
+ [polygon.id]: http(),
+ },
+ connectors: [
+ injected({
+ target: "metaMask",
+ }),
+ ],
+});
+
+const contractInfo = [
+ {
+ id: 1,
+ name: "Ethereum",
+ contractAddress: "0xEcd0D12E21805803f70de03B72B1C162dB0898d9",
+ },
+ {
+ id: 5,
+ name: "Sepolia",
+ contractAddress: "0x418325c3979b7f8a17678ec2463a74355bdbe72c",
+ },
+ {
+ id: 137,
+ name: "Polygon",
+ contractAddress: "0x418325c3979b7f8a17678ec2463a74355bdbe72c",
+ },
+];
+
+const CallTest = () => {
+ const { account } = useAccount();
+ const { chain } = useProvider();
+ const result = useReadContract({
+ abi: [
+ {
+ type: "function",
+ name: "balanceOf",
+ stateMutability: "view",
+ inputs: [{ name: "account", type: "address" }],
+ outputs: [{ type: "uint256" }],
+ },
+ ],
+ address: contractInfo.find((item) => item.id === chain?.id)
+ ?.contractAddress as `0x${string}`,
+ functionName: "balanceOf",
+ args: [account?.address as `0x${string}`],
+ });
+ const { writeContract } = useWriteContract();
+
+ return (
+
+ {result.data?.toString()}
+
+
+ );
+};
+
+export default function Web3() {
+ return (
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/languages/en/11_MultipleChain/img/image.png b/languages/en/11_MultipleChain/img/image.png
new file mode 100644
index 0000000..893704d
Binary files /dev/null and b/languages/en/11_MultipleChain/img/image.png differ
diff --git a/languages/en/11_MultipleChain/readme.md b/languages/en/11_MultipleChain/readme.md
new file mode 100644
index 0000000..3bbfe5a
--- /dev/null
+++ b/languages/en/11_MultipleChain/readme.md
@@ -0,0 +1,173 @@
+## Multi-Chain Support in DApps
+
+Ethereum offers not only a mainnet and various testnets but also a rich Layer 2 ecosystem. For certain decentralized applications (DApps), there may be a need to connect with different blockchain networks. This session will guide developers on integrating multiple blockchains within a DApp.
+
+### What is Multi-Chain Support?
+
+Supporting multiple blockchains in a DApp allows it to function and interact across different blockchain platforms. Rather than being confined to a single blockchain ecosystem, the DApp can leverage the unique advantages of each platform, such as enhanced functionality, improved user experience, or increased efficiency. This capability enables users to transfer assets like tokens from one blockchain to another using technologies like blockchain bridges. As a result, DApps can utilize these assets across different chains. Each blockchain has its own set of features, like varying transaction speeds and fees, and distinct user communities. By supporting multiple blockchains, a DApp can attract and unify users from these diverse communities.
+
+### How to Implement Multi-Chain Support
+
+To enable multi-chain functionality in a DApp, developers typically use technologies such as cross-chain bridges, multi-version smart contract management, and inter-chain communication protocols. Although this can make development and maintenance more complex, the scalability and diversity benefits encourage many DApps to adopt multi-chain support.
+
+### Coding for Multi-Chain NFT Contracts
+
+Let's consider a multi-chain NFT (Non-Fungible Token) contract. Several strategies can enable NFTs to operate across multiple blockchains:
+
+1. **Cross-Chain Bridges**: These are commonly used to transfer assets between blockchains. An NFT can be "wrapped" and moved from one chain to another, allowing the DApp to use the "wrapped" asset on the target chain.
+
+2. **Sidechains**: These auxiliary chains extend the main chain’s capabilities. NFTs can move between the main chain and its sidechains, providing liquidity across related blockchains.
+
+3. **Cross-Chain Protocols**: Protocols like Cosmos or Polkadot create interoperable ecosystems between blockchains, facilitating easy movement of NFTs across compatible networks.
+
+4. **Multi-Chain Smart Contracts**: Some projects use smart contracts that function on multiple blockchains, ensuring the NFT's attributes and ownership remain consistent across all chains.
+
+5. **Decentralized Identity and Metadata Storage**: Using decentralized storage solutions like IPFS (InterPlanetary File System) ensures that, even if NFT tokens exist on different blockchains, their associated content remains unified and persistent.
+
+In previous sessions [Lecture 4](./04_CallContract/readme.md) and [Lecture 5](./05_Events/readme.md), we covered how to invoke contracts and listen for events. Building on this foundation, we will now explore how to modify code to support multiple blockchains in a DApp.
+
+```diff
+import { createConfig, http, useReadContract, useWriteContract } from "wagmi";
+- import { mainnet, sepolia } from "wagmi/chains";
++ import { mainnet, sepolia, polygon } from "wagmi/chains";
+import {
+ WagmiWeb3ConfigProvider,
+ MetaMask,
+ Sepolia,
++ Polygon
+} from "@ant-design/web3-wagmi";
+import {
+ Address,
+ NFTCard,
+ Connector,
+ ConnectButton,
+ useAccount,
++ useProvider
+} from "@ant-design/web3";
+import { injected } from "wagmi/connectors";
+import { Button, message } from "antd";
+import { parseEther } from "viem";
+
+const config = createConfig({
+- chains: [mainnet, sepolia],
++ chains: [mainnet, sepolia, polygon],
+ transports: {
+ [mainnet.id]: http(),
+ [sepolia.id]: http(),
++ [polygon.id]: http(),
+ },
+ connectors: [
+ injected({
+ target: "metaMask",
+ }),
+ ],
+});
+
++ const contractInfo = [
++ {
++ id:1,
++ name: "Ethereum",
++ contractAddress: "0xEcd0D12E21805803f70de03B72B1C162dB0898d9"
++ }, {
++ id:5,
++ name: "Sepolia",
++ contractAddress: "0x418325c3979b7f8a17678ec2463a74355bdbe72c"
++ }, {
++ id:137,
++ name: "Polygon",
++ contractAddress: "0x418325c3979b7f8a17678ec2463a74355bdbe72c"
++ }
++ ]
+
+const CallTest = () => {
+ const { account } = useAccount();
++ const { chain } = useProvider();
+ const result = useReadContract({
+ abi: [
+ {
+ type: "function",
+ name: "balanceOf",
+ stateMutability: "view",
+ inputs: [{ name: "account", type: "address" }],
+ outputs: [{ type: "uint256" }],
+ },
+ ],
+- // Sepolia test contract 0x418325c3979b7f8a17678ec2463a74355bdbe72c
+- address: "0xEcd0D12E21805803f70de03B72B1C162dB0898d9",
++ address: contractInfo.find((item) => item.id === chain?.id)?.contractAddress as `0x${string}`,
+ functionName: "balanceOf",
+ args: [account?.address as `0x${string}`],
+ });
+ const { writeContract } = useWriteContract();
+
+ return (
+
+ {result.data?.toString()}
+
+
+ );
+};
+
+export default function Web3() {
+ return (
+
+
+
+
+
+
+
+
+ );
+}
+```
+When you click to switch the network to `Polygon`, a popup window will appear:
+
+![Switch Chain](./img/image.png)
+
+The `useProvider` feature from `@ant-design/web3` gives us access to information about the current blockchain network. With the chain ID acquired after switching, we can identify the contract addresses for various networks stored in our `contractInfo`. This functionality allows us to check balances and execute transactions on different networks by using the `mint` button, directing actions to the appropriate contract addresses on those networks.
diff --git a/languages/en/12_Signature/img/demo.png b/languages/en/12_Signature/img/demo.png
new file mode 100644
index 0000000..1829270
Binary files /dev/null and b/languages/en/12_Signature/img/demo.png differ
diff --git a/languages/en/12_Signature/readme.md b/languages/en/12_Signature/readme.md
new file mode 100644
index 0000000..2f113de
--- /dev/null
+++ b/languages/en/12_Signature/readme.md
@@ -0,0 +1,269 @@
+Signature verification is a crucial process in ensuring secure transactions. In this lesson, we'll explore how to implement client-side signing and server-side verification.
+
+---
+
+## Why are Signatures Important?
+
+In decentralized applications (DApps), user identities are typically tied to blockchain addresses, with each address representing a unique user. In traditional systems, we authenticate users using methods like passwords and SMS codes. But how do we confirm that someone is the rightful owner of a blockchain address in a DApp?
+
+Previously, we demonstrated how to connect to a blockchain address by accessing the user's wallet, allowing the DApp to retrieve the address. However, just having access to an address doesn't confirm ownership. Should we permit users to manage related assets in the DApp simply because they've connected their address?
+
+For assets that exist on the blockchain, this could work, as smart contract interactions require signing with the private key linked to the address. But not all assets are on-chain. If your DApp interacts with assets stored in a traditional database, you must verify that the user has the appropriate permissions.
+
+Relying solely on wallet connections to verify ownership is unreliable because the process of retrieving an address can be deceived by client-side spoofing. Therefore, we require users to authenticate their identity by signing a message with their private key. The DApp's server then uses the corresponding public key to verify the signature, ensuring the user has the right permissions.
+
+In this lesson, we'll create a simple example where you can sign a message and verify your identity after connecting to a wallet.
+
+![demo](./img/demo.png)
+
+### Implementing Frontend Signature
+
+Let's start by implementing the frontend logic. Following the steps from the previous lesson, we will quickly set up [wallet connection](../03_ConnectWallet/readme.md).
+
+Begin by creating a new file named `pages/sign/index.tsx`. Copy the relevant code from the previous example and modify it to build a new component called `components/SignDemo`.
+
+This approach ensures continuity and builds on the existing framework, making it easier to integrate the new functionality.
+
+```diff
+import React from 'react';
+- import { Address, ConnectButton, Connector, NFTCard } from "@ant-design/web3";
+import { MetaMask, WagmiWeb3ConfigProvider } from "@ant-design/web3-wagmi";
+import { createConfig, http } from 'wagmi';
+import { injected } from "wagmi/connectors";
+import { mainnet } from 'wagmi/chains';
++ import SignDemo from '../../components/SignDemo';
+
+
+const config = createConfig({
+ chains: [mainnet],
+ transports: {
+ [mainnet.id]: http(),
+ },
+ connectors: [
+ injected({
+ target: "metaMask",
+ }),
+ ],
+});
+const Demo:React.FC = () => {
+ return (
+
++
+-
+-
+-
+-
+-
+
+ );
+}
+export default Demo;
+
+```
+In the `SignDemo` component, you'll create a simple button for connecting a wallet. Below is the code for this button:
+
+```tsx
+import React from "react";
+import { ConnectButton, Connector } from "@ant-design/web3";
+
+const SignDemo: React.FC = () => {
+ return (
+
+
+
+ );
+};
+export default SignDemo;
+```
+We have now established the fundamental connection logic.
+
+Next, we will enhance the logic for the signature section. To do this, we first need to utilize the `useSignMessage` hook from `wagmi` and the `useAccount` hooks from Ant Design Web3 to implement the `doSignature` function.
+
+```diff
+import React from "react";
+- import { ConnectButton, Connector } from "@ant-design/web3";
++ import { ConnectButton, Connector, useAccount } from "@ant-design/web3";
++ import { useSignMessage } from "wagmi";
++ import { message } from "antd";
+
+const SignDemo: React.FC = () => {
++ const { signMessageAsync } = useSignMessage();
++ const { account } = useAccount();
+
++ const doSignature = async () => {
++ try {
++ const signature = await signMessageAsync({
++ message: "test message for WTF-DApp demo",
++ });
++ } catch (error: any) {
++ message.error(`Signature failed: ${error.message}`);
++ }
++ };
+
+ return (
+
+
+
+ );
+};
+export default SignDemo;
+```
+Let's create a button that activates the `doSignature` method when clicked. To ensure functionality, we've configured the button's `disabled` attribute so that it only becomes active after a successful connection is established.
+
+```diff
+import React from "react";
+import { ConnectButton, Connector, useAccount } from "@ant-design/web3";
+import { useSignMessage } from "wagmi";
+- import { message } from "antd";
++ import { message, Space, Button } from "antd";
+
+const SignDemo: React.FC = () => {
+
+// ...
+
+ return (
++
+
+
+
++
++
+ );
+};
+export default SignDemo;
+```
+
+To implement the client-side signature logic, we must also handle server-side verification. As previously discussed, signatures created on the client need to be verified by the server. Let's set up the server-side verification endpoint next.
+
+## Setting Up Server-Side Signature Verification
+
+For verifying signatures on the server, libraries like `viem` or `ethers` are commonly used. We'll start by creating a new file named `/pages/api/signatureCheck.ts`. In Next.js, any file within the `/api` directory is automatically treated as a server-side [Vercel Function](https://vercel.com/docs/functions/quickstart).
+
+We'll use the `viem` library to implement the server-side signature verification:
+
+```ts
+// /pages/api/signatureCheck.ts
+import type { NextApiRequest, NextApiResponse } from "next";
+import { createPublicClient, http } from "viem";
+import { mainnet } from "viem/chains";
+
+export const publicClient = createPublicClient({
+ chain: mainnet,
+ transport: http(),
+});
+
+export default async function handler(
+ req: NextApiRequest,
+ res: NextApiResponse
+) {
+ try {
+ const body = req.body;
+ const valid = await publicClient.verifyMessage({
+ address: body.address,
+ message: "test message for WTF-DApp demo",
+ signature: body.signature,
+ });
+ res.status(200).json({ data: valid });
+ } catch (err: any) {
+ res.status(500).json({ error: err.message });
+ }
+}
+```
+
+If you're proficient with `ethers`, you can use the following code:
+
+```tsx
+const verifyMessage = async (signerAddress, signature) => {
+ const recoveredAddress = ethers.utils.verifyMessage(
+ "test message for WTF-DApp demo",
+ signature
+ );
+ return recoveredAddress === signerAddress;
+};
+```
+
+## Front-end API Call Signature Verification
+
+To conclude, let's implement the logic needed to handle front-end API call signature verification. Simply copy the code provided below and paste it into the `SignDemo` component:
+
+```tsx
+const checkSignature = async (params: {
+ address?: string;
+ signature: string;
+}) => {
+ try {
+ const response = await fetch("/api/signatureCheck", {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify(params),
+ });
+ const result = await response.json();
+ if (result.data) {
+ message.success("Signature success");
+ } else {
+ message.error("Signature failed");
+ }
+ } catch (error) {
+ message.error("An error occurred");
+ }
+};
+```
+
+In the `doSignature` method, the next step is to call this function and incorporate a Loading state to indicate progress.
+
+```diff
+import React from "react";
+import { ConnectButton, Connector, useAccount } from "@ant-design/web3";
+import { useSignMessage } from "wagmi";
+import { message, Space, Button } from "antd";
+
+const SignDemo: React.FC = () => {
+ const { signMessageAsync } = useSignMessage();
+ const { account } = useAccount();
++ const [signLoading, setSignLoading] = React.useState(false);
+
+ const doSignature = async () => {
++ setSignLoading(true);
+ try {
+ const signature = await signMessageAsync({
+ message: "test message for WTF-DApp demo",
+ });
++ await checkSignature({
++ address: account?.address,
++ signature,
++ });
+ } catch (error: any) {
+ message.error(`Signature failed: ${error.message}`);
+ }
++ setSignLoading(false);
+ };
+
+// checkSignature here
+
+ return (
+
+
+
+
+
+
+ );
+};
+export default SignDemo;
+```
+
+To access all the relevant source files, the complete code is available in the [sign directory](../demo/pages/sign).
diff --git a/languages/en/13_Payment/img/send.png b/languages/en/13_Payment/img/send.png
new file mode 100644
index 0000000..83c8e45
Binary files /dev/null and b/languages/en/13_Payment/img/send.png differ
diff --git a/languages/en/13_Payment/readme.md b/languages/en/13_Payment/readme.md
new file mode 100644
index 0000000..6d85ad9
--- /dev/null
+++ b/languages/en/13_Payment/readme.md
@@ -0,0 +1,181 @@
+This lecture explains how to make transfers and receive payments using blockchain technology.
+
+---
+
+## Transfers
+
+In the world of blockchain, a transfer involves moving assets between participants. These assets can be cryptocurrencies like Bitcoin or Ethereum, or other digital assets such as tokens and NFTs. Transfers are logged on the blockchain and secured through the network's consensus mechanism, making them a fundamental operation in any blockchain system. Here are the key elements involved in a transfer:
+
+- **Source Address (Sender):** The blockchain address from which the assets will be sent.
+- **Destination Address (Receiver):** The blockchain address that will receive the assets.
+- **Amount of Assets:** The specific amount or value of assets to be transferred.
+- **Transaction Fee:** This fee is usually charged by miners or validator nodes that process the transaction. The size of this fee can affect how quickly the transaction is confirmed.
+- **Network:** The specific blockchain network where the transfer takes place, such as Bitcoin or Ethereum.
+
+Once a transfer is initiated, it must be confirmed by miners or participants in the consensus mechanism. After confirmation, the transaction is recorded in a block, which is then added to the blockchain.
+
+The general steps for making a transfer on a blockchain are as follows:
+
+1. The person initiating the transfer signs the transaction details with their private key and sends this information to the network.
+
+2. Nodes (miners or validators) in the network receive and verify the transaction request, checking the validity of both the signature and the transaction itself.
+
+3. After verification, the transfer is bundled with other transactions to create a new block.
+
+4. This block is confirmed via the network's consensus mechanism and then added to the blockchain.
+
+5. Upon completion, the balance at the destination address is updated to reflect the transferred assets.
+
+The specifics of this transfer process can vary depending on the particular blockchain technology and type of asset being used.
+
+### Initiate a Transfer
+
+Let's start by implementing the frontend logic. Referring to the previous lessons, we will quickly set up the wallet connection as outlined [here](../03_ConnectWallet/readme.md).
+
+Begin by creating a new file named `pages/transaction/index.tsx`. Copy the code from the previous example into this file. Then, modify it to develop a new component called `SendEth.tsx` within the `pages/transaction` directory.
+
+```diff
+ import React from "react";
+ import { MetaMask, WagmiWeb3ConfigProvider} from "@ant-design/web3-wagmi";
+ import { createConfig, http } from "wagmi";
+ import { injected } from "wagmi/connectors";
+ import { mainnet, sepolia } from "wagmi/chains";
+ import { ConnectButton, Connector } from '@ant-design/web3';
++ import { SendEth } from "../../components/SendEth";
+
+
+ const config = createConfig({
+ chains: [mainnet, sepolia],
+ transports: {
+ [mainnet.id]: http(),
+ [sepolia.id]: http(),
+ },
+ connectors: [
+ injected({
+ target: "metaMask",
+ }),
+ ],
+ });
+ const TransactionDemo: React.FC = () => {
+
+ return (
+
+
+
+
++
+
+ );
+ };
+ export default TransactionDemo;
+
+```
+In the `SendEth` component, set up input fields for the recipient's address (`to`) and the transaction amount (`value`). Also, include a button to execute the transfer. Here's the code for reference:
+
+```tsx
+import * as React from "react";
+import { Button, Checkbox, Form, type FormProps, Input } from "antd";
+import {
+ type BaseError,
+ useSendTransaction,
+ useWaitForTransactionReceipt,
+} from "wagmi";
+import { parseEther } from "viem";
+
+type FieldType = {
+ to: `0x${string}`;
+ value: string;
+};
+
+export const SendEth: React.FC = () => {
+ const {
+ data: hash,
+ error,
+ isPending,
+ sendTransaction,
+ } = useSendTransaction();
+
+ const { isLoading: isConfirming, isSuccess: isConfirmed } =
+ useWaitForTransactionReceipt({ hash });
+
+ const onFinish: FormProps["onFinish"] = (values) => {
+ console.log("Success:", values);
+ sendTransaction({ to: values.to, value: parseEther(values.value) });
+ };
+
+ const onFinishFailed: FormProps["onFinishFailed"] = (
+ errorInfo
+ ) => {
+ console.log("Failed:", errorInfo);
+ };
+
+ return (
+
+ label="to"
+ name="to"
+ rules={[{ required: true, message: "Please input!" }]}
+ >
+
+
+
+
+ label="value"
+ name="value"
+ rules={[{ required: true, message: "Please input!" }]}
+ >
+
+
+
+
+
+
+
+ {hash &&
Transaction Hash: {hash}
}
+ {isConfirming &&
Waiting for confirmation...
}
+ {isConfirmed &&
Transaction confirmed.
}
+ {error && (
+
Error: {(error as BaseError).shortMessage || error.message}
+ )}
+
+ );
+};
+```
+In the code, we used the `Form` component from [Ant Design](https://ant.design/components/form) and hooks from `wagmi` to start a transaction and track its status. To see how this works, fill in the `to` and `value` fields, then click 'initiate'. The outcome will appear as shown in the image below:
+
+![Initiate](./img/send.png)
+
+With this, we've successfully implemented a basic transfer feature. You can try out the complete demo by visiting [https://wtf-dapp.vercel.app/transaction](https://wtf-dapp.vercel.app/transaction). Please proceed cautiously to avoid potential financial loss.
+
+## Receiving Payments
+
+Receiving payments on the blockchain usually involves accepting cryptocurrencies or other blockchain-based assets like tokens (including NFTs). To receive payments, provide your public address to the sender. This can be done by sharing the address directly or generating a QR code that can be easily scanned by mobile wallets.
+
+Key points to remember:
+
+1. Never share your private key or mnemonic phrase; this information should remain private.
+2. Double-check that the address you provide is correct. An incorrect address can result in lost funds.
+3. The sender is responsible for network fees (also known as "miner fees"), which can influence how fast a transaction is processed.
+4. Once a blockchain transaction is initiated, it cannot be canceled; you must wait for it to be confirmed by the network.
+5. Ensure you're using the correct blockchain network. For instance, only send Ether (ETH) to an Ethereum network address and Bitcoin (BTC) to a Bitcoin network address.
+
+### QR Code Payments
+
+`ant-design-web3` is developing a quick payment feature to facilitate QR code payments for specific wallets. This will allow users to select a blockchain, choose a wallet, and enter an amount easily. Stay tuned for updates!
diff --git a/languages/en/14_LocalDev/img/addnetwork.png b/languages/en/14_LocalDev/img/addnetwork.png
new file mode 100644
index 0000000..99e7ec6
Binary files /dev/null and b/languages/en/14_LocalDev/img/addnetwork.png differ
diff --git a/languages/en/14_LocalDev/img/hardhat.png b/languages/en/14_LocalDev/img/hardhat.png
new file mode 100644
index 0000000..ebbbcfc
Binary files /dev/null and b/languages/en/14_LocalDev/img/hardhat.png differ
diff --git a/languages/en/14_LocalDev/img/initfiles.png b/languages/en/14_LocalDev/img/initfiles.png
new file mode 100644
index 0000000..7e8b45f
Binary files /dev/null and b/languages/en/14_LocalDev/img/initfiles.png differ
diff --git a/languages/en/14_LocalDev/img/localnode.png b/languages/en/14_LocalDev/img/localnode.png
new file mode 100644
index 0000000..5e03bd0
Binary files /dev/null and b/languages/en/14_LocalDev/img/localnode.png differ
diff --git a/languages/en/14_LocalDev/img/minttest.png b/languages/en/14_LocalDev/img/minttest.png
new file mode 100644
index 0000000..6e8d04b
Binary files /dev/null and b/languages/en/14_LocalDev/img/minttest.png differ
diff --git a/languages/en/14_LocalDev/readme.md b/languages/en/14_LocalDev/readme.md
new file mode 100644
index 0000000..15f9f32
--- /dev/null
+++ b/languages/en/14_LocalDev/readme.md
@@ -0,0 +1,232 @@
+In previous lessons, we explored how to develop contracts using Remix through CloudIDE. In a local development setting, we can leverage additional tools like Git for version control to enhance development efficiency. This lesson will guide you through the process of developing and debugging contracts locally, as well as writing unit tests to verify smart contract logic.
+
+---
+
+## Setting Up the Project
+
+The Ethereum ecosystem provides a variety of development tools, including [Hardhat](https://hardhat.org/) and [Foundry](https://getfoundry.sh/). For this lesson, we'll use Hardhat to establish a local development environment and transfer the contracts developed in earlier lessons to this setup.
+
+We'll follow the [Hardhat quick start guide](https://hardhat.org/hardhat-runner/docs/getting-started) and use the following commands to quickly initialize a project:
+
+```bash
+mkdir demo-contract
+cd demo-contract
+npx hardhat@2.22.3 init
+```
+
+Similar to how we initialized a Next.js project in [Chapter 1](../01_QuickStart/readme.md), `npx` is a command-line tool included with Node.js. The command above automatically downloads the [Hardhat npm package](https://www.npmjs.com/package/hardhat) and runs the `init` command. We specify version `2.22.3` to ensure compatibility with this course's setup, but you can omit the version number to use the latest version available.
+
+We select the third option, "Typescript + viem," to ensure consistency with the technical stack used in previous lessons. (Note: "viem" is a tool for Ethereum development, similar to ethers.js, used for interacting with the blockchain.)
+
+![hardhat](./img/hardhat.png)
+
+After the setup, your project directory will look like this:
+
+![initfiles](./img/initfiles.png)
+
+Here's what each file and folder is for:
+
+- `contracts`: Contains Solidity contract code.
+- `test`: Contains test scripts for your contracts.
+- `ignition`: Houses deployment scripts for contracts, including deployment parameters.
+- `hardhat.config.ts`: The configuration file for Hardhat.
+
+## Local Development and Debugging
+
+When you initialize the project, dependencies are installed automatically. If not, you can install them manually by running `npm i`. Once the dependencies are in place, compile the contracts using:
+
+```bash
+npx hardhat compile
+```
+
+To run the test cases, use the following command:
+
+```bash
+npx hardhat test
+```
+
+To start a local test network for debugging, execute:
+
+```bash
+npx hardhat node
+```
+
+Once the local node is running, it will automatically allocate a set of accounts for testing purposes. You can deploy your contracts to this local node with:
+
+```bash
+npx hardhat ignition deploy ./ignition/modules/Lock.ts --network localhost
+```
+
+Upon successful deployment, you can check the transaction details in the logs of your local test network. These logs are typically visible in the terminal where Hardhat node is running.
+
+![node](./img/localnode.png)
+
+With the local environment set up, we can proceed to deploy our previously developed NFT for debugging within this environment.
+
+## Migrating Contracts
+
+Transfer the contract code from previous lessons, specifically [MyToken.sol](../07_ContractDev/MyToken.sol), into the `contracts` directory, and remove the example code `Lock.sol`.
+
+Our contract depends on `@openzeppelin/contracts`, so we need to install this dependency by running:
+
+```bash
+npm install @openzeppelin/contracts
+```
+
+This ensures all necessary libraries are available for your contract. By following these steps, you'll have a fully operational local development environment ready for testing and refining your smart contracts.
+
+```bash
+npm install @openzeppelin/contracts --save
+```
+
+When creating the test file `test/MyToken.ts`, we use `test/Lock.ts` as a reference. The content of `test/MyToken.ts` is as follows:
+
+```ts
+import { loadFixture } from "@nomicfoundation/hardhat-toolbox-viem/network-helpers";
+import { expect } from "chai";
+import hre from "hardhat";
+
+describe("MyToken", function () {
+ async function deployFixture() {
+ const token = await hre.viem.deployContract("MyToken");
+ return {
+ token,
+ };
+ }
+
+ describe("ERC721", function () {
+ describe("name", function () {
+ it("Get NFT name", async function () {
+ const { token } = await loadFixture(deployFixture);
+ expect(await token.read.name()).to.equal("MyToken");
+ });
+ });
+ });
+});
+```
+
+This test case retrieves the name of the NFT by invoking the `name` method from the contract. This method is part of the ERC721 standard for NFT contracts.
+
+Next, we add the file `ignition/modules/MyToken.ts` for deployment, with the following content:
+
+```ts
+import { buildModule } from "@nomicfoundation/hardhat-ignition/modules";
+
+const MyTokenModule = buildModule("MyTokenModule", (m) => {
+ const lock = m.contract("MyToken");
+
+ return { lock };
+});
+
+export default MyTokenModule;
+```
+
+To test the contract, run `npx hardhat test`. This command will automatically compile your contract.
+
+Next, run the following command to deploy the contract:
+
+```bash
+npx hardhat ignition deploy ./ignition/modules/MyToken.ts --network localhost
+```
+
+Once the deployment is successful, we will try to call the contract from our earlier NFT project. To proceed, follow the instructions in the previous lesson to set up the front-end project using Vite.
+
+To add a `Hardhat` network in your code:
+
+```diff
+- import { mainnet, sepolia, polygon } from "wagmi/chains";
++ import { mainnet, sepolia, polygon, hardhat } from "wagmi/chains";
+import {
+ WagmiWeb3ConfigProvider,
+ MetaMask,
+ Sepolia,
+ Polygon,
++ Hardhat,
+ WalletConnect,
+} from "@ant-design/web3-wagmi";
+
+// ...
+
+const config = createConfig({
+- chains: [mainnet, sepolia, polygon],
++ chains: [mainnet, sepolia, polygon, hardhat],
+ transports: {
+ [mainnet.id]: http(),
+ [sepolia.id]: http(),
+ [polygon.id]: http(),
++ [hardhat.id]: http("http://127.0.0.1:8545/"),
+ },
+ connectors: [
+ injected({
+ target: "metaMask",
+ }),
+ walletConnect({
+ projectId: "c07c0051c2055890eade3556618e38a6",
+ showQrModal: false,
+ }),
+ ],
+});
+
+const contractInfo = [
+ {
+ id: 1,
+ name: "Ethereum",
+ contractAddress: "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512",
+ },
+ {
+ id: 5,
+ name: "Sepolia",
+ contractAddress: "0x418325c3979b7f8a17678ec2463a74355bdbe72c",
+ },
+ {
+ id: 137,
+ name: "Polygon",
+ contractAddress: "0x418325c3979b7f8a17678ec2463a74355bdbe72c",
+ },
++ {
++ id: hardhat.id,
++ name: "Hardhat",
++ contractAddress: "0x5FbDB2315678afecb367f032d93F642f64180aa3", // 这里需要替换为你本地部署后获得的地址
++ },
+];
+
+// ...
+
+export default function Web3() {
+ return (
+
+
+
+
+
+
+
+
+ );
+}
+```
+To add a network in MetaMask:
+
+![addnetwork](./img/addnetwork.png)
+
+You can import a test account into MetaMask using the private key shown in the console when you start with `npx hardhat node`. This will allow you to test with an account that has a balance of 10,000 ETH.
+
+Next, open the front-end page you've launched locally at [http://localhost:3000/web3](http://localhost:3000/web3). Switch the network to Hardhat and proceed to mint NFTs.
+
+![minttest](./img/minttest.png)
+
+Please note that if you restart your local Hardhat test network, you might encounter an error in MetaMask when connecting to the local RPC. An example of such an error is: `Received invalid block tag 1. Latest block number is 0`. To resolve this, go to the advanced settings in your MetaMask account and select 'Clear activity and nonce data.' If you're unsure where to find this option, it is typically located under the settings menu in MetaMask.
diff --git a/languages/en/15_WagmiCli/readme.md b/languages/en/15_WagmiCli/readme.md
new file mode 100644
index 0000000..ef27553
--- /dev/null
+++ b/languages/en/15_WagmiCli/readme.md
@@ -0,0 +1,130 @@
+In the previous session, we set up a contract project on your local machine using Hardhat. As we progress in our DEX practical courses, we will continue to build increasingly complex contracts based on this foundation. With this complexity, the ABI will also expand. At this stage, a tool to assist in contract debugging becomes essential. In this session, we'll introduce you to using Wagmi CLI to automatically generate debugging code for contracts and integrate it into your project, preparing you for the upcoming DEX practical courses.
+
+---
+
+## Setting Up Wagmi CLI
+
+Follow the steps below to get started, or consult the [official Wagmi CLI documentation](https://wagmi.sh/cli/getting-started) for more details.
+
+Install dependencies:
+
+```bash
+npm install --save-dev @wagmi/cli
+```
+
+Update the `demo/wagmi.config.ts` configuration file to include the [hardhat](https://wagmi.sh/cli/api/plugins/hardhat) and [react](https://wagmi.sh/cli/api/plugins/react) plugins.
+
+Here is the complete configuration:
+
+```ts
+import { defineConfig } from "@wagmi/cli";
+import { hardhat } from "@wagmi/cli/plugins";
+import { react } from "@wagmi/cli/plugins";
+
+export default defineConfig({
+ out: "utils/contracts.ts",
+ plugins: [
+ hardhat({
+ project: "../demo-contract",
+ }),
+ react(),
+ ],
+});
+```
+
+To generate code, use the following command:
+
+```bash
+npx wagmi generate
+```
+
+Once the execution is complete, you'll find the generated code in `utils/contracts.ts`. After that, you can easily use the React Hooks exported from `utils/contracts.ts` to interact with contracts in your project.
+
+## Implementing Wagmi CLI Generated Code
+
+Next, update the code in `demo/pages/web3.tsx` by replacing the `useReadContract` and `useWriteContract` functions with the newly generated code from the Wagmi CLI.
+
+```diff
+// ...
+-import {
+- createConfig,
+- http,
+- useReadContract,
+- useWriteContract,
+- useWatchContractEvent,
+-} from "wagmi";
++import { createConfig, http, useWatchContractEvent } from "wagmi";
+ import { injected, walletConnect } from "wagmi/connectors";
++import {
++ useReadMyTokenBalanceOf,
++ useWriteMyTokenMint,
++} from "@/utils/contracts";
+
+// ...
+```
+
+Update the logic for calling the `balanceOf` Method:
+
+```diff
+
+// ...
+
+- const result = useReadContract({
+- abi: [
+- {
+- type: "function",
+- name: "balanceOf",
+- stateMutability: "view",
+- inputs: [{ name: "account", type: "address" }],
+- outputs: [{ type: "uint256" }],
+- },
+- ],
++ const result = useReadMyTokenBalanceOf({
+ address: contractInfo.find((item) => item.id === chain?.id)
+ ?.contractAddress as `0x${string}`,
+- functionName: "balanceOf",
+ args: [account?.address as `0x${string}`],
+ });
+
+// ...
+
+```
+Update the logic for calling the `mint` method:
+
+```diff
+- const { writeContract } = useWriteContract();
++ const { writeContract: mintNFT } = useWriteMyTokenMint();
+
+// ...
+
+ const CallTest = () => {
+ {result.data?.toString()}
+