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.
Related
I'm trying to test this contract using "truffle test" but it show the following error:
Error: Returned error: VM Exception while processing transaction: revert only owner can call this function - - Reason given: Only owner can call this function.
Gaming.sol
pragma solidity ^0.5.0;
contract Gaming {
/* Our Online gaming contract */
address public owner;
bool public online;
struct Player {
uint wins;
uint losses;
}
mapping (address => Player) public players;
constructor() public payable {
owner = msg.sender;
online = true;
}
modifier isOwner() {
require(msg.sender == owner, "Only owner can call this function");
_;
}
}
TestGaming.sol
pragma solidity ^0.5.0;
import "truffle/Assert.sol";
import "truffle/DeployedAddresses.sol";
import "../contracts/Gaming.sol";
contract TestGaming {
uint public initialBalance = 10 ether;
Gaming gaming;
address owner;
function beforeAll() public {
gaming = Gaming(DeployedAddresses.Gaming());
owner = gaming.owner();
}
function testWithdrawFunds() public {
uint ownerBalanceBefore = owner.balance;
gaming.withdrawFunds();
uint ownerBalanceAfter = owner.balance;
Assert.equal (initialBalance, ownerBalanceAfter - ownerBalanceBefore, "The owner's balance should have increased by 10 ether");
}
Error
Error: Returned error: VM Exception while processing transaction: revert only owner can call this function - - Reason given: Only owner can call this function.
You may deployed the Contract with other account and test it with another accounts..
please do add your test script
I'm trying to call a method from a contract that was instantiated from another contract. I use truffle console.
Here are the details: (I think you can gloss over the code, since this question is aimed at using truffle console, not fixing any bugs)
I have a contract called MyCoinSupply.sol:
pragma solidity ^0.8.0;
import "./MyCoin.sol";
contract MyCoinSupply is MyCoin("MyCoin", "MYC") // MyCoin is ERC20
{
constructor() public // gives 1000 tokens to the owner
{
_mint(msg.sender, 1000);
}
function getCoinbase() public returns (address) // for debugging purposes
{
return block.coinbase;
}
function _mintMinerReward() internal // gives 20 tokens to the miner
{
// _mint(block.coinbase, 20); // for some reason, block.coinbase is address 0
}
function _transfer(address from, address to, uint256 value) override internal
{
_mintMinerReward();
super._transfer(from, to, value);
}
}
I instantiate MyCoinSupply.sol within MyCoinDEX.sol:
pragma solidity ^0.8.0;
import "./IERC20.sol";
import "./MyCoinSupply.sol";
contract MyCoinDEX
{
IERC20 public token;
event Bought(uint256 amount);
event Sold(uint256 amount);
constructor() public
{
token = new MyCoinSupply();
}
function showSender() public view returns (address) // for debugging purposes
{
return (msg.sender);
}
function buy(uint256 amountTobuy) payable public // send ether and get tokens in exchange; 1 token == 1 ether
{
uint256 dexBalance = token.balanceOf(address(this));
require(amountTobuy > 0, "You need to send some ether");
require(amountTobuy <= dexBalance, "Not enough tokens in the reserve");
token.transfer(msg.sender, amountTobuy);
emit Bought(amountTobuy);
}
function sell(uint256 amount) public // send tokens to get ether back
{
require(amount > 0, "You need to sell at least some tokens");
uint256 allowance = token.allowance(msg.sender, address(this));
require(allowance >= amount, "Check the token allowance");
token.transferFrom(msg.sender, address(this), amount);
payable(msg.sender).transfer(amount);
emit Sold(amount);
}
}
Here's my migration for deploying contracts:
const MyCoinSupply = artifacts.require("MyCoinSupply");
const MyCoinDEX = artifacts.require("MyCoinDEX");
module.exports = function(deployer) {
deployer.deploy(MyCoinSupply);
deployer.deploy(MyCoinDEX);
};
Here's my problem:
When I try to call functions that belong to MyCoinDEX, all works well:
truffle(development)> let instance = await MyCoinDEX.deployed()
undefined
truffle(development)> instance.buy(1)
All is good here.
However, when I try the following I get an error:
truffle(development)> instance.token.balanceOf("0xE1994C1054f9c4171B8ae9a7E7a68F404c2bF829")
evalmachine.<anonymous>:0
instance.token.balanceOf("0xE1994C1054f9c4171B8ae9a7E7a68F404c2bF829")
^
Uncaught TypeError: instance.token.balanceOf is not a function
I Googled this error (I read this solution and this solution), but I didn't really stumble upon a solution. Can someone tell me how can I call token's methods from MyCoinDEX?
Thank you in advance!
I am having issues verifying my smart contract on Rinkeby Testnet using #chainlink. My contract is deploying to Rinkeby fine but runs into issues when I have publish_source on. The contract verifies fine when using v0.6 and Solidity 0.6.6, I seem to only have issues with v0.8. Anyone know why this might be happening?
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "#chainlink/contracts/src/v0.8/ChainlinkClient.sol";
import "#openzeppelin/contracts/token/ERC721/ERC721.sol";
/**
* #title MyContract is an example contract which requests data from
* the Chainlink network
* #dev This contract is designed to work on multiple networks, including
* local test networks
*/
contract MyContract is ChainlinkClient, ERC721 {
using Chainlink for Chainlink.Request;
address constant RINKEBY_ORACLE =
0x3A56aE4a2831C3d3514b5D7Af5578E45eBDb7a40;
bytes32 constant RINKEBY_JOB_ID =
bytes32("187bb80e5ee74a139734cac7475f3c6e");
uint256 constant FEE = 0.1 * 10**18;
uint256 public tokenCounter;
bytes32 public data;
string public image_url;
struct Clip {
string name;
string url;
}
Clip[] public clips;
mapping(bytes32 => string) public requestIdToClipName;
mapping(bytes32 => address) public requestIdToSender;
mapping(bytes32 => string) public requestIdToTokenURI;
event requestedCollectible(bytes32 indexed requestId);
mapping(bytes32 => uint256) public requestToTokenId;
mapping(uint256 => string) public tokenIdToName;
mapping(uint256 => string) public tokenIdToImgUrl;
constructor(address _link) public ERC721("Tests", "TST") {
if (_link == address(0)) {
setPublicChainlinkToken();
} else {
setChainlinkToken(_link);
}
setChainlinkOracle(0x3A56aE4a2831C3d3514b5D7Af5578E45eBDb7a40);
tokenCounter = 0;
}
function getChainlinkToken() public view returns (address) {
return chainlinkTokenAddress();
}
function createRequestTo(
string memory _url,
string memory _path,
string memory clipName
) public returns (bytes32 requestId) {
Chainlink.Request memory req = buildChainlinkRequest(
RINKEBY_JOB_ID,
address(this),
this.fulfill.selector
);
req.add("get", _url);
req.add("path", _path);
requestId = sendChainlinkRequestTo(RINKEBY_ORACLE, req, FEE);
requestIdToClipName[requestId] = clipName;
requestIdToSender[requestId] = msg.sender;
emit requestedCollectible(requestId);
return requestId;
}
function fulfill(bytes32 _requestId, bytes32 _data)
public
recordChainlinkFulfillment(_requestId)
{
address nftOwner = requestIdToSender[_requestId];
string memory name = requestIdToClipName[_requestId];
uint256 newItemId = clips.length;
data = _data;
image_url = bytes32ToString(data);
clips.push(Clip(name, image_url));
_safeMint(nftOwner, newItemId);
tokenIdToName[newItemId] = name;
tokenIdToImgUrl[newItemId] = image_url;
}
function getNumberOfClips() public view returns (uint256) {
return clips.length;
}
/**
* #notice Allows the owner to withdraw any LINK balance on the contract
*/
function withdrawLink() public {
LinkTokenInterface link = LinkTokenInterface(chainlinkTokenAddress());
require(
link.transfer(msg.sender, link.balanceOf(address(this))),
"Unable to transfer"
);
}
/**
* #notice Call this method if no response is received within 5 minutes
* #param _requestId The ID that was generated for the request to cancel
* #param _payment The payment specified for the request to cancel
* #param _callbackFunctionId The bytes4 callback function ID specified for
* the request to cancel
* #param _expiration The expiration generated for the request to cancel
*/
function cancelRequest(
bytes32 _requestId,
uint256 _payment,
bytes4 _callbackFunctionId,
uint256 _expiration
) public {
cancelChainlinkRequest(
_requestId,
_payment,
_callbackFunctionId,
_expiration
);
}
}
Here is the script I am using to deploy:
from brownie import MyContract, accounts, network, config
from scripts.helpful_scripts import fund_contract
def main():
dev = accounts.add(config['wallets']['from_key'])
print(network.show_active())
publish_source = True
my_contract = MyContract.deploy(
config['networks'][network.show_active()]['link_token'],
{"from": dev},
publish_source=publish_source
)
fund_contract(my_contract)
return my_contract
This is the error:
Transaction sent: 0x01ef409a5b0c2f6de60194f14f1d2e8ffac9a6ce6397aaa38392d949e2656909
Gas price: 1.000000008 gwei Gas limit: 2793499 Nonce: 359
MyContract.constructor confirmed Block: 9332417 Gas used: 2539545 (90.91%)
MyContract deployed at: 0x42fBa06808C3cB11e5D7AcC6BB02B1A612e040d1
File "brownie/_cli/run.py", line 49, in main
return_value, frame = run(
File "brownie/project/scripts.py", line 103, in run
return_value = f_locals[method_name](*args, **kwargs)
File "./scripts/deploy_api.py", line 11, in main
my_contract = MyContract.deploy(
File "brownie/network/contract.py", line 600, in __call__
return tx["from"].deploy(
File "brownie/network/account.py", line 555, in deploy
contract.publish_source(deployed_contract, silent=silent)
File "brownie/network/contract.py", line 410, in publish_source
contract_info = self.get_verification_info()
File "brownie/network/contract.py", line 292, in get_verification_info
if symbol_alias["local"] is not None:
KeyError: 'local'
Two points going on here:
Brownie Etherscan verification doesn't work well with Solidity v0.8. Work is being done to fix this issue.
Looks like you are deploying to a local network instead of Rinkeby. Either remove publish_source when deploying locally or add --network rinkeby to the end of your deploy script to deploy to Rinkeby.
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'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