Checking and granting role in Solidity - ethereum

I'm trying to create a factory contract which is used to mint ERC721 tokens, at two different prices depending on whether it's during presale.
I'm using the Access library from OpenZeppelin, and have my contract set up with two roles (plus the default administrator role). Some lines are excluded for brevity:
import "#openzeppelin/contracts/access/AccessControl.sol";
import "./Example.sol";
contract ExampleFactory is AccessControl {
// ...
bool public ONLY_WHITELISTED = true;
uint256 public PRESALE_COST = 6700000 gwei;
uint256 public SALE_COST = 13400000 gwei;
uint256 MAX_PRESALE_MINT = 2;
uint256 MAX_LIVE_MINT = 10;
uint256 TOTAL_SUPPLY = 100;
// ...
bytes32 public constant ROLE_MINTER = keccak256("ROLE_MINTER");
bytes32 public constant ROLE_PRESALE = keccak256("ROLE_PRESALE");
// ...
constructor(address _nftAddress) {
nftAddress = _nftAddress;
// Grant the contract deployer the default admin role: it will be able
// to grant and revoke any roles
_setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
_setupRole(ROLE_MINTER, msg.sender);
_setupRole(ROLE_PRESALE, msg.sender);
}
function mint(uint256 _mintAmount, address _toAddress) public payable {
// If the user doesn't have the minter role then require payment
if (hasRole(ROLE_MINTER, msg.sender) == false) {
if (ONLY_WHITELISTED == true) {
// If still in whitelist mode then require presale role & enough value
require(hasRole(ROLE_PRESALE, msg.sender), "address is not whitelisted");
require(msg.value >= PRESALE_COST * _mintAmount, "tx value too low for quantity");
} else {
require(msg.value >= SALE_COST * _mintAmount, "tx value too low for quantity");
}
}
// Check there are enough tokens left to mint
require(canMint(_mintAmount), "remaining supply too low");
Example token = Example(nftAddress);
for (uint256 i = 0; i < _mintAmount; i++) {
token.mintTo(_toAddress);
}
}
function canMint(uint256 _mintAmount) public view returns (bool) {
if (hasRole(ROLE_MINTER, msg.sender) == false) {
if (ONLY_WHITELISTED == true) {
require((_mintAmount <= MAX_PRESALE_MINT), "max 2 tokens can be minted during presale");
} else {
require((_mintAmount <= MAX_LIVE_MINT), "max 10 tokens can be minted during sale");
}
}
Example token = Example(nftAddress);
uint256 issuedSupply = token.totalSupply();
return issuedSupply < (TOTAL_SUPPLY - _mintAmount);
}
}
There are a couple of different paths to mint:
If the user has ROLE_MINTER, they can mint without payment or limits
If ONLY_WHITELISTED is true, the transaction must have enough value for presale price, and they must have ROLE_PRESALE
If ONLY_WHITELISTED is false, anyone can mint
I've written a script to test minting:
const factoryContract = new web3Instance.eth.Contract(
FACTORY_ABI,
FACTORY_CONTRACT_ADDRESS,
{ gasLimit: '1000000' }
);
console.log('Testing mint x3 from minter role')
try {
const result = await factoryContract.methods
.mint(3, OWNER_ADDRESS)
.send({ from: OWNER_ADDRESS });
console.log(' ✅ Minted 3x. Transaction: ' + result.transactionHash);
} catch (err) {
console.log(' 🚨 Mint failed')
console.log(err)
}
Running this successfully mints 3 tokens to the factory owner. No value is attached to this call, and it's minting more than the maximum, so in order for it to be successful it has to follow the ROLE_MINTER path.
However, if I call hasRole from the same address, the result is false which doesn't make sense.
const minterHex = web3.utils.fromAscii('ROLE_MINTER')
const result = await factoryContract.methods.hasRole(minterHex, OWNER_ADDRESS).call({ from: OWNER_ADDRESS });
// result = false
If I try to run the test mint script from another address (with no roles) it failed as expected, which suggests roles are working but I'm using hasRole wrong?

