vscode+solidity开发简介

74 阅读2分钟

solidity

环境安装

  • wsl2(可选)
  • nodejs
  • yarn
  • vscode
    • Solidity+Hardhat(Solidity): 语法解释插件
    • prettier: 代码格式化
    • remote development(在wsl2环境开发): ctrl+shift+p输入wsl->WSL: Connect to WSL in New Window
  • git

vscode代码格式化: ctrl+shift+p输入setting->Preferences: Open Using Settings(JSON),编辑文件:

{
    "security.workspace.trust.untrustedFiles": "open",
    "solidity.telemetry": true,
    "[solidity]": {
        "editor.defaultFormatter": "NomicFoundation.hardhat-solidity"
    },
    "[javascript]": {
        "editor.defaultFormatter": "esbenp.prettier-vscode"
    },
    "editor.formatOnSave": true
}

ctrl+shift+p输入setting->Preferences: Open Settings(UI),勾选Editor: Format On Save选项.

自定义格式化代码

安装 prettierprettier-plugin-solidity 插件:

yarn add prettier prettier-plugin-solidity

安装后 package.json 文件变化:

{
  "dependencies": {
    "prettier": "^3.5.2",
    "prettier-plugin-solidity": "^1.4.2"
  }
}

创建格式化规则文件 .prettierrc

{
  "tabWidth": 4, // 代码缩进宽度
  "useTabs": false, // 使用空格来进行代码缩进,而不是使用制表符(Tab)
  "semi": false, // 代码行结尾无分号
  "singleQuote": false // 字符串统一使用双引号
}

这个文件中配置的规则优先于vscode插件中的配置.

开发

mkdir hh-fcc
cd hh-fcc

# 创建项目目录
mkdir ethers-simple-storage-fcc

# 用`vscode`打开目录
code ethers-simple-storage-fcc
  • 创建合约文件SimpleStorage.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.7;

// EVM, Ethereum Virtual Machine

contract SimpleStorage {
    uint256 favoriteNumber;

    struct People {
        uint256 favoriteNumber;
        string name;
    }
    People[] public people;

    mapping(string => uint256) public nameToFavoriteNumber;

    // virtual can be override
    function store(uint256 _favoriteNumber) public virtual {
        favoriteNumber = _favoriteNumber;
    }

    function retrieve() public view returns (uint256) {
        return favoriteNumber;
    }

    function addPerson(string memory _name, uint256 _favoriteNumber) public {
        people.push(People(_favoriteNumber, _name));
        nameToFavoriteNumber[_name] = _favoriteNumber;
    }
}

  • 编译合约
# 安装 solc
yarn add solc@0.8.7-fixed

yarn solcjs --bin --abi --include-path ./node_modules/ --base-path . -o . ./SimpleStorage.sol
  • 创建部署合约代码文件deploy.js
const ethers = require("ethers");
const fs = require("fs-extra");
require("dotenv").config();

