Unable to get value from another contract - ethereum

I am facing a head scratching issue. I have created two contracts UserRole which has a map of username to a role and a Base contract that has a modifier which checks if the role is < 10.
So I deploy the first UserRole contract first and then I called the set function with the parameters _username = "jamesbond" and _role=7.
After the transaction is mined I call the getRole passing _username = "jamesbond" and I get back 7.
Now I deploy Base and pass the address of the the UserRole contract that I deployed earlier. I call the testModifier function and I pass it _username = "jamesbond". I expect that I get the value 7 back.
I tested this on http://remix.ethereum.org first. Then I tried it on quorum and parity. On remix it works as expected but on both quorum and parity I do not get any values back.
I am not sure what I am doing wrong.
pragma solidity ^0.4.24;
contract UserRole {
address owner;
mapping (string => uint8) userRoles;
constructor()
public
{
owner = msg.sender;
}
function set(string _username, uint8 _role)
public
returns (bool sucesss)
{
userRoles[_username] = _role;
return true;
}
function getRole(string _username)
public
view
returns (uint8 _role)
{
return userRoles[_username];
}
}
contract Base {
address owner;
UserRole userRole;
address public userRoleAddress;
constructor(address _t)
public
{
owner = msg.sender;
userRoleAddress = _t;
userRole = UserRole(_t);
}
modifier isAuthorized(string _username) {
uint8 role = 5;
require(role < 10);
_;
}
function testModifier(string _username)
public
isAuthorized(_username)
view
returns (uint8 result)
{
return userRole.getRole(_username);
}
}

I have faced similar issues when compiling the contract with Remix.
The solution is as follows:
Install solcjs using
npm install -g solc
It will provide executable binary of solcjs.
2.Create two file named UserRole.sol and Base.sol and copy the respective code in the files. Compile both the contracts using solcjs compiler (binary installed in your system.).
solcjs -o output --bin --abi UserRole.sol
solcjs -o output --bin --abi Base.sol
It will produce two abi and two bin file inside output folder.
Use these abi and bin of respective contracts to create similar script like web3deploy and deploy them in quorum or parity.
This will work.

Related

Constructor of factory-like smart contract keeps reverting