const minterHex = web3.utils.fromAscii('ROLE_MINTER')
This JS snippet returns the hex representation of the ROLE_MINTER string: 0x524f4c455f4d494e544552
bytes32 public constant ROLE_MINTER = keccak256("ROLE_MINTER");
But this Solidity snippet returns the keccak256 hash of the ROLE_MINTER string: 0xaeaef46186eb59f884e36929b6d682a6ae35e1e43d8f05f058dcefb92b601461
So when you're querying the contract if the OWNER_ADDRESS has the role 0x524f4c455f4d494e544552, it returns false because this address doesn't have this role.
You can calculate the hash using the web3.utils.soliditySha3() function (docs).
const minterHash = web3.utils.soliditySha3('ROLE_MINTER');
const result = await factoryContract.methods.hasRole(minterHash, OWNER_ADDRESS).call();
Also note that the OpenZeppelin hasRole() function doesn't check for msg.sender, so you don't need to specify the caller inside the call() function. Just the 2nd argument of the hasRole() as the account that you're asking about their role.

Related

DeclarationError: Undeclared identifier. "..." is not visible at this point

I am practising this tutorial in Remix IDE - https://www.youtube.com/watch?v=_aXumgdpnPU
I saw in the Chainlink documentation that their Randomness VRF code has been changed since the development of the video.
I started replacing the parts and trying to deploy the class via Remix but it gives an error which I am not sure how to fix.
Would you be able to check what I have as code and I'll send a screenshot of the error?
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.11;
import "#chainlink/contracts/src/v0.8/ConfirmedOwner.sol";
import "#chainlink/contracts/src/v0.8/VRFV2WrapperConsumerBase.sol";
contract Lottery is
VRFV2WrapperConsumerBase,
ConfirmedOwner
{
address public owner;
address payable[] public players;
uint public lotteryId;
mapping (uint => address payable) public lotteryHistory;
event RequestSent(uint256 requestId, uint32 numWords);
event RequestFulfilled(
uint256 requestId,
uint256[] randomWords,
uint256 payment
);
struct RequestStatus {
uint256 paid; // amount paid in link
bool fulfilled; // whether the request has been successfully fulfilled
uint256[] randomWords;
}
mapping(uint256 => RequestStatus)
public s_requests; /* requestId --> requestStatus */
// past requests Id.
uint256[] public requestIds;
uint256 public lastRequestId;
// Depends on the number of requested values that you want sent to the
// fulfillRandomWords() function. Test and adjust
// this limit based on the network that you select, the size of the request,
// and the processing of the callback request in the fulfillRandomWords()
// function.
uint32 callbackGasLimit = 100000;
// The default is 3, but you can set this higher.
uint16 requestConfirmations = 3;
// For this example, retrieve 2 random values in one request.
// Cannot exceed VRFV2Wrapper.getConfig().maxNumWords.
uint32 numWords = 2;
// Address LINK - hardcoded for Goerli
address linkAddress = 0x326C977E6efc84E512bB9C30f76E30c160eD06FB;
// address WRAPPER - hardcoded for Goerli
address wrapperAddress = 0x708701a1DfF4f478de54383E49a627eD4852C816;
constructor()
ConfirmedOwner(msg.sender)
VRFV2WrapperConsumerBase(linkAddress, wrapperAddress)
{
owner = msg.sender;
lotteryId = 1;
}
function requestRandomWords()
external
onlyOwner
returns (uint256 requestId)
{
requestId = requestRandomness(
callbackGasLimit,
requestConfirmations,
numWords
);
s_requests[requestId] = RequestStatus({
paid: VRF_V2_WRAPPER.calculateRequestPrice(callbackGasLimit),
randomWords: new uint256[](0),
fulfilled: false
});
requestIds.push(requestId);
lastRequestId = requestId;
emit RequestSent(requestId, numWords);
return requestId;
}
function fulfillRandomWords(
uint256 _requestId,
uint256[] memory _randomWords
) internal override {
require(s_requests[_requestId].paid > 0, "request not found");
s_requests[_requestId].fulfilled = true;
s_requests[_requestId].randomWords = _randomWords;
emit RequestFulfilled(
_requestId,
_randomWords,
s_requests[_requestId].paid
);
payWinner();
}
function getRequestStatus(
uint256 _requestId
)
external
view
returns (uint256 paid, bool fulfilled, uint256[] memory randomWords)
{
require(s_requests[_requestId].paid > 0, "request not found");
RequestStatus memory request = s_requests[_requestId];
return (request.paid, request.fulfilled, request.randomWords);
}
/**
* Allow withdraw of Link tokens from the contract
*/
function withdrawLink() public onlyOwner {
LinkTokenInterface link = LinkTokenInterface(linkAddress);
require(
link.transfer(msg.sender, link.balanceOf(address(this))),
"Unable to transfer"
);
}
function getWinnerByLottery(uint lottery) public view returns (address payable) {
return lotteryHistory[lottery];
}
function getBalance() public view returns (uint) {
return address(this).balance;
}
function getPlayers() public view returns (address payable[] memory) {
return players;
}
function enter() public payable {
require(msg.value > .01 ether);
// address of player entering lottery
players.push(payable(msg.sender));
}
//function getRandomNumber() public view returns (uint) {
//return uint(keccak256(abi.encodePacked(owner, block.timestamp)));
//}
function pickWinner() public onlyowner {
requestRandomWords;
}
function payWinner() public {
uint index = lastRequestId % players.length;
players[index].transfer(address(this).balance);
lotteryHistory[lotteryId] = players[index];
lotteryId++;
// reset the state of the contract
players = new address payable[](0);
}
modifier onlyowner() {
require(msg.sender == owner);
_;
}
}
enter image description here