async function main() {
  // First, compile this!
  // And make sure to have your ganache network up!
  // The old way can be seen below:
  // let provider = new ethers.providers.JsonRpcProvider(process.env.RPC_URL)
  // On ether 6 and above, you should use like this
  let provider = new ethers.JsonRpcProvider(process.env.RPC_URL);
  try {
    const blockNumber = await provider.getBlockNumber();
    console.log("Connected to Sepolia. Current block number:", blockNumber);
  } catch (error) {
    console.error("Failed to connect to the RPC provider:", error);
  }

  const encryptedJson = fs.readFileSync(".encryptedKey.json", "utf8");
  let wallet = ethers.Wallet.fromEncryptedJsonSync(
          encryptedJson,
          process.env.PRIVATE_KEY_PASSWORD,
  );
  wallet = await wallet.connect(provider);
  // let wallet = new ethers.Wallet(process.env.PRIVATE_KEY, provider);

  let abi = fs.readFileSync("./SimpleStorage_sol_SimpleStorage.abi", "utf8");
  let binary = fs.readFileSync("./SimpleStorage_sol_SimpleStorage.bin", "utf8");

  const contractFactory = new ethers.ContractFactory(abi, binary, wallet);
  console.log("Deploying, please wait ...");
  // const contract = await contractFactory.deploy({ gasLimit: 1000000000000 });
  const contract = await contractFactory.deploy(); // Stop here! Wait for contract to deploy
  console.log("Deploy Transaction:", contract.deploymentTransaction());
  await contract.waitForDeployment();
  // await contract.deploymentTransaction.wait(1); // eth v5 syntax
  console.log(`Contract adddress: ${contract.target}`);

  // const transactionReceipt = await contract.deployTransaction.wait(1);
  // console.log("Here is deployment transaction (transaction response): ");
  // console.log(contract.deployTransaction);
  // console.log("Here is transaction receipt: ");
  // console.log(transactionReceipt);

  // const nonce = await wallet.getNonce();
  // let tx = {
  //   nonce: nonce,
  //   gasPrice: 100000000000,
  //   gasLimit: 1000000,
  //   to: null,
  //   value: 0,
  //   data: "0x608060405234801561001057600080fd5b50610771806100206000396000f3fe608060405234801561001057600080fd5b50600436106100575760003560e01c80632e64cec11461005c5780636057361d1461007a5780636f760f41146100965780638bab8dd5146100b25780639e7a13ad146100e2575b600080fd5b610064610113565b604051610071919061052a565b60405180910390f35b610094600480360381019061008f919061046d565b61011c565b005b6100b060048036038101906100ab9190610411565b610126565b005b6100cc60048036038101906100c791906103c8565b6101b6565b6040516100d9919061052a565b60405180910390f35b6100fc60048036038101906100f7919061046d565b6101e4565b60405161010a929190610545565b60405180910390f35b60008054905090565b8060008190555050565b6001604051806040016040528083815260200184815250908060018154018082558091505060019003906000526020600020906002020160009091909190915060008201518160000155602082015181600101908051906020019061018c9291906102a0565b505050806002836040516101a09190610513565b9081526020016040518091039020819055505050565b6002818051602081018201805184825260208301602085012081835280955050505050506000915090505481565b600181815481106101f457600080fd5b906000526020600020906002020160009150905080600001549080600101805461021d9061063e565b80601f01602080910402602001604051908101604052809291908181526020018280546102499061063e565b80156102965780601f1061026b57610100808354040283529160200191610296565b820191906000526020600020905b81548152906001019060200180831161027957829003601f168201915b5050505050905082565b8280546102ac9061063e565b90600052602060002090601f0160209004810192826102ce5760008555610315565b82601f106102e757805160ff1916838001178555610315565b82800160010185558215610315579182015b828111156103145782518255916020019190600101906102f9565b5b5090506103229190610326565b5090565b5b8082111561033f576000816000905550600101610327565b5090565b60006103566103518461059a565b610575565b90508281526020810184848401111561037257610371610704565b5b61037d8482856105fc565b509392505050565b600082601f83011261039a576103996106ff565b5b81356103aa848260208601610343565b91505092915050565b6000813590506103c281610724565b92915050565b6000602082840312156103de576103dd61070e565b5b600082013567ffffffffffffffff8111156103fc576103fb610709565b5b61040884828501610385565b91505092915050565b600080604083850312156104285761042761070e565b5b600083013567ffffffffffffffff81111561044657610445610709565b5b61045285828601610385565b9250506020610463858286016103b3565b9150509250929050565b6000602082840312156104835761048261070e565b5b6000610491848285016103b3565b91505092915050565b60006104a5826105cb565b6104af81856105d6565b93506104bf81856020860161060b565b6104c881610713565b840191505092915050565b60006104de826105cb565b6104e881856105e7565b93506104f881856020860161060b565b80840191505092915050565b61050d816105f2565b82525050565b600061051f82846104d3565b915081905092915050565b600060208201905061053f6000830184610504565b92915050565b600060408201905061055a6000830185610504565b818103602083015261056c818461049a565b90509392505050565b600061057f610590565b905061058b8282610670565b919050565b6000604051905090565b600067ffffffffffffffff8211156105b5576105b46106d0565b5b6105be82610713565b9050602081019050919050565b600081519050919050565b600082825260208201905092915050565b600081905092915050565b6000819050919050565b82818337600083830152505050565b60005b8381101561062957808201518184015260208101905061060e565b83811115610638576000848401525b50505050565b6000600282049050600182168061065657607f821691505b6020821081141561066a576106696106a1565b5b50919050565b61067982610713565b810181811067ffffffffffffffff82111715610698576106976106d0565b5b80604052505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b61072d816105f2565b811461073857600080fd5b5056fea26469706673582212209c736c0e989b45ac568fd272f855507e975964dd10c0ec5dfac5f0be86d908bf64736f6c63430008070033",
  //   chainId: 1337,

  //   // Additionally, there is a v,r,and s variable that ethers handles for us.
  //   // This is the signature of the transaction.
  //   // There is a lot of math going on with those values, but that's how it's gaurenteed that the transaction is signed!
  //   // https://ethereum.stackexchange.com/questions/15766/what-does-v-r-s-in-eth-gettransactionbyhash-mean
  // };
  // // There is also a v, r, and s component of the transaction that Ethers will handle for us.
  // // It's these three components that make up the cryptographic signature.
  // // We won't go into this, because it's a lot of math.

  // console.log("Let's deploy another! Please Wait...");
  // // let resp = await wallet.signTransaction(tx)
  // const sentTxResponse = await wallet.sendTransaction(tx);
  // await sentTxResponse.wait(1);
  // console.log(sentTxResponse);

  // Get number
  const currentFavoriteNumber = await contract.retrieve();
  console.log(`Current favorite number: ${currentFavoriteNumber.toString()}`); // BigNumber

  const transactionResponse = await contract.store("9");
  const transactionReceipt = await transactionResponse.wait(1);
  const updateFavoriteNumber = await contract.retrieve();
  console.log(`Current favorite number: ${updateFavoriteNumber}`);
}

