I'm working on a new ERC721 Crypto-Collectible project, which also means creating an Auction and Marketplace functionalities into the whole thing.

Should I just start with a basic ERC721 Contract - using the open-zeppelin library, and then write all this other functionality into it? Or is there some logistical/practical reason to keep the ERC721 Collectable contract separate from the Auction/Marketplace contract?

Here's my starting point:

contract MyCollectable is ERC721Full {

constructor (string memory _name, string memory _symbol) public
    ERC721Full(_name, _symbol) {

* Custom accessor to create a unique token
function mintUniqueTokenTo(address _to, uint256 _tokenId, string memory _tokenURI)  
    public {
        super._mint(_to, _tokenId);
        super._setTokenURI(_tokenId, _tokenURI);

I would incline to separate contracts for these reasons.

  1. If you use open-zeppelin, you begin with a certain level of quality-assurance and peer-review. That remains until you break it. From an audit/QA perspective, I think there is some value in staying with unvarnished code that is believed to be of good quality. I'd say be grateful for the well-crafted modules and "color inside the lines". There will be enough custom code to scrutinize before it goes live.
  2. An auction/marketplace contract is likely to encompass a lot of concerns. A modular approach at the outset can help keep module complexity under control. A collection of small, comphrensible contracts is better than a large confusing contract convering multiple concerns, in many cases.
  3. A monolithic contract may cost more to deploy than the block gasLimit allows. That will, in effect, force a pivot to modularization in any case. Starting off that way might help you avoid refactoring before you finish.
  4. It is admittedly a matter of style but I would let my ERC721 be an ERC721 and avoid loading up the interface with a lot of concerns that have nothing to do with the standard ERC721 interface. Not to be a purist about it, because it's perfectly valid to extend the interface. I would stop if I anticipated it would be more than a few additional functions.

These are "logistical/practical reasons" but certainly not hard rules. In general, I think it's best to save gas optimization for last. That could possibly surface good reasons to merge some of the separate modules into larger contracts.

If you have a good feel for data organization and where the gas goes (so you don't make inherently gas-guzzling functions), I find it works best to focus on readability, feature-completeness, and unit tests, then look for opportunities to optimize gas.

Hope it helps.

Not the answer you're looking for? Browse other questions tagged or ask your own question.