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,
0x..C9E3
address, thus whitelisting it as a client0x..C9E3
again by calling addClientAssuming you are using a version of this Roles.sol library, if we look at the implementation of
add
we'll notice two checks: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.