main()
        .then(() => process.exit(0))
        .catch((error) => {
          console.error(error);
          process.exit(1);
        });

执行:

node ./deploy.js
  1. 安装 ethers
yarn add ethers

安装完成后 package.json:

{
  "dependencies": {
    "ethers": "^6.13.5"
  }
}
  • 格式化一个文件: ctrl+shift+p输入Format Document, 选择想要的格式(如 JSON)

  • 私钥的加密: 防止系统被攻击后被盗

const ethers = require("ethers");
const fs = require("fs-extra");
require("dotenv").config();

async function main() {
  const wallet = new ethers.Wallet(process.env.PRIVATE_KEY);
  // In later version (^6.2.3 as of this commit) of etherjs, PRIVATE_KEY is inferred from wallet, so there is no need to
  // pass private key again.
  const encryptedJsonKey = await wallet.encrypt(
    process.env.PRIVATE_KEY_PASSWORD
  );
  console.log(encryptedJsonKey);
  fs.writeFileSync("./.encryptedKey.json", encryptedJsonKey);
}

main()
  .then(() => process.exit(0))
  .catch((error) => {
    console.error(error);
    process.exit(1);
  });

创建钱包的代码修改为:

{
  const encryptedJson = fs.readFileSync(".encryptedKey.json", "utf8");
  let wallet = ethers.Wallet.fromEncryptedJsonSync(
          encryptedJson,
          process.env.PRIVATE_KEY_PASSWORD
  );
  wallet = await wallet.connect(provider);
  // let wallet = new ethers.Wallet(process.env.PRIVATE_KEY, provider);
}

将合约部署到测试网

这里以eth sepolia为例.

img.png

  • metamask key 配置到rpc_url,.env文件:
RPC_URL = https://sepolia.infura.io/v3/<your_key>
  • 部署:
$env:PRIVATE_KEY_PASSWORD="password"; node ./deploy.js