Ikeh Akinyemi Ikeh Akinyemi is a software engineer based in Rivers State, Nigeria. He’s passionate about learning pure and applied mathematics concepts, open source, and software engineering.

Building secure smart contracts with OpenZeppelin

5 min read 1566

Building Secure Smart Contracts OpenZeppelin

Security is one of the core foundations of blockchain technology. Well-publicized cybersecurity breaches have actually been attributed to systems linking to those using the blockchain, rather than to the blockchain itself. Still, security is often top-of-mind for users who interact with a decentralized application. They may wonder: “How secure is the network?” Or, “How secure is my transaction or assets?” OpenZeppelin, a provider of security products for decentralized applications, aims to address these concerns.

OpenZeppelin offers open source OpenZeppelin Contracts, written in Solidity, for building secure smart contracts. OpenZeppelin Contracts uses ERC standards for Ethereum-based tokens that can be used in many types of projects. In an effort to minimize cyber risk associated with building secure smart contracts on Ethereum or other blockchains, OpenZeppelin Contracts are continually audited and tested.

In this tutorial, we’ll investigate how to use OpenZeppelin Contracts and the Truffle framework to build and test secure smart contracts. Additionally, we’ll demonstrate how to use OpenZeppelin Contracts on the Remix IDE.

Using OpenZeppelin Contracts in a Truffle project

Let’s explore building smart contracts using the Truffle framework and OpenZeppelin Contracts. OpenZeppelin Contracts contains Solidity codes imported into the source files containing our Solidity code. This reduces the amount of code that must be written manually.

We’ll review how to set up a Truffle development environment, as well as how to install and implement OpenZeppelin in a Truffle project.

Setting up a Truffle development environment

Let’s set up a development environment for our project using the Truffle framework.

To install the framework globally, run the following command:

npm install truffle -g

Next, create a new directory to contain the Solidity project, openzeppelin-contracts:

mkdir openzeppelin-contracts

Now, cd into the newly created folder, and run the following Truffle command:

truffle init

This will create a Truffle template project containing folders, source files, and configuration files. We can tweak and/or add more source code to the existing source code to build our specific project.



Next, let’s install the OpenZeppelin Contracts package with npm:

npm init -y
npm install @openzeppelin/contracts

Now, we’ll spin up some custom token projects, using OpenZeppelin Contracts to save on development time and also ensure the security of the project. As we develop the projects, we’ll introduce some of the interfaces exposed by the package for use within our code.

Building an ERC-20 project using OpenZeppelin Contracts and Truffle

Let’s set up an ERC-20 fungible token project.

First, cd into the ./contracts folder which contains the solidity source files. Inside this directory, create a .sol file, Rocket.sol.

touch Rocket.sol

Using your preferred IDE, open the project and update the Rocket.sol file with the following code to implement a custom token:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import '@openzeppelin/contracts/token/ERC20/ERC20.sol';

contract Rocket is ERC20 {
  constructor(uint256 initialSupply) ERC20("ROCKET", "ROC") {
    _mint(msg.sender, initialSupply * (10 ** decimals()));
  }
}

This sample code represents the minimum number of OpenZeppelin Contracts package interfaces imports required to implement a custom token. Using the ERC20 function, we define a custom name for the token, ROCKET, as well as a symbol, ROC.

The initialSupply parameter on the constructor function will hold the total amount of supply intended for this token. Visit OpenZeppelin Contracts on GitHub to see other interfaces inherited from the ERC20.sol file.


More great articles from LogRocket:


Now, let’s write a unit test for our smart contract. We’ll implement the migration file to develop the smart contract locally, and then write the test suite.

Open the /migrations folder, and create a new .js file. Then, run the following command to create the 2_customToken.js file:

cd migrations
touch 2_customToken.js

Inside the new file, add the following JavaScript code to set up a migration process for our custom token project:

const Rocket = artifacts.require("Rocket");

module.exports = function(deployer) {
  deployer.deploy(Rocket, 1000000);
}

Next, let’s create a .test.js file inside the /test folder. We’ll use this .test.js file to test our project.

cd test
touch customToken.test.js

Inside the new file, add the following JavaScript code to implement a test suite for our custom token project:

const Rocket = artifacts.require("contracts/Rocket.sol");

contract("Rocket", (accounts) => {
  before(async () => {
    rocket = await Rocket.deployed();
  });

  it("mint 1M worth of tokens", async () => {
    let balance = await rocket.balanceOf(accounts[0]);
    balance = web3.utils.fromWei(balance);
    assert.equal(balance, 1000000, "Initial supply of token is 1000000");
  });

  it("transfer token to another account", async () => {
    let amount = web3.utils.toWei("10000", "ether");
    await rocket.transfer(accounts[1], amount, { from: accounts[0] });
    let balance = await rocket.balanceOf(accounts[1]);
    balance = web3.utils.fromWei(balance);
    assert.equal(balance, 10000, "token balance is 10000");
  });
});

With the help of Truffle, we define unit tests to check the token account balance and confirm that the initial supply matches the specified amount.

We also define a transfer transaction between the first account holding the token, accounts[0], and another, account[1]. These are dummy accounts provided to us by the Truffle framework. We transfer 10,000 worth of ROC tokens from account[0] to account[1].

Now, let’s run the test:

truffle test

