How to develop smart contracts with OpenZeppelin

How to develop smart contracts with OpenZeppelin

This article is not about how to use the Solidity language to write contracts, but how to use the framework to develop contracts. Learn about the grammar and keywords of the Solidity language through other channels. The preparation of the Node..js development environment is also not specific.

First create a project:

mkdir learn && cd learn

npm init -y

The relatively well-known Ethereum development framework is Hardhat, which is generally used in conjunction with ethers.js. Another well-known development framework is Truffle, which is generally used with web3.js. Each framework has its own advantages.

First install Hardhat in our project directory

npm install --save-dev hardhat

After the installation is complete, you can use the npx hardhat command to create the Hardhat configuration file hardhat.config.js

first contract

Create a directory contracts to store Solidity source code files, this directory is similar to the src directory in other languages.

Create a simple smart contract named Box that can store a value and then be retrieved.

Create the file contracts/Box.sol.

// contracts/Box.sol

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

contract Box {

    uint256 private _value;

    // Emitted when the stored value changes

    event ValueChanged(uint256 value);

    // Stores a new value in the contract

    function store(uint256 value) public {

        _value = value;

        emit ValueChanged(value);

    }

    // Reads the last stored value

    function retrieve() public view returns (uint256) {

        return _value;

    }

}

Compile Solidity

Since the Ethereum Virtual Machine (EVM) cannot directly run Solidity code, we need to compile the Solidity code into EVM bytecode.

The Solidity version used by our smart contract is 0.8, which requires a little configuration in hardhat.config.js.

/**

 * @type import('hardhat/config').HardhatUserConfig

 */

 module.exports = {

  solidity: "0.8.4",

};

Compile the contract: npx hardhat compile

The compilation task will automatically retrieve the contracts in the contracts directory and compile them using the Solidity compiler configured in hardhat.config.js, so it should be noted that the Solidity version number of the pragma in the contract is consistent with the version number in hardhat.config.js.

After the compilation task is completed, a directory named artifacts will be generated in the root directory of the project, which contains the compiled bytecode and metadata json files. This directory can be added to the .gitignore file.

Add more contracts

As the project develops, we will create more contracts that interact with each other, and these contracts will be stored in their own .sol files.

Now add a simple access control system to the Box contract. Create an Auth contract to store the administrator address, and only allow the Box contract to use the accounts allowed by the Auth contract.

Since the compiler will automatically retrieve all files in the contracts directory and its subdirectories, we can organize the code structure freely. We create a subdirectory access-control to store the Auth contract.

// contracts/access-control/Auth.sol

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.4;

contract Auth {

    address private _administrator;

    constructor(address deployer) {

        // Make the deployer of the contract the administrator

        _administrator = deployer;

    }

    function isAdministrator(address user) public view returns (bool) {

        return user == _administrator;

    }

}

In order to use this contract, we use import in the Box contract to associate the relative path of the Auth contract.

// contracts/Box.sol

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.4;

// Import Auth from the access-control subdirectory

import "./access-control/Auth.sol";

contract Box {

    uint256 private _value;

    Auth private _auth;

    event ValueChanged(uint256 value);

    constructor() {

        _auth = new Auth(msg.sender);

    }

    function store(uint256 value) public {

        // Require that the caller is registered as an administrator in Auth

        require(_auth.isAdministrator(msg.sender), "Unauthorized");

        _value = value;

        emit ValueChanged(value);

    }

    function retrieve() public view returns (uint256) {

        return _value;

    }

}

Breaking a complex contract into multiple contracts is a good way to keep each contract simple and a best practice for contract development. Of course, it can also be achieved through the inheritance of Solidity.

Using the OpenZeppelin contract

Reusable modules and libraries are the foundation of good software. OpenZeppelin Contracts contains many audited smart contract templates that can be easily used.

about inheritance

We can achieve our goal by adding functionality to the OpenZeppelin contract. Solidity's multiple inheritance is a great way to do this.

For example, the Ownable contract stipulates that the deployer account is the owner of the contract, and provides a modifier called onlyOwner.

Import the OpenZeppelin contract

Execute the following command to install the latest released OpenZeppelin contract library.

npm install --save-dev @openzeppelin/contracts

To use a contract, it can be imported using the path prefix @openzeppelin/contracts. For example, to replace our own Auth contract, we can use @openzeppelin/contracts/access/Ownable.sol to import the Ownable.sol contract to add access control to Box.

// contracts/Box.sol

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.4;

// Import Ownable from the OpenZeppelin Contracts library

import "@openzeppelin/contracts/access/Ownable.sol";

// Make Box inherit from the Ownable contract

contract Box is Ownable {

    uint256 private _value;

    event ValueChanged(uint256 value);

    // The onlyOwner modifier restricts who can call the store function

    function store(uint256 value) public onlyOwner {

        _value = value;

        emit ValueChanged(value);

    }

    function retrieve() public view returns (uint256) {

        return _value;

    }

}

Guess you like

Origin blog.csdn.net/yishui_hengyu/article/details/125135536