Smart Contract Method Reverted in Ganache but Worked Fine in Remix

Overview
I am working on a project for a Blockchain Developer Course, the project must be tested with Ganache because the Reviewer Team for the course will use Ganache.
The Smart Contract that I am working right now works with two different smart contracts, one is for data storage and the other is for the application purposes (user interaction).
When an user interacts with the registerOracle function the smart contract will ask for 1 ether to generate 1 oracle (this oracle will have an ID and 3 Indexes).
I tested the Smart Contract on Remix and everything works find but when I try to test the oracle generator function in Ganache it gives me a revert error.
Project URL
The project's files are in my Github's repository along with some screenshot I took showcasting the procedure to test the smart contract on Remix and the error I get on Ganache.
The code is too extend, but I will be posting here the relevant parts that are giving me the errors. The complete code can be seen on my repository.
TestingEnv.js File
const Web3 = require('web3');
const fs = require('fs').promises;
async function main () {
/**
* IGNORE FROM HERE
*/
const web3 = new Web3(new Web3.providers.WebsocketProvider("HTTP://127.0.0.1:8545"));
const appABI = (JSON.parse(await fs.readFile("./bin/appAbi.json", "utf8"))).abi;
const appBYTECODE = (JSON.parse(await fs.readFile("./bin/appByteCode.json", "utf8"))).bytecode;
const dataABI = (JSON.parse(await fs.readFile("./bin/dataAbi.json", "utf8"))).abi;
const dataBYTECODE = (JSON.parse(await fs.readFile("./bin/dataByteCode.json", "utf8"))).bytecode;
const owner = (await web3.eth.getAccounts())[0];
const appContract = await new web3.eth.Contract(appABI).deploy({data: '0x' + appBYTECODE})
.send({from: owner, gasLimit: 6000000, gasPrice: web3.utils.toWei('5', 'gwei')});
const dataContract = await new web3.eth.Contract(dataABI).deploy({data: '0x' + dataBYTECODE})
.send({from: owner, gasLimit: 6000000, gasPrice: web3.utils.toWei('5', 'gwei')});
const appAddress = appContract._address;
const dataAddress = dataContract._address;
await appContract.methods.registerDataContract(dataAddress).send({from: owner});
await dataContract.methods.registerApplicationContract(appAddress).send({from: owner});
/**
* TO HERE
*/
/**
* REVERT ERROR - STARTS
*/
const oracleFee = await web3.utils.toWei("1", "ether");
const server = (await web3.eth.getAccounts())[2];
// I AM REGISTERING 20 ORACLES
for (var i=0; i<20; i++) {
await appContract.methods.registerOracle().send({from: server, value: oracleFee});
await new Promise(resolve => setTimeout(resolve, 2000));
};
const totalOracles = await appContract.methods.getTotalOracles().call({from: server});
console.log(`Total Numbers of Oracles Registered: ${totalOracles}`);
for (var i = 0; i<totalOracles; i++) {
var indexes = await appContract.methods.getOraceIndexes(i).call({from: server});
if (i >= 0 && i <= 9) {
console.log(`Oracle 0${i} indexes: ${indexes._index1.toString()} ${indexes._index2.toString()} ${indexes._index2.toString()}`);
} else {
console.log(`Oracle ${i} indexes: ${indexes._index1.toString()} ${indexes._index2.toString()} ${indexes._index2.toString()}`);
};
};
};
main();
FlightSuretyApp.sol Contract
pragma solidity ^0.4.24;
interface iFlightSuretyData {
function registerOracle(address server, uint8 index1, uint8 index2, uint8 index3) external payable;
}
import "./SafeMath.sol";
contract FlightSuretyApp {
using SafeMath for uint256;
bool operationalStatus;
address owner;
iFlightSuretyData FlightSecuretyData;
constructor() public {
operationalStatus = true;
owner = msg.sender;
}
modifier requireOwner() {
require(msg.sender == owner, "Owner is required");
_;
}
modifier requireOperational() {
require(operationalStatus == true, "Contract is not operational");
_;
}
function registerDataContract(address dataContract) external
requireOwner {
FlightSecuretyData = iFlightSuretyData(dataContract);
}
uint256 constant ORACLE_REGISTRATION_FEE = 1 ether;
modifier requireOracleRegistrationFee() {
require(msg.value == ORACLE_REGISTRATION_FEE, "Oracle Registration Cost 1 ether");
_;
}
function registerOracle() external payable
requireOperational
requireOracleRegistrationFee {
(uint8 index1, uint8 index2, uint8 index3) = indexesThrown();
FlightSecuretyData.registerOracle.value(msg.value)(msg.sender, index1, index2, index3);
}
function indexesThrown() private view returns(uint8 _index1, uint8 _index2, uint8 _index3)
{
uint8 index1 = generateIndex1();
uint8 index2 = generateIndex2(index1);
uint8 index3 = generateIndex3(index1, index2);
return (index1, index2, index3);
}
function generateIndex1() private view returns(uint8 _index) {
uint256 mod = 10;
uint256 time = block.timestamp;
uint256 difficulty = block.difficulty;
uint8 value = uint8(SafeMath.mod(uint256(keccak256(abi.encodePacked(time, difficulty, msg.sender))), mod));
return value;
}
function generateIndex2(uint8 _index1) private view returns(uint8 _index) {
uint256 mod = 10;
uint256 time = block.timestamp;
uint256 difficulty = block.difficulty;
uint8 value = uint8(SafeMath.mod(uint256(keccak256(abi.encodePacked(time, difficulty, msg.sender))), mod));
while(value == _index1) {
time = SafeMath.add(time, 500);
difficulty = SafeMath.add(difficulty, 700);
value = uint8(SafeMath.mod(uint256(keccak256(abi.encodePacked(time, difficulty, msg.sender))), mod));
}
return value;
}
function generateIndex3(uint8 _index1, uint8 _index2) private view returns(uint8 _index) {
uint256 mod = 10;
uint256 time = block.timestamp;
uint256 difficulty = block.difficulty;
uint8 value = uint8(SafeMath.mod(uint256(keccak256(abi.encodePacked(time, difficulty, msg.sender))), mod));
while((value == _index1) || (value == _index2)) {
time = SafeMath.add(time, 500);
difficulty = SafeMath.add(difficulty, 700);
value = uint8(SafeMath.mod(uint256(keccak256(abi.encodePacked(time, difficulty, msg.sender))), mod));
}
return value;
}
}
FlightSuretyData.sol
import "./SafeMath.sol";
contract FlightSuretyData {
using SafeMath for uint256;
bool operationalStatus;
address owner;
address appContract;
constructor() public {
operationalStatus = true;
owner = msg.sender;
airlines[owner].registrationStatus = true;
}
modifier requireOwner() {
require(msg.sender == owner, "Require contract owner");
_;
}
modifier requireApplication() {
require(msg.sender == appContract, "Require application");
_;
}
modifier requireOperational() {
require(operationalStatus == true, "Contract is not operational");
_;
}
function registerApplicationContract(address application) external
requireOwner
requireOperational {
appContract = application;
}
struct oracle {
uint8 index1;
uint8 index2;
uint8 index3;
mapping(bytes32 => bool) voteState;
}
struct oracleServer {
mapping(uint256 => oracle) oracles;
uint256 numberOfOracles;
}
mapping(address => oracleServer) private oracleServers;
function registerOracle(address server, uint8 index1, uint8 index2, uint8 index3) external payable
requireOperational
requireApplication {
uint256 counter = oracleServers[server].numberOfOracles;
oracleServers[server].oracles[counter].index1 = index1;
oracleServers[server].oracles[counter].index2 = index2;
oracleServers[server].oracles[counter].index3 = index3;
counter = SafeMath.add(counter, 1);
oracleServers[server].numberOfOracles = counter;
}
}
How to Run the Project?
It is easy, just install the dependencies and run the TestingEnv.js file.
My Environment Specifications
Windows 10 Home version 21H1
Ganache version 2.5.4 (Use the Ganache Desktop Application from Ganache and not some dependencies)
Node version 11.0.0
Solidity version 0.4.24
Proof that Works on Remix
Proof that Doesn't Work on Ganache
Request
How can I solve it? I am stuck on my project because I can make the project work, I've teste others functions of my smart contracts and they are working fine, but when I try to test the oracle generator it gives an error and doesn't work.
What am I missing?

