Leveraging OpenZeppelin with Sound and Gigantic Steps

Greetings again @frangio,

Allow me to provide you a quick background: I programmed back at collage [1990-1992], thus I got some logic and programming foundation. Today I am embracing a new challenge :point_right: Project Highlights: to create a sound and secured ERC20 token with an associated Smart Contract in which Token holders [via API] will be able to: a) Access Documents (DocumentName, AccessKey), b) Exercise [5] Voting Rights (FinancingGateName, Vote[yes/no]) and c) View Net Profits [given by Contract Owner] (ProfitSource, Value [ETH], IssuedTimeStamp, Hash).

The Five Voting Rights will trigger the following actions:

  1. Approve the issuance [mint] of additional Tokens [stage 2] and make them available in an exchange.
  2. Approve the release [from escrow] of 100% of the funds received (ETH) from the Tokens [stage 2] to the Contract Owner.
  3. Approve the issuance [mint] of additional Tokens [stage 3] and make them available in an exchange.
  4. Approve the release [from escrow] of 30% of the funds received (ETH) from the Tokens [stage 3] to the Contract Owner.
  5. Approve the release [from escrow] of 70% of the funds received (ETH) from the Tokens [stage 3] to the Contract Owner.

Prior to developing the above described Smart Contract, I will issue an ICO [stage 1] with a conditional success threshold of 70% of tokens sold, equivalent to x number of ETH; if not met, the received funds [ETH] will be returned to the originators.

With this scenario, I believe I can leverage many of the OpenZeppelin contracts that engage with ERC20.sol, Crowdsale.Sol, Escrow.Sol, etc. I still need to do further research on Voting/Ballot Contract Samples to obtain and leverage best practices, given that I did not see any related contracts in OpenZepelin GitHub.

By reviewing openzeppelin.org and zeppelinos.org I figure that I would be able to take sound steps by leveraging a modular approach with OpenZeppelin Contracts and by deploying them via ZeppelinOS.

Therefore, here are my first set of questions:

  1. Could you guide me on how to best take advantage of a modular approach? That is, how to best interact with the existing or modified versions of OpenZeppelin contracts?
  2. Could you guide me on how to best take advantage of the Roles.sol?
  3. When deploying a contract that calls [import] other contracts, which call [import] other contracts, via ZeppelinOS, are all those called contracts bundled up and sent to the Main or Test Networks as ONE Contract?
  4. Are there any other OpenZeppelin Contracts.sol, not mentioned in this message that I should review/consider to leverage into my project?

Looking forward to reading from you soon,

Cheers!

3 Likes

Hello @jaureguino, this seems rather complex!
I know I’m not frangio but I’ll answer what I can haha

  1. Not entirely sure what is being asked here, how do the voting rights affect the users?
  2. Roles allows you to mark people who should have exclusivity for certain functionalities, like restricted access to certain functions. If you need a role set up I’d recommend using one the existing roles contracts and renaming it for each of your roles for what they need to be.
  3. If it just calls other contract, as in importing their interfaces, then it won’t be bundled. You can also link it as a library. However if the contract is not already previously deployed I’m positive the contract it will all be in the same contract.
  4. None that I’m aware of.

Let @frangio respond before going through with it all. He’ll have a better idea than I do.

Greetings again @IvanTheGreatDev, and thanks for taking a shot at these questions. Allow me to respond/elaborate further in order to provide you and @frangio with more clarity/context:

  1. No, the question is related to how can I best interact/use/modify existing OpenZeppelin contracts as I develop my ERC20 Token and Smart Contract. i.e.: a) by calling them [import] (OpenZeppelin repository) from my contract, b) by copying them (OpenZeppelin repository) into my own repository and calling them from my contract, c) by literally integrating (copying) the libraries and interfaces into one contract (script), d) So on and so forth, I just need some best practices or recommendations…
  2. Got it. So, since I am the only one Developer/Contract Owner/Minter, then I should not have to worry/use this? Or are there any other security benefits that I should be aware of?
  3. So, lets say that I copy the OpenZeppelin contracts into my own repository, and call them [import] from my contract, linking them as libraries/interfaces. Then I deploy my “master” contract for the very first time, and this “master” contract calls [import] various libraries, contracts, interfaces available in my repository, then should I presume that all the calls/links that my “master” contract makes would be bundled up and deployed as ONE contract? I am fairly new on this, so I am just trying to make sense out of all this in order to make right decisions early on.
  4. Thanks a lot!

