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?
Related
I'm new to dydx flashloans and im builidng a flashloan that should borrow 1INCH tokens but when i tested it on remix it gave an error saying that the token wasnt supported. Is my code wrong or i just cant borrow 1INCH tokens from dydx.
import "#uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol";
import "#openzeppelin/contracts/token/ERC20/IERC20.sol";
interface Structs {
struct Val {
uint256 value;
}
enum ActionType {
Deposit, // supply tokens
Withdraw, // borrow tokens
Transfer, // transfer balance between accounts
Buy, // buy an amount of some token (externally)
Sell, // sell an amount of some token (externally)
Trade, // trade tokens against another account
Liquidate, // liquidate an undercollateralized or expiring account
Vaporize, // use excess tokens to zero-out a completely negative account
Call // send arbitrary data to an address
}
enum AssetDenomination {
Wei // the amount is denominated in wei
}
enum AssetReference {
Delta // the amount is given as a delta from the current value
}
struct AssetAmount {
bool sign; // true if positive
AssetDenomination denomination;
AssetReference ref;
uint256 value;
}
struct ActionArgs {
ActionType actionType;
uint256 accountId;
AssetAmount amount;
uint256 primaryMarketId;
uint256 secondaryMarketId;
address otherAddress;
uint256 otherAccountId;
bytes data;
}
struct Info {
address owner; // The address that owns the account
uint256 number; // A nonce that allows a single address to control many accounts
}
struct Wei {
bool sign; // true if positive
uint256 value;
}
}
abstract contract DyDxPool is Structs {
function getAccountWei(Info memory account, uint256 marketId)
public
view
virtual
returns (Wei memory);
function operate(Info[] memory, ActionArgs[] memory) public virtual;
}
contract DyDxFlashLoan is Structs {
DyDxPool pool = DyDxPool(0x1E0447b19BB6EcFdAe1e4AE1694b0C3659614e4e);
address public WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
mapping(address => uint256) public currencies;
constructor() {
currencies[WETH] = 1;
}
modifier onlyPool() {
require(
msg.sender == address(pool),
"FlashLoan: could be called by DyDx pool only"
);
_;
}
function tokenToMarketId(address token) public view returns (uint256) {
uint256 marketId = currencies[token];
require(marketId != 0, "FlashLoan: Unsupported token");
return marketId - 1;
}
// the DyDx will call `callFunction(address sender, Info memory accountInfo, bytes memory data) public` after during `operate` call
function flashloan(
address token,
uint256 amount,
bytes memory data
) internal {
IERC20(token).approve(address(pool), amount + 1);
Info[] memory infos = new Info[](1);
ActionArgs[] memory args = new ActionArgs[](3);
infos[0] = Info(address(this), 0);
AssetAmount memory wamt = AssetAmount(
false,
AssetDenomination.Wei,
AssetReference.Delta,
amount
);
ActionArgs memory withdraw;
withdraw.actionType = ActionType.Withdraw;
withdraw.accountId = 0;
withdraw.amount = wamt;
withdraw.primaryMarketId = tokenToMarketId(token);
withdraw.otherAddress = address(this);
args[0] = withdraw;
ActionArgs memory call;
call.actionType = ActionType.Call;
call.accountId = 0;
call.otherAddress = address(this);
call.data = data;
args[1] = call;
ActionArgs memory deposit;
AssetAmount memory damt = AssetAmount(
true,
AssetDenomination.Wei,
AssetReference.Delta,
amount + 1
);
deposit.actionType = ActionType.Deposit;
deposit.accountId = 0;
deposit.amount = damt;
deposit.primaryMarketId = tokenToMarketId(token);
deposit.otherAddress = address(this);
args[2] = deposit;
pool.operate(infos, args);
}
}
contract Arbitrage is DyDxFlashLoan {
IUniswapV2Router02 immutable uRouter;
IUniswapV2Router02 immutable sRouter;
address public owner;
constructor(address _uRouter, address _sRouter) {
uRouter = IUniswapV2Router02(_uRouter);
sRouter = IUniswapV2Router02(_sRouter);
owner = msg.sender;
}
function executeTrade(
address _tokenA,
uint _tokensFromFlashLoan,
address _tokenB,
bool _startOnUniswap
) external {
uint balanceBefore = IERC20(_tokenA).balanceOf(address(this));
bytes memory data = abi.encode(
_startOnUniswap,
_tokenA,
_tokenB,
_tokensFromFlashLoan,
balanceBefore
);
flashloan(_tokenA, _tokensFromFlashLoan, data);
}
function callFunction(
address,
Info calldata,
bytes calldata data
) external onlyPool {
(
bool _startOnUniswap,
address _tokenA,
address _tokenB,
uint256 _tokensFromFlashLoan,
uint256 balanceBefore
) = abi.decode(data, (bool, address, address, uint256, uint256));
uint balanceAfter = IERC20(_tokenA).balanceOf(address(this));
require(
balanceAfter - balanceBefore == _tokensFromFlashLoan,
"didnt receive flash loan"
);
address[] memory tokens;
if (_startOnUniswap == true) {
tokens[0] = _tokenA;
tokens[1] = _tokenB;
swapOnUniswap(_tokensFromFlashLoan, 0, tokens);
tokens[0] = _tokenB;
tokens[1] = _tokenA;
swapOnSushiswap(
IERC20(tokens[0]).balanceOf(address(this)),
0,
tokens
);
} else {
tokens[0] = _tokenA;
tokens[1] = _tokenB;
swapOnSushiswap(_tokensFromFlashLoan, 0, tokens);
tokens[0] = _tokenB;
tokens[1] = _tokenA;
swapOnUniswap(
IERC20(tokens[0]).balanceOf(address(this)),
0,
tokens
);
}
}
function swapOnUniswap(
uint _amountIn,
uint _amountOut,
address[] memory _path
) internal {
require(
IERC20(_path[0]).approve(address(uRouter), _amountIn),
"Uniswap failed the approval"
);
uRouter.swapExactTokensForTokens(
_amountIn,
_amountOut,
_path,
address(this),
(block.timestamp + 1200)
);
}
function swapOnSushiswap(
uint _amountIn,
uint _amountOut,
address[] memory _path
) internal {
require(
IERC20(_path[0]).approve(address(sRouter), _amountIn),
"Sushiswap failed the approval"
);
uRouter.swapExactTokensForTokens(
_amountIn,
_amountOut,
_path,
address(this),
(block.timestamp + 1200)
);
}
}```
I tried to look at dydx docs but i didnt find nothing that could help me
I am working on dapp using hardhat and goerli testnet and when I run it using npx hardhat run scripts/deploy.js --network goerli, I receive this error, I do have goerli faucet eth on my wallet but still it will display that my account balance is 0:
Error: insufficient funds for intrinsic transaction cost [ See: https://links.ethers.org/v5-errors-INSUFFICIENT_FUNDS ] (error={"name":"ProviderError","_stack":"ProviderError: HttpProviderError\n at HttpProvider.request (C:\\Users\\SAIFUL\\RTW3-Week7-NFT-Marketplace\\node_modules\\hardhat\\src\\internal\\core\\providers\\http.ts:78:19)\n at LocalAccountsProvider.request (C:\\Users\\SAIFUL\\RTW3-Week7-NFT-Marketplace\\node_modules\\hardhat\\src\\internal\\core\\providers\\accounts.ts:181:36)\n at processTicksAndRejections (internal/process/task_queues.js:95:5)\n at EthersProviderWrapper.send (C:\\Users\\SAIFUL\\RTW3-Week7-NFT-Marketplace\\node_modules\\#nomiclabs\\hardhat-ethers\\src\\internal\\ethers-provider-wrapper.ts:13:20)","code":-32000,"_isProviderError":true}, method="sendTransaction", transaction=undefined, code=INSUFFICIENT_FUNDS, version=providers/5.7.2)
at Logger.makeError (C:\Users\SAIFUL\RTW3-Week7-NFT-Marketplace\node_modules\#ethersproject\logger\src.ts\index.ts:269:28)
at Logger.throwError (C:\Users\SAIFUL\RTW3-Week7-NFT-Marketplace\node_modules\#ethersproject\logger\src.ts\index.ts:281:20)
at checkError (C:\Users\SAIFUL\RTW3-Week7-NFT-Marketplace\node_modules\#ethersproject\providers\src.ts\json-rpc-provider.ts:98:16)
at C:\Users\SAIFUL\RTW3-Week7-NFT-Marketplace\node_modules\#ethersproject\providers\src.ts\json-rpc-provider.ts:265:24
at processTicksAndRejections (internal/process/task_queues.js:95:5) {
reason: 'insufficient funds for intrinsic transaction cost',
code: 'INSUFFICIENT_FUNDS',
error: ProviderError: HttpProviderError
at HttpProvider.request (C:\Users\SAIFUL\RTW3-Week7-NFT-Marketplace\node_modules\hardhat\src\internal\core\providers\http.ts:78:19)
at LocalAccountsProvider.request (C:\Users\SAIFUL\RTW3-Week7-NFT-Marketplace\node_modules\hardhat\src\internal\core\providers\accounts.ts:181:36)
at processTicksAndRejections (internal/process/task_queues.js:95:5)
at EthersProviderWrapper.send (C:\Users\SAIFUL\RTW3-Week7-NFT-Marketplace\node_modules\#nomiclabs\hardhat-ethers\src\internal\ethers-provider-wrapper.ts:13:20),
method: 'sendTransaction',
transaction: undefined
}
here is my deploy.js file everything is supposed to fine and working but error won't disappear
require("#nomiclabs/hardhat-waffle");
require("#nomiclabs/hardhat-ethers");
const fs = require('fs');
// const infuraId = fs.readFileSync(".infuraid").toString().trim() || "";
task("accounts", "Prints the list of accounts", async(taskArgs, hre) => {
const accounts = await hre.ethers.getSigners();
for (const account of accounts) {
console.log(account.address);
}
});
module.exports = {
defaultNetwork: "hardhat",
networks: {
hardhat: {
chainId: 1337
},
goerli: {
url: "https://eth-mainnet.g.alchemy.com/v2/2Hmp7mz6XEpw6Z47cCZx2vEIAYXFGYOL",
accounts: ["private key here"]
}
},
solidity: {
version: "0.8.4",
settings: {
optimizer: {
enabled: true,
runs: 200
}
}
}
};
here is my solidity contract:
//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0;
import "hardhat/console.sol";
import "#openzeppelin/contracts/utils/Counters.sol";
import "#openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "#openzeppelin/contracts/token/ERC721/ERC721.sol";
contract NFTMarketplace is ERC721URIStorage {
address payable owner;
using Counters for Counters.Counter;
Counters.Counter private _tokenIds;
Counters.Counter private _itemsSold;
uint256 listPrice = 0.01 ether;
constructor() ERC721("NFTMarketplace", "NFTM") {
owner = payable(msg.sender);
}
struct ListedToken {
uint256 tokenId;
address payable owner;
address payable seller;
uint256 price;
bool currentlyListed;
}
mapping(uint256 => ListedToken) private idToListedToken;
function updateListPrice(uint256 _listprice) public payable {
require(owner == msg.sender, "only owner can update listing price ");
listPrice = _listprice;
}
function getListPrice() public view returns (uint256) {
return listPrice;
}
function getLatestIdToListedtoken()
public
view
returns (ListedToken memory)
{
uint256 currentTokenId = _tokenIds.current();
return idToListedToken[currentTokenId];
}
function getListedForToken(uint256 tokenId) public view returns (ListedToken memory) {
return idToListedToken[tokenId];
}
function getCurrentToken() public view returns (uint256) {
return _tokenIds.current();
}
function createToken(string memory tokenURI, uint256 price) public payable returns (uint) {
require(msg.value == listPrice, "Send enough ether to list");
require(price > 0, "Make sure the price isn't negative");
_tokenIds.increment();
uint256 currentTokenId = _tokenIds.current();
_safeMint(msg.sender, currentTokenId);
_setTokenURI(currentTokenId, tokenURI);
createListedToken(currentTokenId, price);
return currentTokenId;
}
function createListedToken(uint256 tokenId, uint256 price) private {
idToListedToken[tokenId] = ListedToken(
tokenId,
payable(address(this)),
payable(msg.sender),
price,
true
);
_transfer(msg.sender, address(this), tokenId);
}
function getAllNFTs() public view returns(ListedToken[] memory) {
uint nftCount = _tokenIds.current();
ListedToken[] memory tokens = new ListedToken[](nftCount);
uint currentIndex = 0;
for(uint i=0; i<nftCount; i++) {
uint currentId = i+1;
ListedToken storage currentItem = idToListedToken[currentId];
tokens[currentIndex] = currentItem;
currentIndex += 1;
}
return tokens;
}
function getMyNFTs() public view returns (ListedToken[] memory) {
uint totalItemCount = _tokenIds.current();
uint itemCount = 0;
uint currentIndex = 0;
uint currentId;
for(uint i=0; i < totalItemCount; i++)
{
if(idToListedToken[i+1].owner == msg.sender || idToListedToken[i+1].seller == msg.sender){
itemCount += 1;
}
}
ListedToken[] memory items = new ListedToken[](itemCount);
for(uint i=0; i < totalItemCount; i++) {
if(idToListedToken[i+1].owner == msg.sender || idToListedToken[i+1].seller == msg.sender) {
currentId = i+1;
ListedToken storage currentItem = idToListedToken[currentId];
items[currentIndex] = currentItem;
currentIndex += 1;
}
}
return items;
}
function executeSale(uint256 tokenId) public payable {
uint price = idToListedToken[tokenId].price;
require(msg.value == price, "please submit the relevant NFT price in order to purchase");
address seller = idToListedToken[tokenId].seller;
idToListedToken[tokenId].currentlyListed = true;
idToListedToken[tokenId].seller = payable(msg.sender);
_itemsSold.increment();
_transfer(address(this), msg.sender, tokenId);
approve(address(this), tokenId);
payable(owner).transfer(listPrice);
payable(seller).transfer(msg.value);
}
}
I tried different networks, but I have no choice except goerli because I will use alchemy Api.
You still need to get more Goerli ETH from faucets or from your other peers.
Try that and see if it will work
Finally I switched from INFURA to ALCHEMY and following the instructions here:
https://hardhat.org/tutorial/deploying-to-a-live-network
it worked!
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.
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.
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?