I'd like to estimate the gas of a simple ERC20 transfer between two addresses. The web3.js docs on estimateGas are admittedly confusing:
// using the callback
myContract.methods.myMethod(123).estimateGas({gas: 5000000}, function(error, gasAmount){
if(gasAmount == 5000000)
console.log('Method ran out of gas');
});
The myMethod(123) is what I find confusing. What is that for? The following is what I'm currently thinking, but I'm getting TypeError: contract.methods.send is not a function. What should I substitute for myMethod(123)?
try {
await contract.methods
.send("0xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe")
.estimateGas({ gas: 60000 }, (error, gasAmount) => {
return gasAmount;
});
} catch (err) {
console.log(err);
}
send() refers to a contract method called send. You do not have send in your Solidity Contract source code.
Instead, try contract.methods.myMethod.send
A bit late, but you need to look for the function in the smartcontract you want to call and replace myMethod(123) for the function in the contract you are calling.
For example, if you are working with the pancakeswap contract and check in the code the function function deposit(uint256 _pid, uint256 _amount) public {...} (line 1674), you will need to do this:
const estimatedGas = await contract.methods.deposit(123, 0).estimateGas({from: "0x345})
You need to pass the args to deposit in order to make it work.
Related
I have a custom token I've sent to my DApp, and I am trying to send some amount of 'rewards' to my users for posting things on the website. However, it seems like nothing is being sent from my backend server/DApp to the users. I'm wondering what I might be doing wrong here. Code below:
server.js
(Basic idea - call approve then transferFrom)
app.post('/send-TOKEN-to-addr', async (req, res) => {
const post_addr = req.body.addr; // the client sends in user's addr
var transfer_amt = 5000; // token reward to user
try {
console.log('send-TOKEN-to-addr gets called for :: '+String(post_addr));
TOKEN_Contract.methods.approve(DAppAddr, regular_post_transfer_amt).call((err, result) => {
console.log('approve:: '+String(result));
//return res.send(result);
});
TOKEN_Contract.methods.transferFrom(DAppAddr, post_addr, transfer_amt).call((err, result) => {
//console.log(result);
return res.send(result);
});
} catch (e) { throw e; }
});
On the backend I get:
send-TOKEN-to-addr gets called for :: 0xb65ec054bd7f633efd8bd0b59531de464046a7c0
approve:: true
But on the frontend I get no response. As well, when I check the balances of TOKEN for the DApp and the addr, nothing changes, so I think nothing happens here.
I am seeking advice on getting my DApp to send the tokens it has to other addresses. I confirmed that the DApp has the tokens already, I just can't seem to send on behalf of it within my node.js framework.
Edit 1
I have some basic functionality already working with my token (within the DApp), such as the below call:
app.post('/balanceOf-TOKEN-by-addr', async (req, res) => {
//console.log('balanceOf-TOKEN-by-addr - server');
const post_addr = req.body.addr;
//console.log(post_addr);
try {
TOKEN_Contract.methods.balanceOf(post_addr).call((err, result) => {
//console.log(result);
return res.send(result);
});
} catch (e) { throw e; }
});
Edit 2
Adding code for how I initialize my DApp - I will need the private keys to call send() methods from it? Because my DApp has a bunch of the TOKENs that I want to send out.
const WEB3_PROVIDER = "HTTP://127.0.0.1:7545"
if (typeof web3 !== 'undefined') {
web3 = new Web3(web3.currentProvider);
console.log("web3 already initialized.");
} else {
// set the provider you want from Web3.providers
web3 = new Web3(new Web3.providers.HttpProvider(WEB3_PROVIDER));
console.log("New web3 object initialized.");
}
const DAppABIFile = require('./assets/abis/DAppABI');
const DAppABI = DAppABIFile.DAppABI;
const DAppAddr = "0x5C7704a050286D742............"; // public key
const DAppContract = new web3.eth.Contract(DAppABI, DAppAddr);
There's a difference between a call (read-only) and a transaction (read-write).
Your snippet only calls the contract but doesn't send transactions. So the contract is not able to write state changes from just the calls.
TOKEN_Contract.methods.<method>.call((err, result) => {});
Web3 uses the .send() function (docs) to send a transaction.
TOKEN_Contract.methods.<method>.send({
from: '0x<sender_address>'
}, (err, result) => {});
Note that the transaction needs to be signed with the private key of the from address. This can be done in 2 ways:
The node provider (most likely passed in the new Web3(<provider_url>) constructor) knows the private key to the sender address and has its account unlocked - this is usually done only on local networks used for development.
You have passed the sender private key to your web3 instance using the wallet.add() function (docs). Then it gets signed in your JS app, and the payload sent to the node in the background contains only the signed transaction (not the private key).
You can also set this address as the defaultAccount (docs) so that you don't have to keep passing it to the .send() function.
Well first check if the wallet that you are using to call the functions is the owner of the tokens and not the dapp, if the dapp is the owner i would recommend you that the address you are using to call the contract have an owner permission and add the dapp contract a function to send the token to some address, if the address that you are using is the owner of the tokens just call the ´TOKEN_CONTRACT.methods.transfer(post_addr,transfer_amt)´
now, just as an explanation, the reason because that endpoint is not sending the tokens is this, in that operation are 4 address used, the user address, the contract address, the token address and the address you are using to send the transactions from the backend, when you call TOKEN_Contract.methods.approve(DAppAddr, regular_post_transfer_amt) you are approving the contract address to move the tokens that owns the address you use in your backend not viceversa, for that the contract would have made that call and pass as a parameter the backend address, so when you call TOKEN_Contract.methods.transferFrom(DAppAddr, post_addr, transfer_amt) you are trying to move the tokens of the contract approved to the backend address and send it to the user address, but the amount of this approved tokens is 0, because the thing i explained before
I am trying to read some data from a smart contract that I have created and available on the main net here:
https://snowtrace.io/address/0x98608c1e3ae104e7a11ea2879b44669a1c38b73d#code
When I try to interact with my smart contract via Web3 library I am able to read all public details, except for one called: getCurrentBalance()
which requires owner permission (which I am). I triple checked to make sure that I am on the right owner account in Metamask.
This is my html file:
<!DOCTYPE html>
<html>
<head>
<title>Web 3 Demo</title>
<script src='node_modules/web3/dist/web3.min.js'></script>
</head>
<body>
<button onclick="getCurrentBalance();">Get Current Contract Balance</button>
Status: <span id="status">Loading...</span>
<script type="text/javascript" src="index.js"></script>
</body>
</html>
This is the index.js:
// Check metamask installation and connect to site
async function loadWeb3() {
if (window.ethereum) {
window.web3 = new Web3(window.ethereum);
window.ethereum.enable();
}
}
// In the html file there is a span called status this function updates the print out
function updateStatus(status) {
const statusEl = document.getElementById('status');
statusEl.innerHTML = status;
console.log(status);
}
// This function links to the smart contract for interactions
async function loadContract() {
return await new window.web3.eth.Contract([{"inputs": [],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"constant":false,"inputs":[{"internalType":"address payable","name":"_liquitdateTo","type":"address"}],"name":"destroySmartContract","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"fundsReceived","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getCurrentBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"getFunds","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"internalType":"bool","name":"_bool","type":"bool"}],"name":"pause","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"smartContractAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"tresury","outputs":[{"internalType":"address payable","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"}], '0x98608C1e3ae104E7A11EA2879b44669a1c38b73D');
}
// This is the main program function that calls all the previous ones
async function load() {
await loadWeb3();
window.contract = await loadContract();
updateStatus('Ready!');
}
async function getCurrentBalance() {
updateStatus('fetching...');
const getCurrentBalance = await window.contract.methods.getCurrentBalance().call();
updateStatus(`Current Contract Balance: ${getCurrentBalance}`);
}
load();
This is the error I get in the console:
How to read the getCurrentBalance() from the smart contract?
The getCurrentBalance() function of the smart is defined as:
function getCurrentBalance() public view returns(uint) {
require(owner == msg.sender, 'You are not the owner!!!');
require(paused == false, 'Contract Status: Paused!!!');
return address(this).balance;
}
In your case, the first require() condition fails with the "You are not the owner!!!" error message, because the address invoking the function is not the expected value stored in the owner variable.
A systematic solution would be to invoke the getCurrentBalance() function from the owner address:
// where `0x123` is the `owner` address
await window.contract.methods.getCurrentBalance().call({from: '0x123'});
However, since the function effectively just returns the contract address balance, you can also perform a workaround, and query the contract address balance directly from the blockchain (without invoking the contract function):
await web3.eth.getBalance('0x98608C1e3ae104E7A11EA2879b44669a1c38b73D');
I have a contract deployed onchain:
function getNonces(bytes32 _hashWalletSig, uint256[] calldata _dataTypes)
public
view
virtual
returns (uint8[] memory, bytes32[] memory)
{
...
}
If I point the remix editor to this contract and call the getNonce() function directly with the parameters:
0xa58d6abf78c9492df62daf0e251c6af49d5101eaadf2c4786e277a8da97f9f73 and [0]
I get my expected result.
However, I have a frontend application using Web3Js that attempts to call the getNonce function but I'm getting a completely different value (i.e. 0x00000000) which indicates that nothing is found:
export const getBlockchainStatus = async (walletSignature: string): Promise<boolean> => {
const MainContract = new web3.eth.Contract(
Ky0xMainAbi as AbiItem[],
CONTRACT_ADDRESS,
);
const hashwalletSig = ethers.utils.keccak256(walletSignature);
console.log('hashwalletSig:', hashwalletSig);
const response = await MainContract.methods.getNonces(hashwalletSig, ['0']).call();
console.log('response', response[1][0]);
return response[1][0] !== '0x0000000000000000000000000000000000000000000000000000000000000000';
};
The hashWalletSig matches the original value that I passed into the remix editor.
Can someone tell me what I'm doing wrong in Web3JS?
I am working on a simple bank smart contract example but I am having trouble getting the contract to restrict a sender from withdrawing more than the remaining balance. Here is my function within the contract:
function withdraw(uint withdrawAmount) public returns (uint) {
assert(balances[msg.sender] >= withdrawAmount);
balances[owner] -= withdrawAmount;
emit LogWithdrawal(msg.sender, withdrawAmount, balances[msg.sender]);
return balances[msg.sender];
}
Here is the .js test:
it("should not be able to withdraw more than has been deposited", async() => {
await instance.enroll({from: alice})
await instance.deposit({from: alice, value: deposit})
await catchRevert(instance.withdraw(deposit + 1, {from: alice}))
})
I was thinking maybe assert(.....) but that didn't work so any assistance would be appreciated.
First of all, you should use require instead of assert in this case. That being said your code seems ok, so make sure you are keeping track of the user balances correctly.
Your code is not consistent, you check the balance for msg.sender, but withdraw from owner.
It should be the correct version:
function withdraw(uint withdrawAmount) public returns (uint) {
require(balances[msg.sender] >= withdrawAmount);
balances[msg.sender] -= withdrawAmount;
emit LogWithdrawal(msg.sender, withdrawAmount, balances[msg.sender]);
return balances[msg.sender];
}
I am trying to subscribe to a PurchaseMade event defined in Solidty from the frontend. I am not getting the expected results and need help with what I'm doing wrong.
Environment:
ganache-cli, Truffle
web3.js, React.js
Initialing contract Instance:
export const getContractInstance = () => {
let web3Provider
if (typeof window.web3 !== 'undefined') {
// if metamask is on, web3 is injected...
web3Provider = web3.currentProvider
} else {
// otherwise, use ganache-cli...
web3Provider = new Web3.providers.HttpProvider('http://localhost:8545')
}
web3 = new Web3(web3Provider)
return new web3.eth.Contract(CryptoKpopAbi, CONTRACT_ADDRESS)
}
Subscribing to PurchaseMade event
onBuy = (obj) => {
web3.eth.subscribe("PurchaseMade", {}, () => {
debugger
});
this.ContractInstance.methods.buy(1).send({
from: this.state.currentUserAddress,
gas: GAS_LIMIT,
value: web3.utils.toWei(price.toString(), "ether"),
}).then((receipt) => {
console.log(receipt)
}).catch((err) => {
console.log(err.message)
})
}
I get this warning when I call web3.eth.subscribe:
Subscription "PurchaseMade" doesn't exist. Subscribing anyway.
I get this error on tx receipt (after send()` succeeds
Uncaught TypeError: Cannot read property 'subscriptionName' of undefined
I used this official doc to setup the subscription
http://web3js.readthedocs.io/en/1.0/web3-eth-subscribe.html
Thank you in advance!
UPDATE:
Event declaration in contract
event PurchaseMade(uint objId, uint oldPrice, uint newPrice, string objName, address prevOwner, address newOwner);
Event call in contract
function buy(uint _tokenId) payable public {
address prevOwner = ownerOf(_tokenId);
uint currentPrice = tokenIdToPrice[_tokenId];
...
PurchaseMade(_tokenId, currentPrice, newPrice,
tokens[_tokenId].name, prevOwner, msg.sender);
}
You're attempting to subscribe to the event itself. The API lets you subscribe to an event type and add filters. The valid event types are:
pendingTransactions: Receive a subset of new transactions sent to the blockchain (Primarily used for miners who want to be selective of the transactions they process)
newBlockHeaders: Receive notification when a new block has been added to the blockchain.
syncing: Receive notification when node syncing starts/stops
logs: Receive notification on log updates on the blockchain. These are the events you're interested in.
Look at the API documentation for examples on how to use subscribe("logs").
The subscribe API is usually used to listen to events occurring across the blockchain. An easier approach for listening to events for a specific contract is to use events for the deployed contract (documentation). It's not much different than using subscribe above, but it already has the contract address and topic filters.
this.ContractInstance.events.PurchaseMade({}, (error, data) => {
if (error)
console.log("Error: " + error);
else
console.log("Log data: " + data);
});
There's one important note, though. With web3 1.0, listening to events is not supported using HttpProvider. You have to use Websockets or IPC.
EDIT - I forgot to mention you can also get the events from the transaction receipt:
contractInstance.events.eventName.returnValues;