AssertionError: expected '0x0000000000000000000000000000000000000000'

I am getting this error in testing my upgradeable smart contract.
I am really stuck with this can anyone help me get out of this?
contracts/StakingContract.sol
//SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
import "#openzeppelin/contracts-upgradeable/math/SafeMathUpgradeable.sol";
import "#openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "#openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol";
import "#openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
import "#openzeppelin/contracts-upgradeable/token/ERC20/SafeERC20Upgradeable.sol";
import "#openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
import "#openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
import "#openzeppelin/contracts-upgradeable/proxy/Initializable.sol";
contract StakingContract is ERC20Upgradeable, OwnableUpgradeable, ReentrancyGuardUpgradeable {
using SafeMathUpgradeable for uint256;
using AddressUpgradeable for address;
using SafeERC20Upgradeable for IERC20Upgradeable;
event TokensStaked(address _tokenStaker, uint256 _amount, uint256 unlockTime);
event TokenUnstaked(address _tokenStaker, uint256 _amount);
// Variable that prevents _deposit method from being called 2 times
bool private locked;
// The total staked amount
uint256 public totalStaked;
mapping (address => uint256) public stakeBalances;
mapping(address => uint256) private timestamp;
function initialize(uint256 initialSupply) public initializer {
__ERC20_init("MyToken", "TKN");
_mint(owner(), initialSupply);
}
function stakeTokens(address token, uint amount) external {
stakeBalances[msg.sender] = stakeBalances[msg.sender].add(amount);
timestamp[msg.sender] = block.timestamp.add(30 days);
require(IERC20Upgradeable(token).allowance(msg.sender, address(this)) >= amount, "Check the token allowance");
require(IERC20Upgradeable(token).transferFrom(msg.sender, address(this), amount), "Transfer failed!");
totalStaked = totalStaked.add(amount);
emit TokensStaked(msg.sender, amount, timestamp[msg.sender]);
}
function unstakeTokens(address token, uint256 amount) external nonReentrant() {
require(amount <= stakeBalances[msg.sender], "Sorry! You don't have sufficient stake balance.");
require(block.timestamp >= timestamp[msg.sender], "Sorry! you cannot withdraw tokens before your stake time.");
stakeBalances[msg.sender] = stakeBalances[msg.sender].sub(amount);
require(IERC20Upgradeable(token).transfer(msg.sender, amount));
totalStaked = totalStaked.sub(amount);
emit TokenUnstaked(msg.sender, amount);
}
function ownerWithdraw(address token) external onlyOwner() nonReentrant() {
require(IERC20Upgradeable(token).transfer(owner(), totalStaked), "Transfer Failed!");
uint256 ownerStakedBalance = stakeBalances[msg.sender];
stakeBalances[msg.sender] = stakeBalances[msg.sender].sub(ownerStakedBalance);
totalStaked = totalStaked.sub(totalStaked);
}
}
test/StakingContract.test.js
const { accounts, contract, web3 } = require('#openzeppelin/test-environment');
const { expect } = require('chai');
const { TestHelper } = require('#openzeppelin/cli');
const { Contracts, ZWeb3 } = require('#openzeppelin/upgrades');
// Import utilities from Test Helpers
const { BN, expectEvent, expectRevert } = require('#openzeppelin/test-helpers');
ZWeb3.initialize(web3.currentProvider);
const Box = Contracts.getFromLocal('StakingContract');
describe('StakingContract', function () {
const [ owner, other ] = accounts;
beforeEach(async function () {
this.project = await TestHelper();
this.proxy = await this.project.createProxy(Box);
});
it('retrieve returns a value previously stored', async function () {
// Use large integer comparisons
expect(await this.proxy.methods.owner().call()).to.equal(owner);
});
});
it gives the following error:
0 passing (864ms)
1 failing
1) StakingContract
retrieve returns a value previously stored:
AssertionError: expected '0x0000000000000000000000000000000000000000' to equal '0xA05c7D52b924DceB23a766ccB1e91e67b4aCF014'
+ expected - actual
-0x0000000000000000000000000000000000000000
+0xA05c7D52b924DceB23a766ccB1e91e67b4aCF014
I have tried to fix it with other things but the still issue persists.
Thanks in advance.