Cheers!

  1. You should install the package through npm and, using a framework like Truffle, import the contracts with Solidity import statements. Please take a look at the “Install” and “Usage” sections in the README. Copying or integrating the source code is strongly discouraged, because you lose the security guarantees we try to build into the contracts.

  2. If you’ll be developing an ERC20 token I would recommend using our ERC20Mintable extension, which uses the MinterRole. This won’t provide security benefits but more flexibility in the future. Other than the built in roles in OpenZeppelin I would suggest to stick to Ownable for anything custom you build, because roles are a complex interface that needs to be tested correctly, and we’re still building some tools for users to create roles themselves.

  3. Every contract you define will result in a separate contract being deployed. If you define contract A is X, Y, the source code for contract A will in a sense “bundle” the source code for A, X and Y together into one contract. If you have multiple contracts in your project, each will be its own “bundle”, which would be deployed separately if you need an instance of it. But some contracts are only there to aid modularization through inheritance, and might not be deployed by themselves, such as maybe X or Y above.

Hope this helps @jaureguino!

2 Likes

Greetings @frangio !

the “Install” and “Usage” sections in README says that I should import from [openzeppelin-solidity], such as:

pragma solidity ^0.5.0;
import 'openzeppelin-solidity/contracts/token/ERC721/ERC721Full.sol';
import 'openzeppelin-solidity/contracts/token/ERC721/ERC721Mintable.sol';
contract MyNFT is ERC721Full, ERC721Mintable {
  constructor() ERC721Full("MyNFT", "MNFT") public {
  }
}

