I've successfully deployed the following contract on Kaleido:
pragma solidity ^0.4.0;
contract Greeter {
string public greeting;
function Greeter() {
greeting = 'Hello';
}
function setGreeting(string _greeting) public {
greeting = _greeting;
}
function greet() constant returns (string) {
return greeting;
}
}
I try to interact with the contract like so:
from web3 import Web3
from web3.providers import HTTPProvider
from solc import compile_source
from web3.contract import ConciseContract
# Solidity source code
contract_source_code = '''
pragma solidity ^0.4.0;
contract Greeter {
string public greeting;
function Greeter() {
greeting = 'Hello';
}
function setGreeting(string _greeting) public {
greeting = _greeting;
}
function greet() constant returns (string) {
return greeting;
}
}
'''
compiled_sol = compile_source(contract_source_code)
contract_interface = compiled_sol[':Greeter']
w3 = Web3(HTTPProvider("https://user:password#u0telyzine-u0od4ny83j-rpc.us-east-2.kaleido.io"))
# address from previous deployment
contract_address = Web3.toChecksumAddress("0x4c94e89d5ec3125339906109f143673f40868df2")
greeter = w3.eth.contract(
address=contract_address,
abi=contract_interface['abi'],
)
print('Default contract greeting: {}'.format(
greeter.functions.greet().call()
))
# --- this hangs ---
print('Setting the greeting to Nihao...')
tx_hash = greeter.functions.setGreeting('Nihao').transact({ 'from': w3.eth.accounts[0], 'gas': 100000})
w3.eth.waitForTransactionReceipt(tx_hash)
print('Updated contract greeting: {}'.format(
greeter.functions.greet().call()
))
reader = ConciseContract(greeter)
assert reader.greet() == "Nihao"
However, when I try to submit a transaction which calls setGreeting the transaction hangs. Viewing the Kaleido logs, I see VM in read-only mode. Mutating opcode prohibited. Also, when I visit the block explorer for my node, the transactions don't load while the blocks do.
What can I do about this read only mode?
moghadasian
I could not recreate your "VM in read-only mode" when submitting a transaction - that worked successfully.
However, I had to do a bit of investigation to get web3/python connecting to Kaleido - so I'm adding a separate answer to help others trying to get going.
Configuring HTTPS authentication to Kaleido from Python web3
On my Mac, with a default pip3 installation of web3, I found the only way to configure the Python Session with auth was to use a $HOME/.netrc file such as:
machine u0oaXXXXXX-u0c4XXXXXX-rpc.us-east-2.kaleido.io
login u0d0bxXXXX
password jA-pJdIrcRaIx7XXXXXXXXXXXXXXXXXXXXXXXXX
Configure web3 for Geth/PoA
My chain was using Geth/PoA, so I had to follow the instructions here, to install the required middleware:
http://web3py.readthedocs.io/en/stable/middleware.html#geth-style-proof-of-authority
Updated example including deployment of contract
Here is the python3 that successfully deployed and reported Updated contract greeting: Nihao.
You will need to change your HTTPProvider to the HTTPS RPC URL of your node, but without the authentication headers.
from web3 import Web3
from web3.providers import HTTPProvider
from solc import compile_source
from web3.contract import ConciseContract
from web3.middleware import geth_poa_middleware
# Solidity source code
contract_source_code = '''
pragma solidity ^0.4.0;
contract Greeter {
string public greeting;
function Greeter() {
greeting = 'Hello';
}
function setGreeting(string _greeting) public {
greeting = _greeting;
}
function greet() constant returns (string) {
return greeting;
}
}
'''
compiled_sol = compile_source(contract_source_code)
contract_interface = compiled_sol['<stdin>:Greeter']
w3 = Web3(HTTPProvider("https://u0oaXXXXXX-u0c4XXXXXX-rpc.us-east-2.kaleido.io"))
w3.middleware_stack.inject(geth_poa_middleware, layer=0)
Greeter = w3.eth.contract(abi=contract_interface['abi'], bytecode=contract_interface['bin'])
tx_hash = Greeter.constructor().transact({ 'from': w3.eth.accounts[0], 'gas': 1000000})
tx_receipt = w3.eth.waitForTransactionReceipt(tx_hash)
print('Deployed greeter contract: {}'.format(tx_receipt.contractAddress))
# address from previous deployment
contract_address = Web3.toChecksumAddress(tx_receipt.contractAddress)
greeter = w3.eth.contract(
address=contract_address,
abi=contract_interface['abi'],
)
print('Default contract greeting: {}'.format(
greeter.functions.greet().call()
))
print('Setting the greeting to Nihao...')
tx_hash = greeter.functions.setGreeting('Nihao').transact({ 'from': w3.eth.accounts[0], 'gas': 100000})
w3.eth.waitForTransactionReceipt(tx_hash)
print('Updated contract greeting: {}'.format(
greeter.functions.greet().call()
))
reader = ConciseContract(greeter)
assert reader.greet() == "Nihao"
moghadasian,
The "VM in read-only mode" is because you are using call to interact with your Smart Contract method. So it's just calling your method in a read-only mode. You would use this to call methods on contracts that query data - without having to submit a transaction to the chain.
[edit] - the above advice is generally helpful for "VM in read-only mode", but if you're trying out python web3, you pobably want the other answer with a full working example: https://stackoverflow.com/a/51155413/4972840 [/edit]
Regards, Peter
Related
I want to get all reserve in UniswapV2 pool in one request to the EVM. Here is the code getReserve :
// SPDX-License-Identifier: MIT
// compiled with 0.8.17
pragma solidity >=0.4.22 <0.9.0;
contract IUniswapV2Pair {
function getReserves() public view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast) {}
}
contract getReserve {
function get(address[] memory _add) external view returns (uint[] memory) {
uint n = _add.length;
uint[] memory liste = new uint[](n*2);
// Define variables to store the returned values
uint112 reserve0;
uint112 reserve1;
uint32 blockTimestampLast;
for (uint i=0; i<n; i++) {
// Call the getReserves function in the other contract and store the returned values
(reserve0, reserve1, blockTimestampLast) = IUniswapV2Pair(_add[i]).getReserves();
liste[i*2] = reserve0;
liste[i*2+1] = reserve1;
}
return liste;
}
function getOnlyOne(address _add) external view returns (uint112, uint112, uint32) {
return IUniswapV2Pair(_add).getReserves();
}
}
To test that this program work well on my Ganache EVM I have created this program IUniswapV2Pair:
// SPDX-License-Identifier: MIT
// compiled with 0.8.17
pragma solidity >=0.4.22 <0.9.0;
contract IUniswapV2Pair {
function getReserves() external pure returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast) {
reserve0 = 11;
reserve1 = 12;
blockTimestampLast = 13;
return (reserve0, reserve1, blockTimestampLast);
}
}
Like I said, it is working well on my Ganache EVM. But when I deploy it on the GOERLI testnet it is not working, here is the addresss :
getReserve : 0x993305D7d90675656857c7Cd69f1CF57242D79D5
IUniswapV2Pair : 0x33e57A530F90aB2A5572E2a877161Ca644e8FC95
My Problem is to make getOnlyOne("0x33e57A530F90aB2A5572E2a877161Ca644e8FC95") working.
1 Testing code on Goerli
I have tried to connect to GOERLI via Python, here is the code :
from web3 import Web3
import json, web3
server = "https://eth-goerli.public.blastapi.io"
w3 = Web3(Web3.HTTPProvider(server))
w3.isConnected()
# Contrat intermédiaire
address = "0x993305D7d90675656857c7Cd69f1CF57242D79D5"
abi = [{"inputs":[{"internalType":"address[]","name":"_add","type":"address[]"}],"name":"get","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function","constant":true},{"inputs":[{"internalType":"address","name":"_add","type":"address"}],"name":"getOnlyOne","outputs":[{"internalType":"uint112","name":"","type":"uint112"},{"internalType":"uint112","name":"","type":"uint112"},{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function","constant":true}]
contract = w3.eth.contract(address=address, abi=abi)
contract.all_functions()
contract.functions.get(["0x33e57A530F90aB2A5572E2a877161Ca644e8FC95"]).call()
contract.functions.getOnlyOne("0x33e57A530F90aB2A5572E2a877161Ca644e8FC95").call()
I have this error web3.exceptions.ContractLogicError: execution reverted, which I don't have when I do it on Ganache.
And to make sure that it is not a problem from IUniswapV2Pair I have this code:
address = "0x33e57A530F90aB2A5572E2a877161Ca644e8FC95"
abi = [{"inputs":[],"name":"getReserves","outputs":[{"internalType":"uint112","name":"reserve0","type":"uint112"},{"internalType":"uint112","name":"reserve1","type":"uint112"},{"internalType":"uint32","name":"blockTimestampLast","type":"uint32"}],"stateMutability":"pure","type":"function","constant":true}]
contract = w3.eth.contract(address=address, abi=abi)
contract.all_functions()
contract.functions.getReserves().call()
Which return me : [11, 12, 13]
2 Testing connection between contracts
I have created 2 others easier smart contract which have been working well here it is :
// SPDX-License-Identifier: MIT
// compiled with 0.8.17
pragma solidity >=0.4.22 <0.9.0;
contract FavoriteNumber {
uint favoriteNumber;
function getFavoriteNumber() external view returns(uint) {
return favoriteNumber;
}
function setFavoriteNumber(uint _favoriteNumber) external {
favoriteNumber = _favoriteNumber;
}
}
// SPDX-License-Identifier: MIT
// compiled with 0.8.17
pragma solidity >=0.4.22 <0.9.0;
contract FavoriteNumber {
function getFavoriteNumber() public view returns(uint) {}
function setFavoriteNumber(uint _favoriteNumber) public {}
}
contract ExistingNoSet {
function getA(address _t) public view returns (uint result) {
return FavoriteNumber(_t).getFavoriteNumber();
}
function setA(address _t, uint _val) public {
FavoriteNumber(_t).setFavoriteNumber(_val);
}
}
Here is the address :
FavoriteNumber : 0x14c89b4F462C11961Bb48aD6B2008f64617CF62a
ExistingNoSet : 0x97BdDaff1a971580f99C1DB850dE5EcF4982251a
And to test the code, here is the Python program, be careful I give a private key here (don't use it outside of a testnet) :
from web3 import Web3
import json, web3
server = "https://eth-goerli.public.blastapi.io"
private_key = "df49b58fbc863c5e60fe4e64829a853c46a8a12c3310404bc2a03bfefb89f68a"
public_add = "0xb4311ad11530F735ecE2d652Cbd56D1FB8D6Efeb"
w3 = Web3(Web3.HTTPProvider(server))
w3.isConnected()
# Contrat intermédiaire
address = "0x97BdDaff1a971580f99C1DB850dE5EcF4982251a"
abi = [{"inputs":[{"internalType":"address","name":"_t","type":"address"}],"name":"getA","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function","constant":true},{"inputs":[{"internalType":"address","name":"_t","type":"address"},{"internalType":"uint256","name":"_val","type":"uint256"}],"name":"setA","outputs":[],"stateMutability":"nonpayable","type":"function"}]
contract = w3.eth.contract(address=address, abi=abi)
contract.all_functions()
contract.functions.getA("0x14c89b4F462C11961Bb48aD6B2008f64617CF62a").call() # It should return 15
txn = contract.functions.setA(
"0x14c89b4F462C11961Bb48aD6B2008f64617CF62a", 3 # You set this new number
).build_transaction({
'nonce': w3.eth.get_transaction_count(public_add),
'gas': 200000,
'gasPrice': w3.toWei('20', 'gwei')
})
signed_txn = w3.eth.account.sign_transaction(txn, private_key=private_key)
w3.eth.send_raw_transaction(signed_txn.rawTransaction)
# You have to time sleep a little bit like 1min
contract.functions.getA("0x14c89b4F462C11961Bb48aD6B2008f64617CF62a").call() # It should return 3
So this program works well.
Thank you very to have read my post until here, I hope you will be able to help me.
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.
I started first steps into ethereum blockchain with parity on a private network. I was able to configure parity and perform the deployment of smart contract on a dev mode chain on my private network through Parity UI which also is able to call methods of the contract.
The problem that I face is related to calling a function in smart contract using Web3.js. I was able to connect to the chain using the Web.js Library;
Web3 = require('web3')
web3 = new Web3('ws://localhost:8546')
mycontract = web3.eth.Contract([{"constant":false,"inputs":[],"name":"greet","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"}],0xEb112542a487941805F7f3a04591F1D6b04D513c)
When I call the method below;
mycontract.methods.greet().call()
It gives me the following output instead of returning the expected string "OK Computer" through a promise object as written in smart contract greet function.
{ [Function: anonymousFunction]
send: { [Function] request: [Function] },
estimateGas: [Function],
encodeABI: [Function] }
Smart Contract code:
pragma solidity ^0.4.22;
//Compiler Version: 0.4.22
contract Greeter {
address owner;
constructor() public {
owner = msg.sender;
}
function greet() public returns(string){
return "OK Computer";
}
}
Every transaction or smart contract method call which involves a change on the blockchain state will return a promise. So you just need to handle the promise accordingly:
mycontract.methods.greet.call().then(function(resp) {
console.log(resp) // This will output "OK Computer"
}
More in web docs
I have a simple token derived from openzeppelin's MintableToken.
However, when I either add a Constructor OR another function, I am constantly running out of gas. But when I add ONLY one of both, either the Constructor OR the function, everything works fine.
My question is: how can I add several functions together with a constructor into my SmartContract?
The Token-Code:
pragma solidity ^0.4.22;
import "openzeppelin-solidity/contracts/token/ERC20/MintableToken.sol";
contract HaioToken is MintableToken {
string public name = "Haio Token";
string public symbol = "HAI";
uint8 public decimals = 18;
uint256 public cap;
constructor(uint256 _cap) public {
cap = _cap;
}
function test(address _to) public returns (bool) {
return true;
}
}
Migrations:
2_deploy_contracts.js:
var HaioToken = artifacts.require("HaioToken");
module.exports = function(deployer, network, accounts) {
const hardCap = 25000000;
return deployer
.then(() => {
return deployer.deploy(HaioToken, hardCap);
})
};
When I want to deploy the code, I get the following error message:
Error: VM Exception while processing transaction: out of gas
If I remove either the constructor or the test-function, everything works fine.
I guess you are running the migration with truffles default settings that came out of the box after running "truffle init" isn't it?
You should raise the gas you want to send on contract deployment this way in truffle.js (or truffle-config.js on Windows):
module.exports = {
networks: {
development: {
host: "localhost",
port: 7545,
network_id: "*",
gas: 5000000
}
}
};
(The value of 5000000 is an example that mostly works out of the box and if you don't have to care because developing on a local testnet :) )
Purpose
I want to deploy the contract to ropsten (test network of ethereum) and make a transaction using web3py.
Environment
I used remix for contract deployment, and here is the etherscan link for the deploy transaction.: etherscan.io/tx/0xb0a
and for making transaction, I used this script: gist
(I also attached the solidity contract code in the link.)
Problems
When I try to unlock my account using w3.personal.unlockAccount in web3py
w3.personal.unlockAccount(account_a, input("Password: "))
It just raises the below error.
I also want to make a transaction using the giveToken function in the contract. But the same error was raised when I executed code.
contract_instance.giveToken(account_b, token_amount, transact={'from': account_a})
for pre-defined variables (such as contract_instance, account_b), you can see the whole code at the below or the gist link above.
Here is my code
token.sol
pragma solidity ^0.4.0;
contract BasicToken {
// Owner
address public owner;
// For manage account and balance
mapping(address => uint) userAccount;
constructor() public {
owner = msg.sender;
}
// Example
event increaseToken(address sender, address receiver, uint amount);
function getBalance(address addr) public view returns (uint) {
return userAccount[addr];
}
function myBalance() public view returns (uint) {
return userAccount[msg.sender];
}
function giveToken(address dest, uint amount) public returns (bool) {
emit increaseToken(msg.sender, dest, amount);
userAccount[dest] += amount;
return true;
}
}
web3py.py
import json
import time
from eth_utils import to_checksum_address
from web3 import Web3, HTTPProvider
from web3.contract import ConciseContract
from web3.middleware import geth_poa_middleware
INFURA_API_KEY = "this is key"
ACCOUNT_PASSWORD = "this is account password"
w3 = Web3(HTTPProvider('https://ropsten.infura.io/{}'.format(INFURA_API_KEY)))
w3.middleware_stack.inject(geth_poa_middleware, layer=0)
from eth_utils import to_checksum_address
contract_address = to_checksum_address('0xFb294910d8193DeB9a294B51F22D8878ad15f2E8')
# Instantiate and deploy contract
contract_instance = w3.eth.contract(abi=contract_abi, address=contract_address, ContractFactoryClass=ConciseContract)
account_a = "0xCa2d22Cb8ff54f2D1DCfDBb75DD6411a5A0ee6f1"
account_b = "0x8F8d1bc97E8939e3932BfBeb923A1B2B972D5A9A"
# Unlock account
w3.personal.unlockAccount(account_a, ACCOUNT_PASSWORD) # Raise the below error!!!
"""
requests.exceptions.HTTPError: 405 Client Error: Method Not Allowed for url: https://ropsten.infura.io/API_KEY
"""
print("Contract: {}".format(contract_address))
print("Before give token")
print('My Balance: {}'.format(contract_instance.myBalance()))
print('{} Balance: {}'.format(account_a, contract_instance.getBalance(account_a)))
print('{} Balance: {}'.format(account_b, contract_instance.getBalance(account_b)))
token_amount = 1000
# Make transaction
contract_instance.giveToken(account_b, token_amount, transact={'from': account_a}) # Raise the below error!!!
"""
requests.exceptions.HTTPError: 405 Client Error: Method Not Allowed for url: https://ropsten.infura.io/API_KEY
"""
try:
assert contract_instance.getBalance(account_b) == token_amount
except AssertionError as e:
print("Test Error: {}".format(e))
print("Amount: {}\t Expected: {}".format(contract_instance.getBalance(account_b), token_amount))
else:
print("After give token")
print('My Balance: {}'.format(contract_instance.myBalance()))
print('{} Balance: {}'.format(account_a, contract_instance.getBalance(account_a)))
print('{} Balance: {}'.format(account_b, contract_instance.getBalance(account_b)))
finally:
print('Done')
FYI
With remix, I could make a transaction. etherscan.io/tx/0x129
Infura doesn't support unlockAccount, because they don't know your private key. (Infura is a public Ethereum node used by many people.)
They don't support any method that requires them to know your private key.