Error: gas required exceeds allowance or always failing transaction - web3js

I get the following error when I try to deploy a smartcontract using web3js. But if I deploy the same contract using Truffle or geth console, deployment succeeds.
Error: gas required exceeds allowance or always failing transaction
Smart Contract solidity code:
pragma solidity >=0.4.25 <0.6.0;
contract Election {
// Model a Candidate
struct Candidate {
uint id;
string name;
uint voteCount;
}
// Store accounts that have voted
mapping(address => bool) public voters;
// voted event
event votedEvent (
uint indexed _candidateId
);
// Store Candidates
// Read Candidate
mapping(uint => Candidate) public candidates;
// Store Candidates count
uint public candidatesCount;
// Constructor
constructor() public{
addCandidate('ABC');
addCandidate('XYZ');
addCandidate('PQR');
}
function addCandidate(string memory _name) private{
candidatesCount++;
candidates[candidatesCount] = Candidate(candidatesCount, _name, 0);
}
function vote(uint _candidateId) public{
// require that they haven't voted before
require(!voters[msg.sender]);
// require a valid candidate
require(_candidateId > 0 && _candidateId <= candidatesCount);
// record that voter has voted
voters[msg.sender] = true;
// update candidate vote Count
candidates[_candidateId].voteCount ++;
// trigger voted event
emit votedEvent(_candidateId);
}
}
Following is the code, I am using to deploy the smartcontract:
const provider = new HDWalletProvider(mnemonic, nodeAccessToken, 1);
const web3 = new Web3(provider);
const contract = new web3.eth.Contract(abi);
const accounts = await web3.eth.getAccounts();
try{
const deployedContract = await contract.deploy({
data: '0x' + bytecodeData, // Adding '0x' since generated bytecodedata doesn't have '0x' prefix.
arguments: args
});
let result = await deployedContract.send({
from: accounts[0],
gasPrice: 0
})
}catch(e){}
Did anyone else face the same issue?

