Skip to content

OpenZeppelin Preset Contracts

Let's add a simple ERC721 Token Preset.

Why use OpenZeppelin Contracts

From the OpenZeppelin Github Page: A library for secure smart contract development. Build on a solid foundation of community-vetted code.

They come with a preset, so we can easily do an ERC721 Contract that is mintable, pausable, burnable and supports all functions we possibly need to be ERC721 compliant.

For this first implementation we will be using

Creating the ERC721 Token

Create a new file in /contracts/AisthisiToken.sol with the following content:

// SPDX-License-Identifier: MIT
pragma solidity 0.8.3;

import "@openzeppelin/contracts/token/ERC721/presets/ERC721PresetMinterPauserAutoId.sol";

contract AisthisiToken is ERC721PresetMinterPauserAutoId {

    constructor() ERC721PresetMinterPauserAutoId("AisthisiToken", "AIS", "") {}

    function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
       return string(abi.encodePacked(super.tokenURI(tokenId),".json"));

This is a normal ERC721 Contract based on the preset that OpenZeppelin gives us. It might look overwhelming at this point, but there is nothing special to it - let's look what happens under the hood. If we open the ERC721PresetMinterPauserAutoId Contract then you see that it actually does a few things in the constructor (here is the documentation of the preset):

  1. It sets up a new ERC721 Token Contract with the name and the symbol, in this case "AisthisiToken" and "AIS".
  2. It sets the tokenURI base url to ""
  3. It will grant the deployer pausing rights and minting rights.
  4. Additionally, and completely optional, but we decided to add ".json" to the url for the metadata file automatically, hence modified the tokenURI function.
 constructor(string memory name, string memory symbol, string memory baseTokenURI) ERC721(name, symbol) {
        _baseTokenURI = baseTokenURI;

        _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());

        _setupRole(MINTER_ROLE, _msgSender());
        _setupRole(PAUSER_ROLE, _msgSender());

Let's give this a try in Truffle.

Adding Migrations to Truffle

Create a migrations file to migrations/2_token_migration.js with the following content

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

module.exports = function (deployer) {

It should look like this now:

In the truffle-config.js file, edit the compiler settings to:

// Configure your compilers
  compilers: {
    solc: {
      version: "0.8.3",    // Fetch exact version from solc-bin (default: truffle's version)
      // docker: true,        // Use "0.5.1" you've installed locally with docker (default: false)
      //settings: {          // See the solidity docs for advice about optimization and evmVersion
      //  optimizer: {
      //    enabled: true,
      //    runs: 200
      //  },
      //  evmVersion: "byzantium"

We will later have a look how we can use the optimizer and other techniques to save gas costs.

Deploying the Token to the Truffle Development Network

Then simply run the truffle development console

truffle development

This should open the Truffle Development console:

Now lets just migrate the token and then mint one token:


will deploy the tokens to this test-chain.

Testing the Token on the Truffle Development Console

Then you can simply type in

let token = await AisthisiToken.deployed();[0]);

This will mint one token to your address.

That also means we can freely send it around!

token.transferFrom(accounts[0], accounts[1], 0);

Will transfer the token from account 0 to account #1.

Let's check the balance:

(await token.balanceOf(accounts[1])).toString()

Should output 1:

That's that. But what about that locking functionality?

Last update: March 28, 2022