Compiling your contracts...
===========================
> Compiling ./contracts/Migrations.sol
> Compiling ./contracts/Rocket.sol
> Compiling @openzeppelin/contracts/token/ERC20/ERC20.sol
> Compiling @openzeppelin/contracts/token/ERC20/IERC20.sol
> Compiling @openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol
> Compiling @openzeppelin/contracts/utils/Context.sol
> Artifacts written to /var/folders/z2/963_51js5f370fvrx34q8cr40000gn/T/test--7499-6UUMhtN9GNHv
> Compiled successfully using:
   - solc: 0.8.11+commit.d7f03943.Emscripten.clang



  Contract: Rocket
    ✓ mint 1M worth of tokens
    ✓ transfer token to another account (69ms)


  2 passing (126ms)

The truffle test command compiles and migrates our smart contract to a temporary local blockchain spun up by the Truffle framework. Next, Truffle runs our test suite against the functions and features defined in our smart contract.

Building an ERC-721 project using OpenZeppelin Contracts and Truffle

Now, let’s set up an ERC-721 non-fungible token project. ERC-721 refers to the defined standard for NFT smart contract projects. Non-fungible means that the tokens are unique.

Let’s create a .sol file, SpaceShip.sol, in the /contracts folder.

Next, we’ll add the following source code to the SpaceShip.sol file:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import"@openzeppelin/contracts/token/ERC721/ERC721.sol";
import"@openzeppelin/contracts/utils/Counters.sol";
contract SpaceShip is ERC721 {
    using Counters for Counters.Counter;
    Counters.Counter private _tokenIds;
    constructor() ERC721("SpaceShip","SPSP") {}
    function _mintToken(address account, string memory tokenURI) public returns (uint256){
        _tokenIds.increment();
        uint256 newTokenId = _tokenIds.current();
        _mint(account, newTokenId);
        return newTokenId;
    }
}

In this code, we import two objects: the ERC721 class object and the Counters library. We inherit all the exposed interfaces defined on the ERC721 class. Using the Counters library, we define unique IDs for every NFT minted using our smart contract.

Our mint function, _mintToken, is the account to which each newly minted token will be linked. We pass the receiver’s account ID, address account, to this mint function as an argument.

According to the metadata specification agreed to by the Ethereum community, the tokenURI argument points to token resources, such as an image, video, access, or any other resource of value. Inside the _mintToken function, we call the _mint function exposed by our smart contract to carry out the actual computation and storage of the NFTs.

Building an ERC-1155 project using OpenZeppelin Contracts and Remix

As the last stop on our tour of OpenZeppelin Contracts, we’ll set up an ERC-1155 fungibility-agnostic, gas-efficient token project on Ethereum’s IDE, Remix. ERC-1155 projects can represent multiple tokens at once with a single contract.

Visit the Remix – Ethereum IDE to get an instance of the online IDE that we’ll use to successfully build and compile our project.

N.B., this tutorial assumes you have some experience with Remix

Let’s create a .sol file, lemonade.sol, in the /contracts folder.

Next, we’ll add the following source code to the lemonade.sol file:

pragma solidity ^0.8.0;

import "https://github.com/OpenZeppelin/openzeppelin-contracts/master/contracts/token/ERC1155/ERC1155.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/master/contracts/access/Ownable.sol";

contract Lemonade is ERC1155, Ownable {
    uint256 public const LEMONADE = 0;
    constructor() ERC1155("") {
        _mint(msg.sender, LEMONADE, 12, "");
    }

    function mint(address account, uint256 tokenID, uint256 supply) public onlyOwner {
        _mint(account, tokenID, supply, "");
    }

    function burn(address account, uint256 tokenID, uint256 amount) public onlyOwner {
        _burn(account, tokenID, amount);
    }
}

In this code, we import the OpenZeplin Contracts in Remix through the GitHub link to the source files, ERC1155.sol and Ownable.sol. We implement both the _mint() and _burn() functions in the smart contract. The _burn() function interface removes the limiting factor on the ERC-721 specification, as minting is immediately limited once a smart contract is deployed on the blockchain. We also extended the smart contract so that we can mint more NFTs in the future.

Conclusion

In this tutorial, we demonstrated how to use OpenZeppelin Contracts and the Truffle framework to build and test secure smart contracts for ERC-20 fungible tokens and ERC-721 non-fungible tokens. We also demonstrated how to use OpenZeppelin Contracts and Remix to build smart contracts for ERC-1155 fungibility-agnostic, gas-efficient tokens.

OpenZeppelin Contracts can be used to build decentralized applications ranging from DeFi, governance, access, and security smart contracts to custom projects like NFT games, staking, or play-to-earn projects.

To better understand how OpenZeppelin Contracts improve the efficiency of DApp development, read up on EIP specifications and study the OpenZeppelin codebase in the company’s docs or on GitHub.

Join organizations like Bitso and Coinsquare who use LogRocket to proactively monitor their Web3 apps

Client-side issues that impact users’ ability to activate and transact in your apps can drastically affect your bottom line. If you’re interested in monitoring UX issues, automatically surfacing JavaScript errors, and tracking slow network requests and component load time, try LogRocket.https://logrocket.com/signup/

LogRocket is like a DVR for web and mobile apps, recording everything that happens in your web app or site. Instead of guessing why problems happen, you can aggregate and report on key frontend performance metrics, replay user sessions along with application state, log network requests, and automatically surface all errors.

Modernize how you debug web and mobile apps — .

Ikeh Akinyemi Ikeh Akinyemi is a software engineer based in Rivers State, Nigeria. He’s passionate about learning pure and applied mathematics concepts, open source, and software engineering.

Leave a Reply