Truffle Gas Price Rinkeby – Why Smart Contract Rinkeby Transactions Are Failing

gas-pricerinkebytruffle

I deployed a smart contract in rinkeby network, and I have no problem deploying the contract. But when I try to run method addClient() it always fails (Seems to be gas issue).
I tried changing gas price with metamask up to 2-3 ethers but still it failed.

https://rinkeby.etherscan.io/tx/0xd91fdbbf350511591685fcfd37d5d992a50c47f1eb36fc2200aca83d8f08cf2c

Is my method too hungry in gas ? (I don't think so)

pragma solidity ^0.4.24;

/**
 * @title Roles
 * @dev Library for managing addresses assigned to a Role.
 */
library Roles {
  struct Role {
    mapping (address => bool) bearer;
  }

  /**
   * @dev give an account access to this role
   */
  function add(Role storage role, address account) internal {
    require(account != address(0));
    require(!has(role, account));

    role.bearer[account] = true;
  }

  /**
   * @dev remove an account's access to this role
   */
  function remove(Role storage role, address account) internal {
    require(account != address(0));
    require(has(role, account));
    role.bearer[account] = false;
  }

  /**
   * @dev check if an account has this role
   * @return bool
   */
  function has(Role storage role, address account)
    internal
    view
    returns (bool)
  {
    require(account != address(0));
    return role.bearer[account];
  }
}

there is a call at the constructor level that add contract owner as a client:

contract ClientRole {
  using Roles for Roles.Role;

  // Define 2 events, one for Adding, and other for Removing
  event ClientAdded(address indexed account);
  event ClientRemoved(address indexed account);

  // Define a struct 'clients' by inheriting from 'Roles' library, struct Role
  Roles.Role private clients;

  // In the constructor make the address that deploys this contract the 1st Client
  constructor() public {
    _addClient(msg.sender);
  }

  // Define a modifier that checks to see if msg.sender has the appropriate role
  modifier onlyClient() {
    require(isClient(msg.sender));
    _;
  }

  // Define a function 'isClient' to check this role
  function isClient(address account) public view returns (bool) {
    return clients.has(account);
  }

  // Define a function 'addClient' that adds this role
  function addClient(address account) public onlyClient {
    _addClient(account);
  }

  // Define a function 'removeClient' that adds this role
  function removeClient(address account) public onlyClient {
    _removeClient(account);
  }

  // Define a function 'renounceClient' to renounce this role
  function renounceClient() public {
    _removeClient(msg.sender);
  }

  // Define an internal function '_addClient' to add this role, called by 'addClient'
  function _addClient(address account) internal {
    clients.add(account);
    emit ClientAdded(account);
  }

  // Define an internal function '_removeClient' to remove this role, called by 'removeClient'
  function _removeClient(address account) internal {
    clients.remove(account);
    emit ClientRemoved(account);
  }
}

Then I use a contract called Verification.sol than inherits Client.sol and I call the method addClient() from Verification.sol contract.

pragma solidity ^0.4.24;

import "../coffeeaccesscontrol/PeriRole.sol";
import "../coffeeaccesscontrol/ClientRole.sol";
import "../coffeecore/Ownable.sol";

// Define a contract 'verification'
contract Verification is ClientRole, PeriRole {

  // Define 'owner'
  address owner;

  // Define a variable with number of transaction
  uint  ct;

  // Define a public mapping 'items' that maps the ct to an Item.
  mapping (uint => Item) items;

  // Define a public mapping 'itemsHistory' that maps the ct to an array of TxHash, 
  // that track its journey through the verification -- to be sent from DApp.
  mapping (uint => string[]) itemsHistory;
  
  // Define enum 'State' with the following values:
  enum State 
  { 
    Rendez,  // 0
    PeriProcessed,  // 1
    ClientProcessed,  // 2
    bothPeri  // 3
  }

  State constant defaultState = State.Rendez;

  // Define enum 'VerifClient' with the following values:
  enum VeriClient 
  { 
    noShow,  // 0
    bad,  // 1
    good  // 2
  }

  // Define enum 'VeriPhotoPeri' with the following values:
  enum VeriPhotoPeri 
  { 
    noShow,  // 0
    bad,  // 1
    good  // 2
  }

  // Define enum 'VeriPeri' with the following values:
  enum VeriPeri
  { 
    noShow,  // 0
    bad,  // 1
    good  // 2
  }

  VeriClient constant defaultVeriClient = VeriClient.noShow;
  VeriPhotoPeri constant defaultVeriPhotoPeri = VeriPhotoPeri.noShow;
  VeriPeri constant defaultVeriPeri = VeriPeri.noShow;

  // Define a struct 'Item' with the following fields:
  struct Item {
    uint    ct; // number of verifications in the blockchain
    address ownerID;  // Metamask-Ethereum address of the current owner as the product moves through 8 stages
    address clientID; // Metamask-Ethereum address of the client
    address    periID;  // Metamask-Ethereum address of the peripateticienne
    uint    dateRdv;  // date in digits
    State   itemState;  // Product State as represented in the enum above
    VeriClient itemVeriClient;
    VeriPhotoPeri itemVeriPhotoPeri;
    VeriPeri itemVeriPeri;
  }

(more methods -------------------------------------)
}

code version:

Truffle v5.4.24 (core: 5.4.24)
Solidity - 0.4.24 (solc-js)
Node v12.0.0
Web3.js v1.5.3

truffle-config.js

rinkeby: {
  provider: () => new HDWalletProvider(mnemonic, `https://rinkeby.infura.io/v3/${infuraKey}`),
    network_id: 4,       // rinkeby's id
    gas: 4500000,      // 4500000  // rinkeby has a lower block limit than mainnet
    gasPrice: 10000000000, // 10000000000 
    from: publicAddress // contract owner in rinkeby: 0x08c1fa540e53c998B5572dA9dd9785f4Ff052704
},

Thanks for your help !

Best Answer

The contracts are correct, it's just a small details you haven't considered. To recap,

  1. You are deploying the contract from the 0x..C9E3 address, thus whitelisting it as a client
  2. You are trying to whitelist 0x..C9E3 again by calling addClient

Assuming you are using a version of this Roles.sol library, if we look at the implementation of add we'll notice two checks:

  require(account != address(0)); // is my address valid
  require(!has(role, account)); // is my account new, no client role yet

The second transaction you're doing, trying to add 0x..C9E3 will fail due to this second condition not being met.

Solution: Try adding another client, with another address. Should work fine!

P.S. When adding more details, please edit your initial post to keep it organized. Also, try looking into more recent openzeppelin access control libraries, always good to keep up with the latest.

Example of bad tx | please call addClient with another address as parameter to make this work.

enter image description here

Related Topic