However, [https://docs.zeppelinos.org/docs/linking.html] tells me to import from [openzeppelin-eth],
such as:

pragma solidity ^0.4.24;
import "openzeppelin-eth/contracts/token/ERC721/ERC721Mintable.sol";
contract MyLinkedContract {
  ERC721Mintable private _token;
  function setToken(ERC721Mintable token) external {
    require(token != address(0));
    _token = token;
  }
} 

and [https://docs.zeppelinos.org/docs/deploying.html] tells me to import from [zos-lib] such as:

pragma solidity ^0.4.24;
import "zos-lib/contracts/Initializable.sol";
contract MyContract is Initializable {
  uint256 public x;
  string public s;
  function initialize(uint256 _x, string _s) initializer public {
    x = _x;
    s = _s;
  }
}

However, I noticed that the ERC20Mintable extension found in [openzeppelin-eth/contracts/token/ERC20/] also calls/imports [zos-lib/contracts/Initializable.sol]. So, I presume that the Initializable.sol contract will always be there…

Q. Which library should I use to develop secured and upgradeable contracts?

If you’re developing upgradeable contracts you should be using openzeppelin-eth. Prioritize the guides in docs.zeppelinos.org, they are made specifically for your use case.

1 Like

Greetings @frangio & @elopio, I hope you all are back safe from EthDenver,

As described at the beginning of this topic, this project´s scope includes both the creation of a token and a smart contact that would allow token holders to vote on five (5) different ballot events at different future periods.

While doing further research I came across with :point_right: https://medium.com/validitylabs/how-to-vote-safely-with-an-erc20-token-518adadbf923 which has led me to believe that I would need an ERC721 instead of the ERC20 in order to avoid:

  1. Double voting per Token.
  2. Having to disable Token trading during voting periods.

Before I re-write the token creation contact I was working on (based on ERC20 logic), I have the following questions:

  1. Could I use the ERC721 logic and still treat each Token as having equal weight/value as if it was fungible?
  2. Do I have to create a logic that generates a unique tokenId each time I call the mintWithTokenURI function? or Did I miss something and such unique tokenId is already provided somewhere in the ERC721Metadata.sol logic?
  3. Can I assign a single and universal “image” to each ABC_Token such as tokenURI = http://[abc].com/images/ABC_Token_Image.png ?

g.a.

1 Like

I’m not sure going with ERC721 is necessarily the best way to go: the questions you arise regarding URIs reflect this in a way.

What you could do is, instead of pausing token transfers during the voting period, use something like our ERC20Snapshot, which will let you store the balances of all accounts at any given point in time, and then use those to calculate voting power.

Keep in mind though ERC0Snapshot is under development at the moment and is not yet part of a release, though it will be included in version 2.2 of OpenZeppelin, in about two weeks.

Thanks @nventuro for your kind response,

Do you have an additional technical reason why you would advise against taking the ERC721 approach?

I would not mind assigning the same universal “image” value into tokenURI to all tokens, which in fact reinforces that each token has the same design/value/weight; and if we add that each tokenId is unique and distinguishable (sort of a certificate number) and that they are also whole, in other words, there are no decimals, it would “look and work” exactly as intended :point_right: a “voting-right” bearer share.

Considering that a snapshot of the balances can be taken via ERC20Snapshot, then only the token holders at that snapshot moment [second] would be able to exercise their vote, and the general intention is that the token holder could still trade their token and/or exercise their vote during the voting period [one week]. The possibility to record the tokenId used during the ballot, would allow me to code the means to prevent its current or future holder (whomever) exercise that right again with the same tokenId. It is expected that a buyer of a token during the voting period may or may not be able to exercise their vote, based on the right exercised or not by the previous owner.

Since the ERC20Snapshot is still under development and I must go live with this token by March 1st, would you have any other technical or logical rationale for me to consider ERC721 a terrible choice?

I look forward to reading your response and thanks again for your contribution,

Dear @frangio or @elopio, do you guys have any other thoughts on this subject?

g.a.

If what you are modelling is closer to paper ballots, then yes, ERC721 may be the way to go. Keep in mind though that, like real life paper ballots, handling multiple of these may be difficult and annoying (e.g. a user votes using their 50 ballots).

Regarding the code itself, you could simply use ERC721, leaving out the ERC721Metadata (optional) extension, and remove the need to deal with tokenURIs that way.

1 Like

Dear @frangio,

While reading https://docs.zeppelinos.org/docs/writing_contracts.html I found what I believe could be a mistake. Could you please verify/confirm/correct my understanding on this section?

After finishing the reading I realized that I may not want to do the Token upgradeable. But will do so with the associated Smart Contract that will handle the three modules mentioned at the beginning of this topic. Therefore I am committed to use Zepkit (which I already installed) to test and deploy the whole Web3 Dapp with its Token and associated Smart Contract.

For the reasons explained during my last two posts, I have leveraged the ERC721MetadataMintable.sol and followed your previous advice and put together the following TFF_Token.sol contract:

pragma solidity ^0.5.0;

import "openzeppelin-eth/contracts/token/ERC721/ERC721Metadata.sol";
import "openzeppelin-eth/contracts/access/roles/MinterRole.sol";
import "openzeppelin-eth/contracts/ownership/Ownable.sol";
import "zos-lib/contracts/Initializable.sol";

/**
 * @title TFF_Token http://estudios-amazonia.com/TheFaustFlick_WP.pdf
 * @dev ERC721 minting logic with metadata, leveraging ZeppelinOS EVM
 * @dev ERC721 facilitates preventing: a) double-voting per token and
 * b) disabling trading during voting periods.
 */

contract TFF_Token is Initializable, ERC721, ERC721Metadata, MinterRole, Ownable {

  address private Owner;
  string private Name;
  string private Symbol;
  string private TokenURI;
  uint256 private TokenId;
  uint8[4] private MintStage;
  uint256[4] private TokensToMint;

  function initialize(address sender) public initializer {
    require(ERC721._hasBeenInitialized());
    require(ERC721Metadata._hasBeenInitialized());
    Name = "TFF_Token";
    Symbol = "TFF";
    ERC721.initialize();
    ERC721Metadata.initialize(Name, Symbol);
    MinterRole.initialize(sender);
    Ownable.initialize(sender);
    Owner = sender;
    TokenURI = "http://thefaustflick.com/images/TFF_Token.png";
    TokenId = 1;
    for (uint8 counter = 0; counter <= 3; counter++) { MintStage[counter] = 0; }
    TokensToMint[0] = 500000;
    TokensToMint[1] = 3000000;
    TokensToMint[2] = 3000000;
    TokensToMint[3] = 3500000;
    Mint_TFF(0);
  }

  /**
   * @dev TFF Minter function * Warning * Review White Paper and get last
   * TokenId to figure out next logically correct Mint_TFF(_Stage) value
   */
  function Mint_TFF(uint8 _Stage) public onlyMinter returns (bool) {
    require(MintStage[_Stage] == 0);
    uint256 tokensToMint = TokensToMint[_Stage];
    for (uint256 counter = 1; counter <= tokensToMint; counter++) {
      _mint(Owner, TokenId);
      _setTokenURI(TokenId, TokenURI);
      TokenId = TokenId.add(1);
    }
    MintStage[_Stage] = 1;
    return true;
  }

  /**
   * @dev Gets Last TokenId function * Useful to determine how many TFF Tokens
   * have been minted. Helps figure the next logically correct Mint_TFF() Value
   */
  function GetLastTokenId() public view returns (uint256) {
      return TokenId;
  }

  function GetTokenURI() public view returns (string memory) {
      return TokenURI;
  }

  uint256[50] private ______gap;
}

Could you or anyone from the Zeppelin Team be able to review/confirm/validate/correct some of assumptions I made in the following sections:

  1. import [Do I call all the appropriate imports?]
  2. function initialize [Are the require , the [ X ].initialize and the rest of the logic correct]
  3. function Mint_TFF [Is the logic correct? Did I miss something?]
  4. When deploying via ZeppelinOS, I would have to use the following command line:
    $ zos create ./contracts/TFF_Token --init initialize --args <address> ?

Also, when trying to deploy with $ npx zos add ./contracts/TFF_token it is calling an older version of the TFF_Token.sol file that had an array definition error, but has been fixed since.

It seems as when trying to deploy the bugged version it stored the TFF_Token.sol in some sort of cache memory, see image :point_down:. I even exited the command prompt and entered back again and gave me the same error.

  1. How do I clear the TFF_Token.sol from cache?

Thanks in advance for all the great work that you guys are doing at Zeppelin; I look forward to hearing from you soon.

g.a.

1 Like

Hey Jaureguino! I’m gonna tag @spalladino because he might know more about your ZOS “caching” issue.

  1. Your imports are fine

For 3, are you minting ERC721s that all have the same metadata?

1 Like

Do you mean by metadata exclusively :point_down:?

    Name = "TFF_Token";
    Symbol = "TFF";

because these above will be the same for all tokens.

Or does it also include :point_down:?

  string private TokenURI;
  uint256 private TokenId;

because the TokenURI will be the same but the TokenId will be unique…

g.a.

I meant the latter, I see :+1:

1 Like

Hello Jaureguino! So after looking for a bit I saw that the error is actually in a different file called ABC_Token.sol. zos add actually runs truffle compile in the package so make sure there's no other errors! Or if you don't need that file then you can get rid of it as well

Try that please and let me know :smile:

For 4 it should be zos create ./contracts/TFF_Token --init initialize --args "addresshere" if I remember correctly.

:man_facepalming:t2:

What an eye!, I guess I should read carefully and entirely the error message. Let me correct that and I will keep you posted…

1 Like

Just out of curiosity, what made you decide not to make the token itself upgradeable?

Greetings @nventuro,

What I understood from the reading, and please do correct me if I am mistaking, is that as it relates to a Token contract, what would be upgradable/modifiable, in my specific case are the metadata variables Name, Symbol, TokenURI and TokenID, and the “minting tools” MintStage[0] and TokensToMint[0], none of which I would want to change in the future since, based on our business logic (see our whitepaper :point_right: http://estudios-amazonia.com/TheFaustFlick_WP.pdf), I consider them to be quite sound.

Since the foundation of the TFF_Token.sol relies on /openzeppelin-eth/contracts/ and /zos-lib/, I take for granted that I am well covered by you guys for the rest.

I could make the token contract upgradeable if I wanted to get fancy, but it would only be for the hype of it, and tedious for me to parse all those variables via command line. I am not a professional developer, nor I intend to be; I am a pragmatic businessman, so I am very conscientious on where I invest my time.

I need to get this token and the associated Smart Contract done the fastest, the easiest and safest way and then focus all my attention on what matters the most to me: sell & deliver #TheFaustFlick project.

Cheers!

g.a.

2 Likes

Hey jaureguino! You’re almost there!

I think this issue arises from your initialize function being faulty, you require the contracts you inherit to be initialized before you initialize them. I would check https://github.com/OpenZeppelin/openzeppelin-eth/blob/master/contracts/token/ERC721/StandaloneERC721.sol for an example of how you should model your initialize function.

Hopefully that helps!

1 Like

Thanks a lot @IvanTheGreatDev,

I revamped and significantly simplified TFF_Token.sol by leveraging https://github.com/OpenZeppelin/openzeppelin-solidity#install and https://docs.openzeppelin.org/docs/get-started.html

The following is the latest contract

pragma solidity ^0.5.0;

import 'openzeppelin-solidity/contracts/token/ERC721/ERC721Full.sol';
import 'openzeppelin-solidity/contracts/token/ERC721/ERC721MetadataMintable.sol';
import 'openzeppelin-solidity/contracts/ownership/Ownable.sol';

/**
 * @title TFF_Token http://estudios-amazonia.com/TheFaustFlick_WP.pdf
 * @dev ERC721 metadata minting logic leveraging openzeppelin-solidity v2.1.2
 * @dev ERC721 facilitates preventing: a) double-voting per token and
 * b) disabling trading during voting periods.
 */

contract TFF_Token is ERC721Full, ERC721MetadataMintable, Ownable {

  address private Owner = 0x8045B92162Bb607454f8CF4CC44CBD9dff518495;
  string private Name = "TFF_Token";
  string private Symbol = "TFF";
  string private TokenURI = "http://thefaustflick.com/images/TFF_Token.png";
  uint256 private TokenId;
  uint8[4] private MintStage;
  uint256[4] private TokensToMint;

  // Constructor
  constructor() ERC721Full(Name, Symbol) public {
    TokenId = 1;
    for (uint8 counter = 0; counter <= 3; counter++) { MintStage[counter] = 0; }
    TokensToMint[0] = 500000;
    TokensToMint[1] = 3000000;
    TokensToMint[2] = 3000000;
    TokensToMint[3] = 3500000;
    Mint_TFF(0);
  }

  /**
   * @dev TFF Minter function * Warning * Review White Paper * Get last
   * TokenId to figure out next logically correct Mint_TFF(_Stage) value
   */
  function Mint_TFF(uint8 _Stage) public onlyOwner returns (bool) {
    if (MintStage[_Stage] == 0) {
      uint256 tokensToMint = TokensToMint[_Stage];
      for (uint256 counter = 1; counter <= tokensToMint; counter++) {
        mintWithTokenURI(Owner, TokenId, TokenURI);
        TokenId = TokenId.add(1);
      }
      MintStage[_Stage] = 1;
      return true;
    }
  }

  /**
   * @dev Gets Last TokenId function * Useful to determine how many TFF Tokens
   * have been minted. Helps figure the next logically correct Mint_TFF() Value
   */
  function GetLastTokenId() public view returns (uint256) {
      return TokenId;
  }

  function GetTokenURI() public view returns (string memory) {
      return TokenURI;
  }
}

When the truffle-config.js file was :point_down:
old%20truffle

And I tried truffle migrate, I got :point_down:
old%20error

So, I changed the truffle-config.js file to :point_down:
new%20taruffle

and I got :point_down:
new%20error

Any thoughts or recommendations on gas and gasPrice settings on the truffle-config.js or
should I adjust the GAS LIMIT and GAS PRICE on the ganache configuration here :point_down:

Should the GAS LIMIT be changed to 350000 and the GAS PRICE to 21?