经济下行阶段,一个 37 岁失业程序员的独白(经历/经验分享)
警告:区块链投资高风险,需要谨慎,谨慎,再谨慎!
实战案例四:DeFi 去中心化交易所
现实情况是期望代币可以在去中心化的交易场所中交换,这篇文章就是从一个简单案例来说明交换,流动性该如何实现。 我们需要先梳理一下,期望这个应用具备哪些功能:只用一个代币对建立交易场所交易收取 1% 的费用用户可以为 UseWeb3Token 添加或删除流动性为用户提供 LP 代币说明:实现会比这个例子复杂的多// SPDX-License-Identifier: SEE LICENSE IN LICENSE pragma solidity ^0.8.4; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; contract UseWeb3Exchange is ERC20 { address public useweb3TokenAddress; constructor(address useweb3TokenContract) ERC20("LP Token", "LP") { useweb3TokenAddress = useweb3TokenContract; } function getReserve() publ...
初识 Solidity 和 OpenZeppelin
Solidity 是一种面向对象的高级静态语言,用于实现智能合约,运行于 以太坊虚拟机,它支持继承,库和自定义类型等。pragma solidity ^0.8.0; contract HelloWorld { } Solidity 有三种类型的变量,熟悉它是因为变量的范围是由它们声明的位置所决定的:Local在函数内部声明且不存储在区块链上State存储在区块链上Global提供区块链相关的信息,它在运行时由以太坊虚拟机注入包括交易发送者,区块时间戳,区块哈希等全局变量语法知识,请阅读:https://docs.soliditylang.org/en/v0.8.9/index.html初识 OpenZeppelin说明:OpenZeppelin 是一家以太坊安全公司,其为流行的智能合约标准开发了一组合约,这些合约经过了大量的测试和安全审查,所以如果我们需要实现这些标准合约时,应该尝试找到 OpenZeppelin 提供的合约,而不是重头开始重写整个标准。https://github.com/OpenZeppelin/openzeppelin-contracts在 useweb3 ...
Dev
经济下行阶段,一个 37 岁失业程序员的独白(经历/经验分享)
警告:区块链投资高风险,需要谨慎,谨慎,再谨慎!
实战案例四:DeFi 去中心化交易所
现实情况是期望代币可以在去中心化的交易场所中交换,这篇文章就是从一个简单案例来说明交换,流动性该如何实现。 我们需要先梳理一下,期望这个应用具备哪些功能:只用一个代币对建立交易场所交易收取 1% 的费用用户可以为 UseWeb3Token 添加或删除流动性为用户提供 LP 代币说明:实现会比这个例子复杂的多// SPDX-License-Identifier: SEE LICENSE IN LICENSE pragma solidity ^0.8.4; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; contract UseWeb3Exchange is ERC20 { address public useweb3TokenAddress; constructor(address useweb3TokenContract) ERC20("LP Token", "LP") { useweb3TokenAddress = useweb3TokenContract; } function getReserve() publ...
初识 Solidity 和 OpenZeppelin
Solidity 是一种面向对象的高级静态语言,用于实现智能合约,运行于 以太坊虚拟机,它支持继承,库和自定义类型等。pragma solidity ^0.8.0; contract HelloWorld { } Solidity 有三种类型的变量,熟悉它是因为变量的范围是由它们声明的位置所决定的:Local在函数内部声明且不存储在区块链上State存储在区块链上Global提供区块链相关的信息,它在运行时由以太坊虚拟机注入包括交易发送者,区块时间戳,区块哈希等全局变量语法知识,请阅读:https://docs.soliditylang.org/en/v0.8.9/index.html初识 OpenZeppelin说明:OpenZeppelin 是一家以太坊安全公司,其为流行的智能合约标准开发了一组合约,这些合约经过了大量的测试和安全审查,所以如果我们需要实现这些标准合约时,应该尝试找到 OpenZeppelin 提供的合约,而不是重头开始重写整个标准。https://github.com/OpenZeppelin/openzeppelin-contracts在 useweb3 ...
Dev

Subscribe to icepy

