Hardhat 项目创建
安装hardhat
首先创建一个项目文件夹
然后使用yarn init
命令初始化项目文件夹
接下来就可以直接使用yarn这个包管理工具安装也可以用npm安装
创建项目
使用
yarn hardhat
出现如下界面
选择创建项目然后一路y到底即可
项目开发
只要介绍一下各个文件夹的作用,我这里会议fundme的智能合约为例
项目环境配置
环境变量
首先的重重之重就是环境变量,在开发时会需要用到一些私密的接口以及密钥等不能向外界传播的数据,但是使用频次也比较高,于是我们就可以使用环境变量来操作这些数据。
要在项目中使用这个需要安装一个包
yarn add --dev dotenv
然后再项目中创建一个.env文件夹用于存放环境变量
hardhat-config.js
然后就是项目中至关重要的一个配置文件,如果没有配置好,那肯定是爆红少不了。
测试用的网络配置,插件的配置,以及测试使用的账户配置都在里面
具体的代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
| require("@nomicfoundation/hardhat-toolbox");
require("hardhat-deploy");
require("dotenv").config();
require("@nomiclabs/hardhat-ethers");
require("@nomicfoundation/hardhat-verify");
require("hardhat-gas-reporter");
const PRIVATE_KEY = process.env.SEPOLIA_PRIVATE_KEY;
const SEPOLIA_RPC_URL = process.env.SEPOLIA_RPC_URL;
const ETHERSCAN_API_KEY = process.env.ETHERSCAN_API_KEY;
const COINMARKET_API_KEY = process.env.COINMARKET_API_KEY;
module.exports = {
solidity: {
compilers: [{ version: "0.8.8" }, { version: "0.6.6" }],
},
defaultNetwork: "hardhat",
networks: {
sepolia: {
url: SEPOLIA_RPC_URL,
accounts: [PRIVATE_KEY],
chainId: 11155111,
blockConfirmations: 6,
},
localhost: {
url: "http://127.0.0.1:8545/",
chainId: 31337,
},
},
etherscan: {
apiKey: ETHERSCAN_API_KEY,
},
gasReporter: {
enabled: false,
outputFile: "gas-report.txt",
noColors: true,
currency: "USD",
},
namedAccounts: {
deployer: {
default: 0,
},
user: {
default: 1,
},
},
};
|
mocking
由于在使用本地网络时无法像在网络上一样得知加密货币与真实货币之间的汇率。
我们需要使用solidity搭建一个会在本地返回汇率的工具;
这个可以在github上找到这个
sol代码
可以将它直接复制到本地编译也可以通过import把它导入到本地
至于这个solidity的版本我并没有去尝试更多版本,毕竟这中版本引起的报错真的太难解决了。把这个合约代码写好之后就需要将它部署。因此还需要写一个部署脚本;
在部署我们的fundme合约之前部署它即可。
由于他们是按顺序执行的脚本所以我们将脚本编号;
部署的代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| const { network } = require("hardhat");
const {
developmentChains,
DECIMALS,
INITIAL_ANSWER,
} = require("../helper-hardhat-config");
module.exports = async ({ getNamedAccounts, deployments }) => {
const { deploy, log } = deployments;
const { deployer } = await getNamedAccounts();
const chainId = network.config.chainId;
if (developmentChains.includes(network.name)) {
log("Local network detected! Deploying mocks..");
await deploy("MockV3Aggregator", {
contract: "MockV3Aggregator",
from: deployer,
log: true,
args: [DECIMALS, INITIAL_ANSWER],
});
log("Mocks deployed!");
console.log("-------------------------------------------");
}
};
module.exports.tags = ["all", "mocks"];
|
讲到这个mocks的部署代码就不得不讲一下helper-hardhat-config.js中的网络设置了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| const networkConfig = {
11155111: {
name: "sepolia",
ethUsdPriceFeed: "0x694AA1769357215DE4FAC081bf1f309aDC325306",
},
};
const developmentChains = ["hardhat", "localhost"];
const DECIMALS = 8;
const INITIAL_ANSWER = 200000000000;
module.exports = {
networkConfig,
developmentChains,
DECIMALS,
INITIAL_ANSWER,
};
|
这个配置文件可以根据运行时包含的网络选项,从而获取相应的FeedPrice,后面module.exports中的变量可以在导入了这个包中的其他文件中使用
合约代码及脚本的编写
合约
Fundme
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
|
pragma solidity ^0.8.8;
import "./PriceConvert.sol";
import "hardhat/console.sol";
error Fundme__NotOwner();
contract Fundme {
using PriceConvert for uint256;
uint256 private constant MINIMUMCNY = 50 * 10 ** 18;
address[] private s_funders;
mapping(address => uint) private s_addresstoAmountfunded;
address private immutable i_owner;
AggregatorV3Interface private s_priceFeed;
constructor(address priceFeedAddress) {
i_owner = msg.sender;
s_priceFeed = AggregatorV3Interface(priceFeedAddress);
}
function fund() public payable {
require(
msg.value.GetConversionRate(s_priceFeed) >= MINIMUMCNY,
"You need to speed more ETH"
);
s_funders.push(msg.sender);
s_addresstoAmountfunded[msg.sender] = msg.value;
}
function Withdraw() public onlyOwner {
for (
uint256 funderIndex = 0;
funderIndex < s_funders.length;
funderIndex++
) {
address funder = s_funders[funderIndex];
s_addresstoAmountfunded[funder] = 0;
}
s_funders = new address[](0);
(bool callSuccess, ) = payable(msg.sender).call{
value: address(this).balance
}("");
require(callSuccess, "call failed");
}
function cheaperWithdraw() public onlyOwner {
address[] memory funders = s_funders;
for (
uint256 funderIndex = 0;
funderIndex < funders.length;
funderIndex++
) {
address funder = funders[funderIndex];
s_addresstoAmountfunded[funder] = 0;
}
s_funders = new address[](0);
(bool callSuccess, ) = payable(msg.sender).call{
value: address(this).balance
}("");
require(callSuccess, "call failed");
}
modifier onlyOwner() {
if (msg.sender != i_owner) {
revert Fundme__NotOwner();
}
_;
}
function getOwner() public view returns (address) {
return i_owner;
}
function getAddressToAmountFunded(
address fundingAddress
) public view returns (uint256) {
return s_addresstoAmountfunded[fundingAddress];
}
function getVersion() public view returns (uint256) {
return s_priceFeed.version();
}
function getFunder(uint256 index) public view returns (address) {
return s_funders[index];
}
function getPriceFeed() public view returns (AggregatorV3Interface) {
return s_priceFeed;
}
}
|
PriceFeed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| //SPXD-License-Identifier:MIT
pragma solidity ^0.8.8;
import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
library PriceConvert {
function GetPrice(
AggregatorV3Interface priceFeed
) internal view returns (uint256) {
(, int price, , , ) = priceFeed.latestRoundData();
return uint256(price * 1e10);
}
// function Getversion() internal view returns(uint256){
// AggregatorV3Interface priceFeed=AggregatorV3Interface(0x694AA1769357215DE4FAC081bf1f309aDC325306);
// return priceFeed.version();
// }
function GetConversionRate(
uint256 ethAmount,
AggregatorV3Interface priceFeed
) internal view returns (uint256) {
uint256 ethprice = GetPrice(priceFeed);
uint256 ethAmountInCn = (ethprice * ethAmount) / 1e18;
return ethAmountInCn;
}
}
|
脚本
mocks部署脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| const { network } = require("hardhat");
const {
developmentChains,
DECIMALS,
INITIAL_ANSWER,
} = require("../helper-hardhat-config");
module.exports = async ({ getNamedAccounts, deployments }) => {
const { deploy, log } = deployments;
const { deployer } = await getNamedAccounts();
const chainId = network.config.chainId;
if (developmentChains.includes(network.name)) {
log("Local network detected! Deploying mocks..");
await deploy("MockV3Aggregator", {
contract: "MockV3Aggregator",
from: deployer,
log: true,
args: [DECIMALS, INITIAL_ANSWER],
});
log("Mocks deployed!");
console.log("-------------------------------------------");
}
};
module.exports.tags = ["all", "mocks"];
|
Fundme部署脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
|
const {
networkConfig,
developmentChains,
} = require("../helper-hardhat-config");
const { network } = require("hardhat");
const { verify } = require("../utils/verify");
module.exports = async ({ getNamedAccounts, deployments }) => {
const { deploy, log } = deployments;
const { deployer } = await getNamedAccounts();
const chainId = network.config.chainId;
let ethUsdPriceFeedAddress;
if (developmentChains.includes(network.name)) {
const ethUsdAggregator = await deployments.get("MockV3Aggregator");
ethUsdPriceFeedAddress = ethUsdAggregator.address;
} else {
ethUsdPriceFeedAddress = networkConfig[chainId]["ethUsdPriceFeed"];
}
const args = [ethUsdPriceFeedAddress];
const fundMe = await deploy("Fundme", {
from: deployer,
args: args,
log: true,
waitConfirmations: network.config.blockConfirmations || 1,
});
console.log("fundme deployed");
if (
!developmentChains.includes(network.name) &&
process.env.ETHERSCAN_API_KEY
) {
await verify(fundMe.address, args);
}
log("----------------------------------------------------");
};
module.exports.tags = ["all", "fundme"];
|
在不部署之后需要对合约进行验证,即verify,但是在verify之前需要等待6各区块我们的部署的这个交易才能确定上链所以这里调用了hardhat.config中的blockConfirmations。
verify
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| const { run } = require("hardhat");
async function verify(contractAddress, args) {
console.log("Verifying contract...");
try {
await run("verify:verify", {
address: contractAddress,
constructorArguments: args,
});
} catch (e) {
if (e.message.toLowerCase().includes("already verified")) {
console.log("Already Verified!");
} else {
console.log(e);
}
}
}
module.exports = { verify };
|
测试
单元测试
这个测试是在本地运行的,花了很多时间用来修改因为版本问题出现的报错,注意事项写在代码的注释里了,不再赘述
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425
| const { deployments, ethers, getNamedAccounts } = require("hardhat");
const { assert, expect } = require("chai");
const {
experimentalAddHardhatNetworkMessageTraceHook,
} = require("hardhat/config");
const { developmentChains } = require("../../helper-hardhat-config.js");
!developmentChains.includes(network.name)
? describe.skip
: describe("fundme", function () {
let fundMe;
let deployer;
let mockV3Aggregator;
let sendValue = ethers.parseEther("1");
beforeEach(async function () {
deployer = (await getNamedAccounts()).deployer;
await deployments.fixture(["all"]);
fundMe = await ethers.getContract("Fundme", deployer);
mockV3Aggregator = await ethers.getContract(
"MockV3Aggregator",
deployer
);
});
describe("constractor", function () {
it("sets the aggregator addresses correctly", async function () {
const response = await fundMe.getPriceFeed();
assert.equal(response, mockV3Aggregator.target);
});
});
describe("fund", function () {
it("Fails if you don't send enough ETH", async function () {
await expect(fundMe.fund()).to.be.revertedWith(
"You need to speed more ETH"
);
});
it("update the amount funded data structure", async function () {
await fundMe.fund({ value: sendValue });
const response = await fundMe.getAddressToAmountFunded(deployer);
assert.equal(response.toString(), sendValue.toString());
});
it("Adds s_funder to array of getFunder", async function () {
await fundMe.fund({ value: sendValue });
const s_funder = await fundMe.getFunder(0);
assert.equal(s_funder, deployer);
});
});
describe("withdraw", function () {
beforeEach(async function () {
await fundMe.fund({ value: sendValue });
});
it("Withdraw ETH from a single founder", async function () {
const startingFundMeBalance = await ethers.provider.getBalance(
fundMe.getAddress()
);
const startingDeployBalance = await ethers.provider.getBalance(
deployer
);
const transactionResponse = await fundMe.Withdraw();
const transactionReceipt = await transactionResponse.wait();
const { gasUsed, gasPrice } = transactionReceipt;
const gasCost = gasUsed * gasPrice;
const endingFundMeBalance = await ethers.provider.getBalance(
fundMe.getAddress()
);
const endingDeployerBalance = await ethers.provider.getBalance(
deployer
);
assert.equal(endingFundMeBalance, 0);
assert.equal(
(startingFundMeBalance + startingDeployBalance).toString(),
(endingDeployerBalance + gasCost).toString()
);
});
it("allows us to withdraw with multiple getFunder", async function () {
const accounts = await ethers.getSigners();
for (let i = 1; i < 6; i++) {
const fundMeConnectedContract = await fundMe.connect(accounts[i]);
await fundMeConnectedContract.fund({ value: sendValue });
}
const startingFundMeBalance = await ethers.provider.getBalance(
fundMe.target
);
const startingDeployerBalance = await ethers.provider.getBalance(
deployer
);
const transactionResponse = await fundMe.Withdraw();
const transactionReceipt = await transactionResponse.wait();
const { gasUsed, gasPrice } = transactionReceipt;
const gasCost = gasUsed * gasPrice;
const endingFundMeBalance = await ethers.provider.getBalance(
fundMe.getAddress()
);
const endingDeployerBalance = await ethers.provider.getBalance(
deployer
);
assert.equal(endingFundMeBalance, 0);
assert.equal(
(startingFundMeBalance + startingDeployerBalance).toString(),
(endingDeployerBalance + gasCost).toString()
);
await expect(fundMe.getFunder(0)).to.be.reverted;
for (i = 1; i < 6; i++) {
assert.equal(
await fundMe.getAddressToAmountFunded(accounts[i].address),
0
);
}
});
it("Only allows the owner to withdraw", async function () {
const accounts = await ethers.getSigners();
const attacker = accounts[1];
const attackerConnectedContract = await fundMe.connect(attacker);
await expect(
attackerConnectedContract.Withdraw()
).to.be.revertedWithCustomError(fundMe, "Fundme__NotOwner");
});
});
describe("cheaperwithdraw", function () {
beforeEach(async function () {
await fundMe.fund({ value: sendValue });
});
it("cheaperWithdraw ETH from a single founder", async function () {
const startingFundMeBalance = await ethers.provider.getBalance(
fundMe.getAddress()
);
const startingDeployBalance = await ethers.provider.getBalance(
deployer
);
const transactionResponse = await fundMe.cheaperWithdraw();
const transactionReceipt = await transactionResponse.wait();
const { gasUsed, gasPrice } = transactionReceipt;
const gasCost = gasUsed * gasPrice;
const endingFundMeBalance = await ethers.provider.getBalance(
fundMe.getAddress()
);
const endingDeployerBalance = await ethers.provider.getBalance(
deployer
);
assert.equal(endingFundMeBalance, 0);
assert.equal(
(startingFundMeBalance + startingDeployBalance).toString(),
(endingDeployerBalance + gasCost).toString()
);
});
it("allows us to cheaperwithdraw with multiple getFunder", async function () {
const accounts = await ethers.getSigners();
for (let i = 1; i < 6; i++) {
const fundMeConnectedContract = await fundMe.connect(accounts[i]);
await fundMeConnectedContract.fund({ value: sendValue });
}
const startingFundMeBalance = await ethers.provider.getBalance(
fundMe.target
);
const startingDeployerBalance = await ethers.provider.getBalance(
deployer
);
const transactionResponse = await fundMe.cheaperWithdraw();
const transactionReceipt = await transactionResponse.wait();
const { gasUsed, gasPrice } = transactionReceipt;
const gasCost = gasUsed * gasPrice;
const endingFundMeBalance = await ethers.provider.getBalance(
fundMe.getAddress()
);
const endingDeployerBalance = await ethers.provider.getBalance(
deployer
);
assert.equal(endingFundMeBalance, 0);
assert.equal(
(startingFundMeBalance + startingDeployerBalance).toString(),
(endingDeployerBalance + gasCost).toString()
);
await expect(fundMe.getFunder(0)).to.be.reverted;
for (i = 1; i < 6; i++) {
assert.equal(
await fundMe.getAddressToAmountFunded(accounts[i].address),
0
);
}
});
it("Only allows the owner to cheaperwithdraw", async function () {
const accounts = await ethers.getSigners();
const attacker = accounts[1];
const attackerConnectedContract = await fundMe.connect(attacker);
await expect(
attackerConnectedContract.Withdraw()
).to.be.revertedWithCustomError(fundMe, "Fundme__NotOwner");
});
});
});
|
集成测试
因为是在线上环境测试,这里比较容易出现的问题就是钱包里的钱不够,你需要把那个parseETH里面的数字改成0.1个eth
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| const { getNamedAccounts, ethers, network } = require("hardhat");
const { developmentChains } = require("../../helper-hardhat-config.js");
const {
isCallTrace,
} = require("hardhat/internal/hardhat-network/stack-traces/message-trace");
const { assert } = require("chai");
developmentChains.includes(network.name)
? describe.skip
: describe("Fundme", function () {
let fundMe;
let deployer;
const sendValue = ethers.parseEther("0.05");
beforeEach(async function () {
deployer = (await getNamedAccounts()).deployer;
fundMe = await ethers.getContract("Fundme", deployer);
});
it("allows people to fund and withdraw", async function () {
await fundMe.fund({ value: sendValue });
await fundMe.cheaperWithdraw();
const endingBalance = await ethers.provider.getBalance(fundMe.target);
assert.equal(endingBalance.toString(), "0");
});
});
|
其他
快速脚本
这里我在跟课程的时候写的,但是并不知道有什么用
fundme
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| const { getNamedAccount, ethers, getNamedAccounts } = require("hardhat");
async function main() {
const { deployer } = await getNamedAccounts();
const fundMe = await ethers.getContract("Fundme", deployer);
console.log("Funding Contract...");
const transactionResponse = await fundMe.fund({
value: ethers.parseEther("0.1"),
});
await transactionResponse.wait(1);
console.log("funded");
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
|
withdraw
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| const { getNamedAccounts, ethers } = require("hardhat");
async function main() {
const { deployer } = await getNamedAccounts();
const fundMe = await ethers.getContract("Fundme", deployer);
console.log("Funding...");
const transactionResponse = await fundMe.Withdraw();
await transactionResponse.wait(1);
console.log("Got it back!");
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
|
vscode的js调试功能
在vscode的左边选择调试然后打开一个js调试终端
然后在调试终端中运行脚本即可
如果需要查看特定的数据可以直接打开调试控制台输入对应的变量名即可
上传到github
先要在github创建仓库
在github上创建好仓库后
在终端输入
git init -b main
初始化项目文件夹、
其中的绿色的是需要上传的,灰色的是在.gitignore中的
使用git status可以查看git的暂存状态
使用
git add .
即可进行暂存
暂存之后的文件就是使用git status展现的如下形式
接下来使用
git commit -m 'first commit'
这次再使用gitstatus就没有东西了
然后使用
git remote add origin https://github.com/guhuihey/web3test.git
连接仓库并命名为origin
使用
git remote -v
可以查看仓库情况
最后通过
git push origin main
上传到github既可,当然因为这一步会有一个身份验证,如果无法验证可以创建一个github token以达成目的
依赖包的安装
1
| yarn add --dev @nomiclabs/hardhat-ethers@npm:hardhat-deploy-ethers ethers @nomiclabs/hardhat-etherscan @nomiclabs/hardhat-waffle chai ethereum-waffle hardhat hardhat-contract-sizer hardhat-deploy hardhat-gas-reporter prettier prettier-plugin-solidity solhint solidity-coverage dotenv @chainlink/contracts
|