The actual amount of the token owner not shown as expected

I created an ERC20 SC, this is the initial supply amount 100000000000. But, when I run unit test (truffle test) to show the total balance amount of the accounts[0] and the actual amount is 99999998877, expected amount should be 100000000000. Can someone explain to me, why that's happened?
Thank you.
it("should return the total supply", function() {
return CToken.deployed().then(function(instance){
return tokenInstance.totalSupply.call();
}).then(function(result){
assert.equal(result.toNumber(), 100000000000, 'total supply is wrong');
})
});
it("should return the balance of token owner", function() {
var token;
return CToken.deployed().then(function(instance){
token = instance
return tokenInstance.balanceOf.call(accounts[0]);
}).then(function(result){
assert.equal(result.toNumber(), 99999998877, 'balance is wrong');
})
});
Contract code:
pragma solidity >=0.4.21 <0.6.0;
import "../node_modules/openzeppelin-solidity/contracts/token/ERC20/StandardToken.sol";
import "../node_modules/openzeppelin-solidity/contracts/ownership/Ownable.sol";
/**
* #title BearToken is a basic ERC20 Token
*/
contract CToken is StandardToken, Ownable{
uint256 public totalSupply;
string public name;
string public symbol;
uint32 public decimals;
/**
* #dev assign totalSupply to account creating this contract
*/
constructor() public {
symbol = "CT";
name = "CToken";
decimals = 18;
totalSupply = 100000000000;
owner = msg.sender;
balances[msg.sender] = totalSupply;
//emit Transfer(0x0, msg.sender, totalSupply);
}
}
Your total supply is 10^29 (10^11 tokens, each token has 10^18 decimal points), which is not something javascript number can safely handle. https://docs.ethers.io/ethers.js/html/notes.html#why-can-t-i-just-use-numbers
web3 returns a BN instance, don't convert it to number.
You can assert like this:
const BN = web3.utils.BN
const ten = new BN("10")
const expected = ten.pow(new BN("27"))
assert(result.eq(expected), "Result doesn't match expected value")