TokensOfConcord Contract Review

A review of the TokensOfConcord smart contract

April 9, 2023 | 3 minute read

TokensOfConcord Contract Review

Tokens of Concord is part of the WAGDIE ecosystem. It’s an ERC1155 contract that the founders created to distribute tokens to their community.

Links:

Creating Tokens

1155s are great for storing multiple tokens of different types in the same contract. i.e. you can have many different ERC721-like and ERC20-like contracts, all in a single contract. You accomplish this with a mapping that tracks the token IDs to the token URIs.

Tokens of Concord is storing their tokens in a tokenURIs mapping:

mapping(uint256 => string) private tokenURIs;

When the project creators want to add a new token to the collection, they call the setURI() method:

function setURI(uint256 _token, string memory _tokenURI)
		external
		onlyCreator
{
		tokenURIs[_token] = _tokenURI;
		emit URI(uri(_token), _token);
}

This function can actually be used for both updating the URI of an existing token or setting the URI of a new token.

One improvement you could make here is to overload this function with a second version that just uses the next token ID. You would have to track the next token ID using a counter, which would be tricky because someone would still be able to create a new token using the original method.

function setURI(string memory _tokenURI) external onlyCreator {
		uint256 tokenId = nextTokenId;
		tokenURIs[tokenId] = _tokenURI;

		++nextTokenId;

		emit URI(uri(tokenId), tokenId);
}

I’ve added a nextTokenId method here, which would be a uint. You would probably want to avoid having the other implementation (function setURI(uint256 _token, string memory _tokenURI)) be able to set a URI for a token that hasn’t been created.

You could do two things here. First, make it so the original implementation can only update the URI for an existing token by adding a require statement:

require(bytes(tokenURIs[_token]).length > 0);

This makes it so you can only update a URI for a token that already has one.

Your second option is to require that the passed in _tokenID for the original implementation of setURI is less than or equal to the nextTokenID counter.

Token Supplies

The Tokens of Concord contract doesn’t have anything that manages the supply of the tokens created. So if you wanted to create a token, but limit the supply to 10, there’s no way to enforce that in the contract. The way to do it is to create a new token (using the setURI() method described above), then create a supply of them using one of the bestowTokens methods. The creators can always come along and increase that supply by calling one of the bestowTokens methods again. This might be fine given their game play mechanics.

Selling, Withdrawing

There’s nothing in the implementation for withdrawing Eth from the contract or selling them at mint. Instead, the minting methods (described below) can only be called by allowed addresses. They have implemented royalties though.

The community has a marketplace set up on OpenSea, so I’m assuming what they do is create a new token and bestow it on the creator’s address. Then they list the item on OpenSea. This is a clever way to avoid having to write the sales logic into the contract. It also avoids having their contract be a honeypot, because it should in theory never hold any Eth.

Minting (Bestowing)

This project has four methods for minting tokens (what they call bestowing):

  1. bestowTokensUponCreator(uint256 _token, uint256 _quantity) external onlyCreator;

This mints a quantity (_quantity) of one of the tokens (_token) to the contract’s creator.

  1. bestowTokensUponCreatorMany(uint256[] memory _tokens, uint256[] memory _amounts) external onlyCreator;

Takes an array of token IDs and quantities and mints them to the creator.

  1. bestowTokens(address[] memory _to, uint256 _token, uint256 _quantity) external onlyOrdainedOrCreator;

Takes an array of addresses, a token, and a quantity and mints that quantity to all the addresses. For example, if you had a token ID of one and you passed in 10 as the quantity, every address would have a balance of 10.

  1. bestowTokensMany(address[] memory _to, uint256[] memory _tokens, uint256[] memory _amounts) external onlyOrdainedOrCreator;

Similar to the above, but for bestowing multiple tokens at a time.

Metadata