How do I test the ERC721 function with Open Zeppelin SDK?

I want to test the following contract.
The contract contracts/Token.sol is using ERC721 contract and Open Zeppelin architecture.
And I wrote the test code test/Token.js following but I ran the truffle test, the transaction was reverted, and said Error: Returned error: VM Exception while processing transaction: revert.
await proxy.methods.mint().send({from: sender}) has some problem.
How do I test the ERC721 minting function?
Thank you!

contracts/Token.sol

pragma solidity ^0.5.8;
import "@openzeppelin/contracts-ethereum-package/contracts/token/ERC721/ERC721Full.sol";
import "@openzeppelin/upgrades/contracts/Initializable.sol";


contract Token is Initializable, ERC721Full {

    string public tokenName;
    uint256 internal nextTokenId;

    function mint() external {
        uint256 tokenId = nextTokenId;
        nextTokenId = nextTokenId.add(1);
        super._mint(msg.sender, tokenId);
    }

    function initialize(string memory _tokenName) public {
        tokenName = _tokenName;
        nextTokenId = 0;
    }

    function setTokenURI(uint256 _tokenId, string calldata _message) external {
        super._setTokenURI(_tokenId, _message);
    }
}

test/Token.js

const { TestHelper } = require('@openzeppelin/cli');
const { Contracts, ZWeb3 } = require('@openzeppelin/upgrades');

ZWeb3.initialize(web3.currentProvider);

const ABNToken = Contracts.getFromLocal('ABNToken');
const tokenName = "start";

web3.eth.getAccounts(function(error, result) {
    if (error) console.log("Couldn't get accounts")
    sender = result[0]
})

require('chai').should();

contract('ABNToken', function () {
    let proxy

    beforeEach(async function () {
        this.project = await TestHelper();
        proxy = await this.project.createProxy(ABNToken);
        proxy.options.from = sender;
        await proxy.methods.initialize(tokenName).send({from: sender});
    })

    it('Initialize function test', async function () {
        let tokenname = await proxy.methods.tokenName().call();
        tokenname.should.eq(tokenName);
    })

    it('Mint function test', async function () {
        console.log(proxy.methods)
        await proxy.methods.mint().send({from: sender})
    })
})
1 Like

Hi @Shinsaku,

I can reproduce the issue. I haven’t found a resolution yet.

Thank you!
I use truffle test right now!

1 Like

revert can mean you are probably not allowed to mint. So you may check this

1 Like

Hi @blexx
Thank you for the answer.

revert can mean you are probably not allowed to mint. So you may check this

Is that mean I need to change configure somehow to execute mint function?

Well,

first I would check if the code in your test is ok. I have not worked with the upgradabel stuff.
Regarding ERC721 check https://docs.openzeppelin.com/contracts/2.x/api/token/erc721

Implementation


There you find ERC721 Mintabel

So there is the method

function mint(address to, uint256 tokenId) public onlyMinter returns (bool) {
        _mint(to, tokenId);
        return true;
    }

So this is as far as i understand the interface of mint needing to params. To equals to recipient address and tokenId is uint256. So you only have one param in you call.

First test the method of the original interface:
mint( sender, unint265(1234)) … Using this should work. So may do this as the first test

Ok, you have made your own defintion of mint without params:
Rename you definition to something like createToken and then test it again. So this insures you don’t get wired when reading error messages.

Then if the first version works, but the second not, then you may start with createToken as a wapper around your the orignal mint interface.

But dependant what you like to archive with you project, there is probably no reason to write your own version of mint, because everything you like can be prepared in test/JavaScript environment.

Second advice, don’t start with ERC721 full when learning. Start with the base contract and go for there.
In case you are a beginner, I would not start with upgradable contracts in the first place. Just take the openZeppelin without it. This makes it easier to lean ERC721 stuff first, then It is a short way to include upgradabel stuff to it.

So may be strip this code down to the basics and go from there.

Hope this helps a little bit. I payed a lot of time starting with the same approach…

1 Like

Just a quick answer here (on mobile)looks like nothing is being initialized- so the contract doesn’t even have any minters assigned, hence it won’t let you mint.

Look into the contracts that ERC721 inherits from, each one of them needs to be initialized. So for example have a look at one of m contracts:

Here I create an erc721 token, but I initialize all the contracts I inherit from in a initialize function.

My example is a little more than you need- but there is also a simple test in the file. Maybe that will set you in the right direction!

2 Likes

Hi @Shinsaku,

With upgradeable contracts you need to ensure that they are initialized.

A simple ERC721 contract and test can be found below (sorry for the delay):

Simple721Token.sol

pragma solidity ^0.5.0;

import "@openzeppelin/upgrades/contracts/Initializable.sol";

import "@openzeppelin/contracts-ethereum-package/contracts/token/ERC721/ERC721Full.sol";
import "@openzeppelin/contracts-ethereum-package/contracts/token/ERC721/ERC721Mintable.sol";

contract Simple721Token is Initializable, ERC721Full, ERC721Mintable {

    function initialize(address sender) public initializer {
        ERC721.initialize();
        ERC721Metadata.initialize("Simple721Token", "721");
        ERC721Enumerable.initialize();
        ERC721Mintable.initialize(sender);
    }
}

Simple721Token.test.js

// Based on https://docs.openzeppelin.com/sdk/2.5/testing

const { TestHelper } = require('@openzeppelin/cli');
const { Contracts, ZWeb3 } = require('@openzeppelin/upgrades');

ZWeb3.initialize(web3.currentProvider);

const Simple721Token = Contracts.getFromLocal('Simple721Token');

require('chai').should();

contract('Simple721Token', function ([_, minter, other]) {

  const firstTokenId = 1;

  beforeEach(async function () {
    this.project = await TestHelper();
    this.proxy = await this.project.createProxy(Simple721Token, { initMethod: 'initialize', initArgs: [minter]});
  })

  it('should have a name', async function () {
    const result = await this.proxy.methods.name().call();
    result.should.eq('Simple721Token');
  })

  it('should mint', async function () {
    await this.proxy.methods.mint(other, firstTokenId).send({from: minter, gas: 5e6});
    const result = await this.proxy.methods.ownerOf(firstTokenId).call()
    result.should.eq(other);
  })
})