I'm working with 2 contracts, one that manages users bets for a specific football match and another (factory style) that generates these individual contracts to be managed separately.
The Generator contract is this:
pragma solidity ^0.8.0;
contract Generator {
address immutable public owner;
string public matchGenerated;
Management[] public bettingHistory;
constructor(string memory _league, string memory _match) {
owner = msg.sender;
matchGenerated = string(abi.encodePacked(_league, "_", _match));
Management newBetContract = new Management(address(this));
bettingHistory.push(newBetContract);
}
}
The Management contract receives data from the Generator to define the manager and the match values, as follows (short version):
contract Management {
address payable immutable public manager;
string public matchDispute;
Generator generator;
constructor(address _contract) {
generator = Generator(_contract);
manager = payable(generator.owner());
matchDispute = generator.matchGenerated();
}
Once I try to deploy the Generator contract in Remix it results in a reverting error. If I remove the instance of the generator in the Management contract and repeat the constructor arguments in both contracts it works, but I want to avoid the redundancy and get the values already stored in the Generator variables automatically.
To implement the above using interface, your solution will be like:
Generator.sol file
pragma solidity ^0.8.0;
contract Generator {
address immutable public owner;
string public matchGenerated;
Management[] public bettingHistory;
constructor(string memory _league, string memory _match) {
owner = msg.sender;
matchGenerated = string(abi.encodePacked(_league, "_", _match));
Management newBetContract = new Management(address(this));
bettingHistory.push(newBetContract);
}
}
IGenerator.sol file
pragma solidity ^0.8.0;
interface IGenerator{
function owner() external view returns (address);
function matchGenerated() external view returns (string)
}
Management.sol file
pragma solidity ^0.8.0;
import "IGenerator.sol";
contract Management {
address payable immutable public manager;
string public matchDispute;
constructor(address _contract) {
manager = payable(IGenerator(_contract).owner());
matchDispute = IGenerator(_contract).matchGenerated();
}
As you can see above, I have exposed all functions of Generator that management needed and separated the definitions of the two. Try that and see if it works and if not let's know what the new error messages are.

Solidity function implementing `public onlyOwner` cannot be called even by the owner

I am following along the documentation here: https://docs.alchemyapi.io/alchemy/tutorials/how-to-create-an-nft/how-to-mint-a-nft. And have a smart contract of form:
pragma solidity ^0.8.0;
import "#openzeppelin/contracts/token/ERC721/ERC721.sol";
import "#openzeppelin/contracts/utils/Counters.sol";
import "#openzeppelin/contracts/access/Ownable.sol";
contract NFTA is ERC721, Ownable {
using Counters for Counters.Counter;
Counters.Counter public _tokenIds;
mapping (uint256 => string) public _tokenURIs;
mapping(string => uint8) public hashes;
constructor() public ERC721("NFTA", "NFT") {}
function mintNFT(address recipient, string memory tokenURI)
public onlyOwner
returns (uint256)
{
_tokenIds.increment();
uint256 newItemId = _tokenIds.current();
_mint(recipient, newItemId);
_setTokenURI(newItemId, tokenURI);
return newItemId;
}
/**
* #dev Sets `_tokenURI` as the tokenURI of `tokenId`.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function _setTokenURI(uint256 tokenId, string memory _tokenURI) internal virtual {
require(_exists(tokenId), "ERC721URIStorage: URI set of nonexistent token");
_tokenURIs[tokenId] = _tokenURI;
}
}
When I attempt to estimate the gas cost of minting with this:
const MY_PUBLIC_KEY = '..'
const MY_PRIVATE_KEY = '..'
const ALCHEMY = {
http: '',
websocket:'',
}
const { createAlchemyWeb3 } = require("#alch/alchemy-web3");
const web3 = createAlchemyWeb3(ALCHEMY.http);
const NFTA = require("../artifacts/contracts/OpenSea.sol/NFTA.json");
const address_a = '0x...';
const nft_A = new web3.eth.Contract(NFTA.abi, address_a);
async function mint({ tokenURI, run }){
const nonce = await web3.eth.getTransactionCount(MY_PUBLIC_KEY, 'latest');
const fn = nft_A.methods.mintNFT(MY_PUBLIC_KEY, '')
console.log( 'fn: ', fn.estimateGas() )
}
mint({ tokenURI: '', run: true })
I receive error:
(node:29262) UnhandledPromiseRejectionWarning: Error: Returned error: execution reverted: Ownable: caller is not the owner
Presumably because mintNFT is public onlyOwner. However, when I check Etherscan, the From field is the same as MY_PUBLIC_KEY, and I'm not sure what else can be done to sign the transaction as from MY_PUBLIC_KEY. The easy way to solve this is to remove the onlyOwner from function mintNFT, and everything runs as expected. But suppose we want to keep onlyOwner, how would I sign the transaction beyond what is already written above.
Note I'm using hardHat to compile the contracts and deploying them. That is:
npx hardhat compile
npx hardhat run scripts/deploy.js
=============================================
addendum
The exact code given by alchemy to deploy the mint is:
async function mintNFT(tokenURI) {
const nonce = await web3.eth.getTransactionCount(PUBLIC_KEY, 'latest'); //get latest nonce
//the transaction
const tx = {
'from': PUBLIC_KEY,
'to': contractAddress,
'nonce': nonce,
'gas': 500000,
'data': nftContract.methods.mintNFT(PUBLIC_KEY, tokenURI).encodeABI()
};
Note in the transaction the from field is PUBLIC_KEY, the same PUBLIC_KEY that deployed the contract, and in this case the nftContract has public onlyOwner specified. This is exactly what I have done. So conceptually who owns this NFT code? On etherscan is it the to address ( the contract address ), or the from address, which is my public key, the address that deployed the contract, and the one that is calling mint, which is now failing with caller is not the owner error.
Search the internet, I see others have encountered this problem here: https://ethereum.stackexchange.com/questions/94114/erc721-testing-transferfrom, for Truffle you can specify the caller with extra field:
await nft.transferFrom(accounts[0], accounts[1], 1, { from: accounts[1] })
Extra parameters is not an option here because I'm using hardhat.
OpenZeppelin's Ownable.sol defines the default owner value as the contract deployer. You can later change it by calling transferOwnership() or renounce the owner (i.e. set to 0x0) by calling renounceOwnership().
The onlyOwner modifier reverts the transaction if it's not sent by the current owner. (see the code)
So you need to call the mintNFT() function from the same address that deployed the contract, because that's the current owner. Or you can change the owner first by calling transferOwnership() (from the current owner address).
Removing the onlyOwner modifier from the mintNFT() function would allow anyone to call the function.
Answering this for anyone else who stumbles across the issue while using the Alchemy tutorial:
In the tutorial, it says to init the contract in your mint method like this:
const contract = require("../artifacts/contracts/MyNFT.sol/MyNFT.json");
const contractAddress = "0x81c587EB0fE773404c42c1d2666b5f557C470eED";
const nftContract = new web3.eth.Contract(contract.abi, contractAddress);
However, if you attempt to call estimateGas() or encodeABI() it will fail with the onlyOwner error.
The solution is to change the third line to:
const nftContract = new web3.eth.Contract(contract.abi, contractAddress, {
from: PUBLIC_KEY
});
This will set a default value of "From" so that when you call estimateGas() on a mint function marked onlyOwner, it will be able to use that from field to see that its the owner calling the estimateGas.
Took forever to figure this out.
I finally figured it out, the contract does not initialize the way I deploy it. So you have to initialize it after deployment.

Solidity public variable getter call doesn't return

I have deployed a very basic solidity contract (pragma solidity^0.4.0) to Rinkeby. When I call the getters on my state variables, nothing is returned and I do not understand why. To compile the contract I'm using "solc": "^0.4.25"
Using Remix to test, the call to the getter works locally without issue. However, the call doesn't work on my contract on Rinkeby.
Here is my solidity code...
pragma solidity^0.4.0;
contract Contest {
address public manager;
uint public submissionCost;
uint8 public votesPerSubmission;
constructor (uint _submissionCost, uint8 _votesPerSubmission) public {
manager = msg.sender;
submissionCost = _submissionCost;
votesPerSubmission = _votesPerSubmission;
}
modifier restricted() {
require(msg.sender == manager, "Not authorized.");
_;
}
function adjustSubmissionCost(uint32 newCost) public restricted {
submissionCost = newCost;
}
function adjustVotesPerSubmission(uint8 newVotes) public restricted {
votesPerSubmission = newVotes;
}
}
The address of the deployed contract on Rinkeby is
0xacBd19113e0D8122E18DF48A320b635fB5D7Cdd0
https://rinkeby.etherscan.io/address/0xacbd19113e0d8122e18df48a320b635fb5d7cdd0
When calling any of the public variables from Remix, I expect a return value, however I only receive the message call to Contest.manager in the Remix console and nothing more.
I get the same behavior in my web3 dapp, with zero return when I try...
await Contest.methods.manager().call()

calling method failed in another contract when create an instance by new

i'm trying to use a contract instance as a variable of another contract, such as the example below.
pragma solidity ^0.4.23;
contract basic {
uint num1 = 10;
function getNum1() public view returns(uint) {
return num1;
}
function setNum1(uint _num) public returns(uint) {
num1 = _num;
}
}
contract parent {
uint public num2;
basic public b;
constructor() public {
b = new basic();
num2 = 20;
}
function getNum1() public constant returns(uint) {
return b.getNum1();
}
}
while when i test the contract in remix and truffle , it worked well.
enter image description here
but util i deployed the contract "parent" on my private network, parent.getNum1() returned '0' instead of '10' as supposed.
further more, i tried other type of constructors such as take an address of 'basic' as a parameter, it didn't work as well.
i also tried some contracts thats takes another contract instance as a variable, they all didn't work well on private network.
does anybody ever meet this problem? help!!!
coming to close the question now !
i deployed my contract on ropsten test network, and the contract worked well.
it seems that my private network didn't support the usage of calling from another contract. anybody interested can have a try to see.

How to get keccak256 hash in Solidity

I just got started with solidity, I have used truffle to compile and deploy the code to ganache, everything works as I expect, I can call the other functions in the code, but there are certain functions that only the owner can access, the code appears to use keccak256 to get back the address calling the function and determine if the caller address is allowed, I have tried to hash my eth address using this website:
https://emn178.github.io/online-tools/keccak_256.html
and then add the hash to the code before recompiling again, but calling the owner function still throws this error:
"Error: VM Exception while processing transaction: revert"
What am i doing wrong ?
Here's the code with the original hash.
modifier onlyOwner(){
address _customerAddress = msg.sender;
require(owners[keccak256(_customerAddress)]);
_;
}
// owners list
mapping(bytes32 => bool) public owners;
function PetShop()
public
{
// add owners here
owners[0x66e62cf7a807daaf3e42f7af3befe7b2416a79ba5348820245a69fe701f80eb4] = true;
}
/*---------- Owner ONLY FUNCTIONS ----------*/
function disableDogs()
onlyOwner()
public
{
onlyDogs = false;
}
/*-----replace owner ------*/
function setOwner(bytes32 _identifier, bool _status)
onlyOwner()
public
{
owners[_identifier] = _status;
}
/*-----set price for pet adoption----*/
function setAdoptionRequirement(uint256 _amountOfTokens)
onlyOwner()
public
{
AdoptionRequirement = _amountOfTokens;
}
The keccak256 implementation in Solidity stores data differently.
keccak256(...) returns (bytes32):
compute the Ethereum-SHA-3 (Keccak-256) hash of the (tightly packed) arguments
Just use the function yourself when creating the contract:
function PetShop() public {
// add owners here
owners[keccak256(msg.sender)] = true;
}
as of now
// .encodePacked merges inputs together
owners[keccak256(abi.encodePacked(_text, _num, _addr))}=true
abi.encodePacked() , Solidity supports a non-standard packed mode where:
types shorter than 32 bytes are neither zero padded nor sign extended
dynamic types are encoded in-place and without the length.
array elements are padded, but still encoded in-place