I deployed the following smart contract on Ropsten ethereum test network and afterwards I tried making a transaction using #alch/alchemy-web3 npm package (Yes, I am using Alchemy API) but as you can see I got a fee on the transaction. Why is that? Aren't public view function calls supposed to cost 0 gas?
deployed smart contract
// SPDX-Lincense-Identifier: MIT
pragma solidity ^0.8.11;
contract VendingMachine {
address public owner;
mapping(address => uint256) public donutBalances;
constructor() {
owner = msg.sender;
donutBalances[address(this)] = 100;
}
function getVendingMachineBalance() public view returns (uint256) {
return donutBalances[address(this)];
}
function restock(uint amount) public {
require(msg.sender == owner, "Only the owner can restock this machine.");
donutBalances[address(this)] += amount;
}
function purchase(uint amount) public payable {
require(msg.sender == owner, "Only the owner can restock this machine.");
require(donutBalances[address(this)] >= amount, "Not enough donuts in stock to fulfill purchase request.");
require(msg.value >= amount*2 ether, "You must pay at least 2 ether / donut.");
donutBalances[address(this)] -= amount;
donutBalances[msg.sender] += amount;
}
}
transaction code javascript
const { API_URL, METAMASK_ACCOUNT_PRIVATE_KEY, METAMASK_ACCOUNT_PUBLIC_KEY } = process.env;
const { createAlchemyWeb3 } = require("#alch/alchemy-web3");
const web3 = createAlchemyWeb3(`${API_URL}`);
const contractAddress = '0xc7E286A86e4c5b8F7d52fA3F4Fe7D9DE6601b6F9'
const contractAPI = new web3.eth.Contract(contract.abi, contractAddress)
const nonce = await web3.eth.getTransactionCount(METAMASK_ACCOUNT_PUBLIC_KEY, 'latest');
const transaction = {
to: contractAddress, // faucet address to return eth
gas: 500000,
nonce: nonce,
data: contractAPI.methods.getVendingMachineBalance().encodeABI()
// optional data field to send message or execute smart contract
};
const signedTx = await web3.eth.accounts.signTransaction(transaction, METAMASK_ACCOUNT_PRIVATE_KEY)
web3.eth.sendSignedTransaction(signedTx.rawTransaction, function (error, hash) {
if (!error) {
console.log("🎉 The hash of your transaction is: ", hash, "\n Check Alchemy's Mempool to view the status of your transaction!");
} else {
console.log("❗Something went wrong while submitting your transaction:", error)
}
});
Transaction: https://ropsten.etherscan.io/tx/0x8ce3a288072809a804adac2206dc566dfb3eb3ddba3330bcb52ca6be71963b71
There are two ways to interact with a smart contract. A transaction (read-write, costs gas fees) and a call (read-only, gas free).
Your JS snippet sends a transaction. If you want to invoke the getVendingMachineBalance() function using a (gas free) call, you can use the .call() web3js method.
const contractAPI = new web3.eth.Contract(contract.abi, contractAddress);
const response = await contractAPI.methods.getVendingMachineBalance().call();
Related
The Open Zeppelin ERC20 contract has a name(), symbol, and totalSupply() function. This is my smart contract that inherits the ERC20 contract
pragma solidity ^0.8.0;
import "#openzeppelin/contracts/token/ERC20/ERC20.sol";
import "#openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
contract QUAD_EK is ERC20, ERC20Burnable {
constructor(address[] memory wallets, uint256[] memory amounts) ERC20("Quadency Token", "QUAD") {
require(wallets.length == amounts.length, "QUAD: wallets and amounts mismatch");
for (uint256 i = 0; i < wallets.length; i++){
_mint(wallets[i], amounts[i]);
if(i == 10){
break;
}
}
}
I've tested that the name() function works in Truffle.
describe('quad ek methods', async () => {
it('gets token name', async () => {
let quadEK = await QUAD_EK.new([account_0, account_1, account_2, account_3, account_4], amounts);
let quadName = await quadEK.name();
assert.equal(quadName.toString(), 'Quadency Token')
})
})
But when I tried to call the name() method in web3, I get the following error: TypeError: quad_ek.methods.name is not a function
My code below (the contract is loaded correctly):
module.exports = async function(callback) {
try{
const web3 = new Web3(new Web3.providers.HttpProvider(
`process.env.INFURA)`
)
);
const signInfo = web3.eth.accounts.privateKeyToAccount('process.env.QUAD_TEST0')
console.log("signingInfo", signInfo)
const nonce = await web3.eth.getTransactionCount(signInfo.address)
console.log("nonce", nonce)
const quad_ek = new web3.eth.Contract(token_abi, token_address )
**
quad_ek.methods.name().call({from: '0x72707D86053cb767A710957a6b9D8b56Dd7Fd835'}, function(error, result){
});**
}
catch(error){
console.log(error)
}
callback()
}
I'm using the documentation from web3:
myContract.methods.myMethod(123).call({from: '0xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe'}, function(error, result){
...
});
My token abi does have the name function:
"name()": {
"details": "Returns the name of the token."
},
"symbol()": {
"details": "Returns the symbol of the token, usually a shorter version of the name."
},
"totalSupply()": {
"details": "See {IERC20-totalSupply}."
},
"transfer(address,uint256)": {
"details": "See {IERC20-transfer}. Requirements: - `to` cannot be the zero address. - the caller must have a balance of at least `amount`."
},
"transferFrom(address,address,uint256)": {
"details": "See {IERC20-transferFrom}. Emits an {Approval} event indicating the updated allowance. This is not required by the EIP. See the note at the beginning of {ERC20}. NOTE: Does not update the allowance if the current allowance is the maximum `uint256`. Requirements: - `from` and `to` cannot be the zero address. - `from` must have a balance of at least `amount`. - the caller must have allowance for ``from``'s tokens of at least `amount`."
}
In your post, you used the the full JSON file (or just its devDocs section, can't tell from the context) as the value of token_abi. This whole file is an output from the compilation process, it contains the ABI as well as other data produced by the compilator (bytecode, list of warnings, ...).
You need to pass just the abi section to new web3.eth.Contract();
const compileOutput = JSON.parse(fs.readFileSync("./QUAD_EK.JSON"));
const token_abi = compileOutput[0].abi;
const quad_ek = new web3.eth.Contract(token_abi, token_address);
I am wrote a contract in using Solidity for a real estate transaction. It will allow you to sale a house. I am currently testing the contract using Truffle and during my test i keep getting a error that says "Reference Error: Home Sale is not defined".
Here is the smart contract:
pragma solidity ^0.6.0;
contract HouseSale {
address payable _BuyerAddress;
address payable _Seller;
address payable _Agent;
struct Home{
uint _priceinBUSD;
address _owner;
bool _homeforsale;
}
Home[1] HomeDB;
modifier SellerOnly() {
require [msg.sender == _Seller];
_;
}
// set price of house
function setPriceofHouse(uint8 _home, uint256 _priceinBUSD) SellerOnly public {
HomeDB[_home].priceinBUSD;
}
function getPriceofHouse(uint8 _home, uint256 _priceinBUSD) public payable returns(uint256) {
return HomeDB[_home].priceinBUSD;
}
// buyer purchase home
function buyAHome(uint8 _home) public payable returns(uint256) {
buyerAddress = msg.sender;
//correct home price
if (msg.value % HomeDB[_home].priceinBUSD == 0 ++) msg.value > 0 ++ HomeDB)
(_home) {
uint256 FinalSalePrice = msg.value/HomeDB(_home).priceinBUSD;
_SellerAddress.transfer(msg.value);
_AgentAddress.transfer(msg.value/100);
return finalSalePrice;
}
}
}
And here is the test file
const HomeSaleTest = artifacts.require("HomeSale");
/*
* uncomment accounts to access the test accounts made available by the
* Ethereum client
* See docs: https://www.trufflesuite.com/docs/truffle/testing/writing-tests-in-javascript
*/
contract("HomeSale", function (accounts) {
let instance;
beforeEach('should setup the contract instance', async () => {
instance = await HomeSale.deployed();
});
it("should return the list of accounts", async ()=> {
console.log(accounts);
});
it("should return price", async () => {
const value = await instance.getPriceofHouse();
assert.equal(value, '10000')
});
});
I am pretty new to the test realm of Smart Contract development.
You're defining HomeSaleTest, but trying to deploy HomeSale.
const HomeSaleTest = artifacts.require("HomeSale");
// ^^ here
instance = await HomeSale.deployed();
// ^^ here
Solution: Rename either of the expressions so that they have the same name. For example:
const HomeSale = artifacts.require("HomeSale");
// ^^ here
instance = await HomeSale.deployed();
// ^^ here
I am working on a simple real estate smart contract in Solidity and I keep receiving this error during testing in Truffle. " TypeError: No matching declaration found after variable lookup". The goal is to have the seller be the one to set the price of the house only.
Here is the contract code for your reference.
pragma solidity ^0.5.16;
contract HomeSale {
address payable _buyerAddress;
address payable _seller;
address payable _agent;
struct Home{
uint _priceinBUSD;
address _owner;
bool _homeforsale;
}
Home[1] HomeDB;
modifier onlySeller() {
require [msg.sender == _seller];
_;
}
// set price of house
function price(uint8 _home, uint256 _priceinBUSD) onlySeller public returns (uint64){
require(msg.sender);
HomeDB[_home].priceinBUSD;
}
function getPriceofHouse(uint8 _home, uint256 _priceinBUSD) public payable returns(uint256) {
return HomeDB[_home].priceinBUSD;
}
// buyer purchase home
function buyAHome(uint8 _home) public payable returns(uint256) {
_buyerAddress = msg.sender;
//correct home price
if (msg.value ==HomeDB[_home].priceinBUSD)(_home);
uint256 finalSalePrice = msg.value/HomeDB(_home).priceinBUSD;
_seller.transfer(msg.value);
_agent.transfer(msg.value/100);
return finalSalePrice;
}
}
Here is my test file:
const HomeSaleTest = artifacts.require("HomeSale");
/*
* uncomment accounts to access the test accounts made available by the
* Ethereum client
* See docs: https://www.trufflesuite.com/docs/truffle/testing/writing-tests-in-javascript
*/
contract("HomeSale", function (accounts) {
let instance;
beforeEach('should setup the contract instance', async () => {
instance = await HomeSale.deployed();
});
it("should return the list of accounts", async ()=> {
console.log(accounts);
});
it("should return price", async () => {
const value = await instance.getPriceofHouse();
assert.equal(value, '10000')
});
});
I am creating a contract (Exchange) within the Factory contract and I want to access via Web3 the Factory contract.
//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0;
import "./Exchange.sol";
contract Factory {
mapping(address => address) public tokenToExchange;
function createExchange(address _tokenAddress) public returns (address) {
require(_tokenAddress != address(0), "invalid token address");
require(
tokenToExchange[_tokenAddress] == address(0),
"exchange already exists"
);
Exchange exchange = new Exchange(_tokenAddress);
tokenToExchange[_tokenAddress] = address(exchange);
return address(exchange);
}
function getExchange(address _tokenAddress) public view returns (address) {
return tokenToExchange[_tokenAddress];
}
}
For example, I want to addLiquidity and for that I need access to Exchange contract. But before I want to check via getExchange() function within Factory contract if the Exchange() contract is already created.
addLiquidity = async (tokenAmount: string, ethAmount: string) => {
this.setState({ loading: true });
let exchangeAddress: string;
try {
const factory = this.state.factory;
const tokenAddress = this.state.tokenAddress;
// if Exchange not already created then we will get of address(0)
0x0000000000000000000000000000000000000000
exchangeAddress = await factory.methods.getExchange(tokenAddress).call();
if (exchangeAddress.startWith('0x00') {
//Exchange address
exchangeAddress = await factory.methods
.createExchange(this.state.tokenAddress)
.call();
console.log(`Èxchange created at: ${exchangeAddress}`);
}
console.log('address already created')
} catch (err) {
console.log(err);
this.setState({ loading: false });
return;
}
If I run the function getExchange(tokenAddress) is alway return the address of address(0) 0x0000000000000000000000000000000000000000
If the return value is 0x0000000000000000000000000000000000000000 the token does not yet have an exchange.
I think that is expected.
https://docs.uniswap.org/protocol/V1/guides/connect-to-uniswap#get-exchange-address
I have a simple Dapp and I want to sign a transaction but I don't have the private key as a string.
The user is using a MetaMask wallet. After they grant web3 access to their account, how can I access the private key to sign a transaction?
const signPromise = web3.eth.accounts.signTransaction(tx, PRIVATE_KEY);
// PRIVATE_KEY is what I'm trying to get.
Metamask doesn't share the private key directly for security reasons. And sometimes it doesn't even have the key - for instance when the user is using the Metamask UI to operate a hardware wallet account.
You'll need to construct the transaction object and pass it to the ethereum.request() method. This will open the Metamask window where the user can sign or decline the transaction request.
Code example is pretty straightforward and is in the linked documentation.
MetaMask does not give you access to a private key and never will.
The whole point of the wallet is to protect the user against malicious Dapps.
Here's an example of how you would sign your Metamask transaction:
export const mintNFT = async(url, name, description) => {
//error handling
if (url.trim() == "" || (name.trim() == "" || description.trim() == "")) {
return {
success: false,
status: "❗Please make sure all fields are completed before minting.",
}
}
//make metadata
const metadata = new Object();
metadata.name = name;
metadata.image = url;
metadata.description = description;
//pinata pin request
const pinataResponse = await pinJSONToIPFS(metadata);
if (!pinataResponse.success) {
return {
success: false,
status: "😢 Something went wrong while uploading your tokenURI.",
}
}
const tokenURI = pinataResponse.pinataUrl;
//load smart contract
window.contract = await new web3.eth.Contract(contractABI, contractAddress);//loadContract();
//set up your Ethereum transaction
const transactionParameters = {
to: contractAddress, // Required except during contract publications.
from: window.ethereum.selectedAddress, // must match user's active address.
'data': window.contract.methods.mintNFT(window.ethereum.selectedAddress, tokenURI).encodeABI() //make call to NFT smart contract
};
//sign transaction via Metamask
try {
const txHash = await window.ethereum
.request({
method: 'eth_sendTransaction',
params: [transactionParameters],
});
return {
success: true,
status: "✅ Check out your transaction on Etherscan: https://ropsten.etherscan.io/tx/" + txHash
}
} catch (error) {
return {
success: false,
status: "😥 Something went wrong: " + error.message
}
}
}
In this example, we're signing a transaction to mint an NFT. You can check out more details here: https://docs.alchemyapi.io/alchemy/tutorials/nft-minter#step-8-implement-the-mintnft-function