Subscribe to icepy
Share Dialog
Share Dialog
<100 subscribers
<100 subscribers
这是一个关于如何配置 hardhat,创建前端工程,编写智能合约,部署智能合约,使用 ethers.js 与之交互的用例。
说明:必须先安装 nodejs,接下来我们所有的编程都会依赖和使用到它。
在本地终端上使用命令创建一个空目录,并且快速生成 package.json 文件,package.json 是 nodejs 用于管理项目和模块的文件:
$ mkdir useweb3
$ cd useweb3
$ npm init -y
安装 Hardhat
$ npm install hardhat --save-dev
让我们对 package.json 做一些适当的修改,scripts 字段上:
"scripts": {
"init": "hardhat",
"node": "hardhat node",
"test": "hardhat test",
"compile": "hardhat compile"
}
在终端处运行 npm run init,且选择 create a basic sample project 来初始化项目。

现在我们得到了一个有如下目录结构的项目:
contracts 编写合同的目录
scripts 编写部署脚本的目录
test 编写测试脚本的目录
hardhat.config.js hardhat 的配置文件
接下来,我们可以利用 create-react-app 来创建前端工程
说明:你也可以选择 Next.js
$ npx create-react-app client
最后我们需要完善 hardhat 的配置和部署脚本,在根目录中创建一个.env文件,并安装 dotenv:
$ npm install dotenv --save-dev
⚠️⚠️⚠️ .env 文件由于存放了私钥,千万不要提交!!!
我们主要将合约部署在本地和 rinkeby 测试网络,所以 .env 文件中,只配置这两项。
ALCHEMY_API_KEY_URL=""
LOCALHOST_URL=""
PRIVATE_KEY=""
LOCALHOST_PRIVATE_KEY=""
在 https://www.alchemy.com/ 上创建一个 rinkeby 项目:

点击 VIEW KEY 来获取 URL 粘贴到 .env 文件的 ALCHEMY_API_KEY_URL 字段

私钥可在 metamask 上获取,粘贴到 .env 文件的 PRIVATE_KEY 字段

接下来我们需要配置本地网络,在终端上使用 npm run node 启动节点,hardhat 会自动创建 20 个地址,每一个地址上都有 10000 个 ETH,随便获取其中一个的私钥:
$ npm run node
粘贴到 .env 文件的 LOCALHOST_PRIVATE_KEY 字段
本地网络 http://localhost:8545 粘贴到 .env 文件 的 LOCALHOST_URL 字段

链 ID 修改为 31337
现在我们需要对 hardhat.config.js 做一些修改:
require("@nomiclabs/hardhat-waffle");
require("dotenv").config({path: '.env'});
// This is a sample Hardhat task. To learn how to create your own go to
// https://hardhat.org/guides/create-task.html
task("accounts", "Prints the list of accounts", async (taskArgs, hre) => {
const accounts = await hre.ethers.getSigners();
for (const account of accounts) {
console.log(account.address);
}
});
// You need to export an object to set up your config
// Go to https://hardhat.org/config/ to learn more
const ALCHEMY_API_KEY_URL = process.env.ALCHEMY_API_KEY_URL;
const LOCALHOST_URL = process.env.LOCALHOST_URL;
const PRIVATE_KEY = process.env.PRIVATE_KEY;
const LOCALHOST_PRIVATE_KEY = process.env.LOCALHOST_PRIVATE_KEY;
/**
* @type import('hardhat/config').HardhatUserConfig
*/
module.exports = {
solidity: "0.8.4",
networks: {
rinkeby: {
url: ALCHEMY_API_KEY_URL,
accounts: [PRIVATE_KEY],
},
localhost: {
url: LOCALHOST_URL,
accounts: [LOCALHOST_PRIVATE_KEY]
}
}
};
最后在 scripts 目录中创建 deploy.js 文件,并修改 package.json 在 scripts 中增加两个配置:
{
"rinkeby": "hardhat run scripts/deploy.js --network rinkeby",
"localhost": "hardhat run scripts/deploy.js --network localhost"
}
deploy.js 因为还是一个空文件,所以并不急于测试。
在 contracts 目录中创建一个 Hello 合约,在初始化时将 Hello useweb3 传递给它,并且实现一个函数可返回这个字符串 !
hardhat/console.sol 是 hardhat 提供可以打印日志的模块
// SPDX-License-Identifier: SEE LICENSE IN LICENSE
pragma solidity ^0.8.4;
import "hardhat/console.sol";
contract Hello {
string text;
constructor(string memory _text) {
console.log("%s", _text);
text = _text;
}
function say() public view returns (string memory) {
return text;
}
}
现在我们需要在deploy.js 文件中编写我们的部署脚本:
const hre = require("hardhat");
async function main(){
const Hello = await hre.ethers.getContractFactory("Hello");
const hello = await Hello.deploy("hello useweb3");
hello.deployed();
console.log("Hello Contract Address: ", hello.address);
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
现在,我们如果想将合约部署到本地网络可运行:
$ npm run localhost

如果我们想将合约部署到rinkeby测试网络可运行:
$ npm run rinkeby
通过在终端上输出的合约地址,在 https://rinkeby.etherscan.io/ 处查看。
最后,我们需要在 test 目录创建一个 Hello.js 用于测试
const { expect } = require("chai");
const { ethers } = require("hardhat");
describe("Hello", function () {
it("测试 say 函数的返回值", async function () {
const value = "hello useweb3";
const Hello = await ethers.getContractFactory("Hello");
const hello = await Hello.deploy(value);
await hello.deployed();
expect(await hello.say()).to.equal(value);
});
});

说明:后续的开发与测试都在本地网络上进行
我们需要将合约进行一些修改,将状态存储到区块链上,并且通过钱包的交互来改变状态。
// SPDX-License-Identifier: SEE LICENSE IN LICENSE
pragma solidity ^0.8.4;
import "hardhat/console.sol";
contract Hello {
string text;
uint256 count;
constructor(string memory _text) {
console.log("%s", _text);
text = _text;
count = 0;
}
function addition() public{
count += 1;
}
function getCount() public view returns (uint256) {
return count;
}
function say() public view returns (string memory) {
return text;
}
}
重新运行 npm run localhost 来部署合约。
在进入之前又需要普及一个知识点:ABI(Application Binary Interface)和 ethers.js。
ABI是合约接口的说明,内容包括合约的接口列表、接口名称、参数名称、参数类型、返回类型等,这些信息以 JSON 格式保存,可以在 solidity 文件编译时由合约编译器生成。
ethers.js 是目前前端用来与合约进行交互的基础库,提供只读(Provider)和可读可写(Signer)的能力
在 client/src 目录中创建一个 abi 目录并创建一个 Hello.json 文件,现在我们需要将之前部署好的合约生成的对应 JSON 复制过来。
说明:由于只支持 metamask 钱包进行交互,因此直接依赖 Web3Provider
import React, { useEffect, useState } from 'react';
import { ethers } from 'ethers';
import HelloJSON from './abi/Hello.json';
import './Hello.css'
const HELLO_CONTRACT_ADDRESS = "0xB581C9264f59BF0289fA76D61B2D0746dCE3C30D";
const abi = HelloJSON.abi;
const getWeb3Provider = () => {
let web3Provider = window.web3Provider;
if (!web3Provider){
web3Provider = new ethers.providers.Web3Provider(window.ethereum);
window.web3Provider = web3Provider;
}
return web3Provider;
}
export default function Hello(){
const [address, setAddress] = useState('');
const [count, setCount] = useState(0);
useEffect(() => {
if (!address){
const web3Provider = getWeb3Provider();
const selectedAddress = web3Provider.provider.selectedAddress;
if (!selectedAddress){
web3Provider.provider.request({ method: 'eth_requestAccounts' }).then((accounts) => {
const account = accounts[0];
setAddress(account);
});
} else {
setAddress(selectedAddress);
}
}
}, [address]);
const onGetCount = async () => {
const web3Provider = getWeb3Provider();
const signer = web3Provider.getSigner();
const helloContract = new ethers.Contract(HELLO_CONTRACT_ADDRESS, abi, signer);
const result = await helloContract.getCount();
setCount(result.toNumber());
}
const onAddition = async () => {
const web3Provider = getWeb3Provider();
const signer = web3Provider.getSigner();
const helloContract = new ethers.Contract(HELLO_CONTRACT_ADDRESS, abi, signer);
await helloContract.addition();
}
return (
<div className='helloContainer'>
<div>address: {address}</div>
<div className='helloButtons'>
<button onClick={onGetCount}>Update Count</button>
<button onClick={onAddition}>Addition</button>
</div>
<div>count: {count}</div>
</div>
);
}
点击 Addition 来修改区块链的状态

点击 Update Count 来更新 count 的值

这是一个关于如何配置 hardhat,创建前端工程,编写智能合约,部署智能合约,使用 ethers.js 与之交互的用例。
说明:必须先安装 nodejs,接下来我们所有的编程都会依赖和使用到它。
在本地终端上使用命令创建一个空目录,并且快速生成 package.json 文件,package.json 是 nodejs 用于管理项目和模块的文件:
$ mkdir useweb3
$ cd useweb3
$ npm init -y
安装 Hardhat
$ npm install hardhat --save-dev
让我们对 package.json 做一些适当的修改,scripts 字段上:
"scripts": {
"init": "hardhat",
"node": "hardhat node",
"test": "hardhat test",
"compile": "hardhat compile"
}
在终端处运行 npm run init,且选择 create a basic sample project 来初始化项目。

现在我们得到了一个有如下目录结构的项目:
contracts 编写合同的目录
scripts 编写部署脚本的目录
test 编写测试脚本的目录
hardhat.config.js hardhat 的配置文件
接下来,我们可以利用 create-react-app 来创建前端工程
说明:你也可以选择 Next.js
$ npx create-react-app client
最后我们需要完善 hardhat 的配置和部署脚本,在根目录中创建一个.env文件,并安装 dotenv:
$ npm install dotenv --save-dev
⚠️⚠️⚠️ .env 文件由于存放了私钥,千万不要提交!!!
我们主要将合约部署在本地和 rinkeby 测试网络,所以 .env 文件中,只配置这两项。
ALCHEMY_API_KEY_URL=""
LOCALHOST_URL=""
PRIVATE_KEY=""
LOCALHOST_PRIVATE_KEY=""
在 https://www.alchemy.com/ 上创建一个 rinkeby 项目:

点击 VIEW KEY 来获取 URL 粘贴到 .env 文件的 ALCHEMY_API_KEY_URL 字段

私钥可在 metamask 上获取,粘贴到 .env 文件的 PRIVATE_KEY 字段

接下来我们需要配置本地网络,在终端上使用 npm run node 启动节点,hardhat 会自动创建 20 个地址,每一个地址上都有 10000 个 ETH,随便获取其中一个的私钥:
$ npm run node
粘贴到 .env 文件的 LOCALHOST_PRIVATE_KEY 字段
本地网络 http://localhost:8545 粘贴到 .env 文件 的 LOCALHOST_URL 字段

链 ID 修改为 31337
现在我们需要对 hardhat.config.js 做一些修改:
require("@nomiclabs/hardhat-waffle");
require("dotenv").config({path: '.env'});
// This is a sample Hardhat task. To learn how to create your own go to
// https://hardhat.org/guides/create-task.html
task("accounts", "Prints the list of accounts", async (taskArgs, hre) => {
const accounts = await hre.ethers.getSigners();
for (const account of accounts) {
console.log(account.address);
}
});
// You need to export an object to set up your config
// Go to https://hardhat.org/config/ to learn more
const ALCHEMY_API_KEY_URL = process.env.ALCHEMY_API_KEY_URL;
const LOCALHOST_URL = process.env.LOCALHOST_URL;
const PRIVATE_KEY = process.env.PRIVATE_KEY;
const LOCALHOST_PRIVATE_KEY = process.env.LOCALHOST_PRIVATE_KEY;
/**
* @type import('hardhat/config').HardhatUserConfig
*/
module.exports = {
solidity: "0.8.4",
networks: {
rinkeby: {
url: ALCHEMY_API_KEY_URL,
accounts: [PRIVATE_KEY],
},
localhost: {
url: LOCALHOST_URL,
accounts: [LOCALHOST_PRIVATE_KEY]
}
}
};
最后在 scripts 目录中创建 deploy.js 文件,并修改 package.json 在 scripts 中增加两个配置:
{
"rinkeby": "hardhat run scripts/deploy.js --network rinkeby",
"localhost": "hardhat run scripts/deploy.js --network localhost"
}
deploy.js 因为还是一个空文件,所以并不急于测试。
在 contracts 目录中创建一个 Hello 合约,在初始化时将 Hello useweb3 传递给它,并且实现一个函数可返回这个字符串 !
hardhat/console.sol 是 hardhat 提供可以打印日志的模块
// SPDX-License-Identifier: SEE LICENSE IN LICENSE
pragma solidity ^0.8.4;
import "hardhat/console.sol";
contract Hello {
string text;
constructor(string memory _text) {
console.log("%s", _text);
text = _text;
}
function say() public view returns (string memory) {
return text;
}
}
现在我们需要在deploy.js 文件中编写我们的部署脚本:
const hre = require("hardhat");
async function main(){
const Hello = await hre.ethers.getContractFactory("Hello");
const hello = await Hello.deploy("hello useweb3");
hello.deployed();
console.log("Hello Contract Address: ", hello.address);
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
现在,我们如果想将合约部署到本地网络可运行:
$ npm run localhost

如果我们想将合约部署到rinkeby测试网络可运行:
$ npm run rinkeby
通过在终端上输出的合约地址,在 https://rinkeby.etherscan.io/ 处查看。
最后,我们需要在 test 目录创建一个 Hello.js 用于测试
const { expect } = require("chai");
const { ethers } = require("hardhat");
describe("Hello", function () {
it("测试 say 函数的返回值", async function () {
const value = "hello useweb3";
const Hello = await ethers.getContractFactory("Hello");
const hello = await Hello.deploy(value);
await hello.deployed();
expect(await hello.say()).to.equal(value);
});
});

说明:后续的开发与测试都在本地网络上进行
我们需要将合约进行一些修改,将状态存储到区块链上,并且通过钱包的交互来改变状态。
// SPDX-License-Identifier: SEE LICENSE IN LICENSE
pragma solidity ^0.8.4;
import "hardhat/console.sol";
contract Hello {
string text;
uint256 count;
constructor(string memory _text) {
console.log("%s", _text);
text = _text;
count = 0;
}
function addition() public{
count += 1;
}
function getCount() public view returns (uint256) {
return count;
}
function say() public view returns (string memory) {
return text;
}
}
重新运行 npm run localhost 来部署合约。
在进入之前又需要普及一个知识点:ABI(Application Binary Interface)和 ethers.js。
ABI是合约接口的说明,内容包括合约的接口列表、接口名称、参数名称、参数类型、返回类型等,这些信息以 JSON 格式保存,可以在 solidity 文件编译时由合约编译器生成。
ethers.js 是目前前端用来与合约进行交互的基础库,提供只读(Provider)和可读可写(Signer)的能力
在 client/src 目录中创建一个 abi 目录并创建一个 Hello.json 文件,现在我们需要将之前部署好的合约生成的对应 JSON 复制过来。
说明:由于只支持 metamask 钱包进行交互,因此直接依赖 Web3Provider
import React, { useEffect, useState } from 'react';
import { ethers } from 'ethers';
import HelloJSON from './abi/Hello.json';
import './Hello.css'
const HELLO_CONTRACT_ADDRESS = "0xB581C9264f59BF0289fA76D61B2D0746dCE3C30D";
const abi = HelloJSON.abi;
const getWeb3Provider = () => {
let web3Provider = window.web3Provider;
if (!web3Provider){
web3Provider = new ethers.providers.Web3Provider(window.ethereum);
window.web3Provider = web3Provider;
}
return web3Provider;
}
export default function Hello(){
const [address, setAddress] = useState('');
const [count, setCount] = useState(0);
useEffect(() => {
if (!address){
const web3Provider = getWeb3Provider();
const selectedAddress = web3Provider.provider.selectedAddress;
if (!selectedAddress){
web3Provider.provider.request({ method: 'eth_requestAccounts' }).then((accounts) => {
const account = accounts[0];
setAddress(account);
});
} else {
setAddress(selectedAddress);
}
}
}, [address]);
const onGetCount = async () => {
const web3Provider = getWeb3Provider();
const signer = web3Provider.getSigner();
const helloContract = new ethers.Contract(HELLO_CONTRACT_ADDRESS, abi, signer);
const result = await helloContract.getCount();
setCount(result.toNumber());
}
const onAddition = async () => {
const web3Provider = getWeb3Provider();
const signer = web3Provider.getSigner();
const helloContract = new ethers.Contract(HELLO_CONTRACT_ADDRESS, abi, signer);
await helloContract.addition();
}
return (
<div className='helloContainer'>
<div>address: {address}</div>
<div className='helloButtons'>
<button onClick={onGetCount}>Update Count</button>
<button onClick={onAddition}>Addition</button>
</div>
<div>count: {count}</div>
</div>
);
}
点击 Addition 来修改区块链的状态

点击 Update Count 来更新 count 的值

No activity yet