I have a contract with the following function:
function supply () constant returns (uint sup) {
sup = 100;
return sup;
}
Running
var token = web3.eth.contract(contractAbi).at(contractAddress);
token.supply.call()
returns:
{ [String: '0'] s: 1, e: 0, c: [ 0 ] }
What's wrong here? This is happening with all my functions in the contract.
Thanks!
You're getting "BigNumber" format which is always the case for uint(256) because the numbers are larger than Javascript can actually handle.
Consider playing with the returned value format. Examples/docs here: https://github.com/ethereum/wiki/wiki/JavaScript-API#a-note-on-big-numbers-in-web3js
Truffle style (promise)
token.supply.call().then(function(returned) {
console.log(returned.toString(10));
}
Callback style
token.supply.call(function(error, returned) {
if(!error) {
console.log(returned.toString(10));
} else {
console.error(error);
});
Above examples will possibly loose some precision. From the doc linked above ...
it is recommended to keep you balance always in wei and only transform
it to other units when presenting to the user:
Hope it helps.
Update:
The contract works for me in Remix, so I would focus on the way you're calling the function and waiting for the response.
Related
I have a Rinkeby Metamask account that I would like to get students to pay me 10 Ether, to show that they have understood the following principles as part of a challenge:
Creation of an online wallet that deals with ethereum
Ability to get enough free ethereum to complete the payment
Finally pay the total in one payment
Once this payment has gone through I would like to give them a text string say "Well_Done_101" that would prove they have completed the challenge.
I understand that smart contracts are available but I'm not sure how this would work as I would want the text string to be invisible until the payment has been completed.
This is some code that I tried earlier:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
contract payment_for_art {
// default value is `false`, don't need to explicitly state it
bool isPaid;
function winner() public view returns (string memory) {
return flag;
}
function hidden() private view returns (string memory) {
string memory flag_true;
flag_true = 'Well_done_101';
return flag;
}
function invest() external payable {
// to prevent multiple payments
// reverts if the condition is not met
require(isPaid == false);
if(msg.value < 10 ether) {
revert('Pay me the full amount');
}
if(isPaid = true) { // flag that the payment has been done
//return ('Well_Done_101');
return flag_true;
}
}
function balance_of() external view returns(uint) {
return address(this).balance;
}
}
Any ideas or comments would be greatly appreciated.
I think what you're searching for is something like this.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
contract payment_for_art {
// Mappings of studentNames to flag; Its like a key => value list data type.
// Public storage variables can be accessed by anyone (solidity creates a getter implicitly), but can't be modified directly.
mapping (string => string) public buyers;
// Recieves the payment, and also the students name (your students must call this function with their name as a parameter).
function invest(string memory studentName) external payable {
// check if "studentname" isn't empty.
require(bytes(studentName).length != 0, "You forgot to specify your student name.");
// check if the payment is 10 eth.
require(msg.value == 10 ether, "You are either paying too much, or too little.");
// check if the student already bought, by checking if his flag is set in the mapping.
require(bytes(buyers[studentName]).length == 0, "You already bought the art.");
// set flag for student.
// While regular string literals can only contain ASCII,
// Unicode literals โ prefixed with the keyword unicode โ can contain any valid UTF-8 sequence (this allows you to use emogis and whatnot).
// They also support the very same escape sequences as regular string literals.
buyers[studentName] = unicode"Well_Done_101 ๐";
}
function balance_of() external view returns(uint) {
return address(this).balance;
}
}
And to check if they bought, just call the mapping and search for your students name.
Example made using web3:
payment_for_artContract.methods.invest("Daniel Jackson")
.send({ from: account, value: weiValue})
.on('transactionHash', (hash) => {
const studentsWhoBought = await payment_for_artContract.methods.buyers().call();
// prints "Well_Done_101 ๐";
console.log(studentsWhoBought["Daniel Jackson"]);
console.log(studentsWhoBought);
})
.on('error', (err) => {
console.error(err);
})
If you want to actually get a value from it when its called, you'll need to use events (triggers that clients can subscribe to) and have web3 subscribe to said event.
For example:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
contract payment_for_art {
// Mappings of studentNames to flag; Its like a key => value list data type.
// Public storage variables can be accessed by anyone (solidity creates a getter implicitly), but can't be modified directly.
mapping (string => string) public buyers;
// Event of buying.
event Bought(string studentName, address studentAddress, string flag);
// Recieves the payment, and also the students name (your students must call this function with their name as a parameter).
function invest(string memory studentName) external payable {
// check if "studentname" isn't empty.
require(bytes(studentName).length != 0, "You forgot to specify your student name.");
// check if the payment is 10 eth.
require(msg.value == 10 ether, "You are either paying too much, or too little.");
// check if the student already bought, by checking if his flag is set in the mapping.
require(bytes(buyers[studentName]).length == 0, "You already bought the art.");
// set flag for student.
// While regular string literals can only contain ASCII,
// Unicode literals โ prefixed with the keyword unicode โ can contain any valid UTF-8 sequence (this allows you to use emogis and whatnot).
// They also support the very same escape sequences as regular string literals.
buyers[studentName] = unicode"Well_Done_101 ๐";
emit Bought(studentName, msg.sender, unicode"Well_Done_101 ๐");
}
function balance_of() external view returns(uint) {
return address(this).balance;
}
}
And then in web3:
payment_for_artContract.methods.invest("Daniel Jackson")
.send({ from: account, value: weiValue})
.on('transactionHash', (hash) => {
console.log("transaction mined");
})
.on('error', (err) => {
console.error(err);
});
const options = {
fromBlock: 0, // Number || "earliest" || "pending" || "latest"
toBlock: 'latest'
};
payment_for_artContract.events.Bought(options)
// prints the student's name who bought, address of the student, and "Well_Done_101 ๐";
.on('data', event => console.log(event))
.on('changed', changed => console.log(changed))
.on('error', err => throw err)
.on('connected', str => console.log(str));
I would stick with the first method, since its easier, yet the second method is how it should be properly done.
Try that, and let me know if thats what you were searching for :)
I have written an async Flutter/Dart function which behaves unexpectedly in my opinion. Following code structure:
static Future<bool> verifySometing() async {
try {
await getCloudData().then((snapshot) {
if (snapshot.exists && snapshot.hasData) {
bool dataValid = await validateData(snapshot.data);
if (dataValid) {
print('Data is correct');
return true;
}
}
});
} catch (e) {
print('Error $e');
return false;
}
print('Something went wrong');
return false;
}
The expected result would be that the function awaits the cloud data, then awaits validation and returns true if the data is valid. In this case, the console would show the following and the function would return true:
Data is correct
What happens in practice is that the console shows the following output and the function first returns true and then false:
Data is correct
Something went wrong
This goes against anything I thought to know about funtions in Dart because I always assumed that once return is fired, the function is done. Any ideas how this happens?
The issue is with this line.
await getCloudData().then((snapshot) {
Here, instead of just awaiting, you have also attached a then callback. So in actuality, whatever you are returning is return value of the callback function ie., (snapshot) {}.
The callback function that you need to pass into the then takes that return true and gives it to us as the result of await.
So, if you would've put something like
var bool = await getCloudData().then((snapshot) { ..... });
Then this bool, would've been equal to true. That's it. No return from the your main function.
Change it to this,
var snapshot = await getCloudData();
if (snapshot.exists && snapshot.hasData) {
bool dataValid = await validateData(snapshot.data);
if (dataValid) {
print('Data is correct');
return true;
}
}
Hope, I was able to explain clearly.
There are a few of faults in your assumptions.
First, you've attached a then to the future returned by getCloudData(). This means that you aren't awaiting getCloudData(), but instead the additional future returned by getCloudData().then(...), and that future will return when the callback function passed to it completes. (And unless the first future throws an error, the callback will be called.)
Second, the callback function operates on its own scope. So this code is not doing what you think it's doing:
bool dataValid = await validateData(snapshot.data);
if (dataValid) {
print('Data is correct');
return true;
}
This return will affect the callback function, not the verifySomething function.
Given these, the order of operation is as follows:
The validateSomething function awaits getCloudData().then(...).
getCloudData() gets called.
getCloudData() returns, the callback passed to then is called.
(Assuming the snapshot has data) validateData is called.
(Assuming data is successfully validated) "Data is correct" gets printed and the callback function returns true.
validateSomething is notified that the awaited future is complete, so execution resumes.
"Something went wrong" gets printed and the validateSomething function returns false.
Generally speaking, these kinds of errors are common when mixing async/await and then patterns. Unless you know what you're doing, stick with either one or the other, preferably the async/await pattern. For example, a refactor of your code to eliminate the call to then is as follows:
static Future<bool> verifySometing() async {
try {
final snapshot = await getCloudData();
if (snapshot.exists && snapshot.hasData) {
bool dataValid = await validateData(snapshot.data);
if (dataValid) {
print('Data is correct');
return true;
}
}
} catch (e) {
print('Error $e');
return false;
}
print('Something went wrong');
return false;
}
Now that there isn't a pesky closure to deal with, return will return from validateSomething as expected and you don't need to deal with issues like callbacks and scope.
I want to use web3.js to show revert reason to user, for example in the case of user trying to mint erc721 token that has already been minted. I am using try catch block and see the error message but I want to isolate the error message to show the user a meaningful reason. Thanks in advance.
The previous answer by #Petr Hejda didn't work for me, and neither did his suggestion in response to #Chakshu Jain's problem in the comments.
Instead, I removed some charactersโfrom the start and the end, with slice()โthat were causing the error when parsing the JSON, so I could handle the error message and get the error message.
if (err) {
var errorMessageInJson = JSON.parse(
err.message.slice(58, err.message.length - 2)
);
var errorMessageToShow = errorMessageInJson.data.data[Object.keys(errorMessageInJson.data.data)[0]].reason;
alert(errorMessageToShow);
return;
}
It's returned in the JS error object as data.<txHash>.reason.
This is a faulty Solidity code
pragma solidity ^0.8.0;
contract Test {
function foo() public {
revert('This is error message');
}
}
So a transaction calling the foo() function should revert with the message This is error message.
try {
await myContract.methods.foo().send();
} catch (e) {
const data = e.data;
const txHash = Object.keys(data)[0]; // TODO improve
const reason = data[txHash].reason;
console.log(reason); // prints "This is error message"
}
After trying out every solution on stackoverflow, random blogs, and even the officially documented "web3.eth.handleRevert = true", none is working for me.
I finally figured out after 25 failed attempts:
try {
await obj.methods.do_something().call({
gasLimit: String(GAS_LIMIT),
to: CONTRACT_ADDRESS,
from: wallet,
value: String(PRICE),
})
}
catch (err) {
const endIndex = err.message.search('{')
if (endIndex >= 0) {
throw err.message.substring(0, endIndex)
}
}
try {
const res = await obj.methods.do_something().send({
gasLimit: String(GAS_LIMIT),
to: CONTRACT_ADDRESS,
from: wallet,
value: String(PRICE),
})
return res.events.Transfer.returnValues.tokenId
}
catch (err) {
console.error(err)
throw err
}
The idea is to use call first. This method doesn't interact with your Metamask, but merely checks if your input arguments go through the contract method. If it can't go through, it will throw exception in the first catch block. If it does go through, we are safe to do use send. This method interacts with your Metamask for real. We have a second catch block in case there are wallet connection or gas fee issues
It is really perplexing why Solidity/Web3 don't have an easy way to extract the require/revert reason from the error object.
For me, the "require" reason is there in the message property of the error object, but it is surrounded by lot of other words which I don't need.
An example error message:
[ethjs-query] while formatting outputs from RPC '{"value":{"code":-32603,"data":{"message":"VM Exception while processing transaction: revert Voting is closed","code":-32000,"data":{"0xf901429f12096d3b5c23a80e56fd2230fa37411bb1f8d3cdbd5c8f91c2670771":{"error":"revert","program_counter":43,"return":"0x08c379a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000165f5f5f566f74696e6720697320636c6f7365645f5f5f00000000000000000000","reason":"Voting is closed"},"stack":"RuntimeError: VM Exception while processing transaction: revert Voting is closed \n at Function.RuntimeError.fromResults (/tmp/.mount_ganachreY1gT/resources/static/node/node_modules/ganache-core/lib/utils/runtimeerror.js:94:13)\n at BlockchainDouble.processBlock (/tmp/.mount_ganachreY1gT/resources/static/node/node_modules/ganache-core/lib/blockchain_double.js:627:24)\n at runMicrotasks (<anonymous>)\n at processTicksAndRejections (internal/process/task_queues.js:93:5)","name":"RuntimeError"}}}}'
You can see the reason Voting is closed stuck in between. Not that user-friendly to read.
I've seen answers that use regex to extract the error reason.
For those like me, who are not a big fan of the regex way, here is my approach.
In your solidity contract, wrap the require reason with a unique delimiter of sorts. In my case, it is "___" (3 underscores).
contract MyContract{
...
...
function vote(address _addr) public payable{
require(votingOpen, "___Voting closed___");
...
}
...
...
}
Declare a helper function to extract the error using JavaScript string utilities. Here's where your delimiter coes in handy.
export const extractErrorCode = (str) => {
const delimiter = '___'; //Replace it with the delimiter you used in the Solidity Contract.
const firstOccurence = str.indexOf(delimiter);
if(firstOccurence == -1) {
return "An error occured";
}
const secondOccurence = str.indexOf(delimiter, firstOccurence + 1);
if(secondOccurence == -1) {
return "An error occured";
}
//Okay so far
return str.substring(firstOccurence + delimiter.length, secondOccurence);
}
Use this function where you catch the error in your frontend
const vote = async (_addr) => {
setLoading(true);
try {
await contest.methods.vote(_addr).send({
from: accounts[0],
})
}
catch (e) {
console.log('Voting failed with error object => ', e)
console.log('Voting failed with the error => ', extractErrorCode(e.message))
}
setLoading(false);
}
Until Solidity & Web3.js (and ether.js) come out with a clean way to parse errors, we are stuck with workarounds like this.
I prefer this workaround over others because I am not that great with regex, and additionally, this one does not depend on a fixed starting position to extract the error code.
Did you try something like this?
error.toString()
It works for me just to show the revert error in the Smart Contract, and return it as a string message.
try {
//Do something
} catch (error) {
res.send({
'status': false,
'result': error.toString()
});
}
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.
I suppose that the answer will be very obvious, but still it evades me. I'm new on working with observables, and now I'm facing issues assigning a value from one. I had success if I define it (this._apps) as an Observable and asking from the view to the service using subscribe (But for my taste is was way convoluted (three levels inside a map just to return another observable with the array and then another function to subscribe the previous to assign the variable and another subscription in the view to finally show the information), inefficient and on top of that I could not get it "right" again). The task is very simple. Given the class Application
export class Application {
name: string;
baseUrl: string;
deprecated: boolean;
}
And the service (just the relevant code)
private _apps: Application[] = [];
constructor(private _http: HttpClient) {
this.getAllApplications().subscribe(apps => {
console.log('Apps subscriber');
this._apps = apps;
console.log('Apps subscriber Ends ' + apps);
},
err => {
console.log(err.status); // 401
console.log(err.error.error); // undefined
console.log(JSON.parse(err.error).error); // unauthorized
});
}
private getAllApplications() {
return this._http.get<Application[]>('http://development:4300/api/v1/apps');
}
From the constructor the function which gets the information from WebAPI is triggered, and the remote call is successful, but the variable this._apps is an empty array if I try to call it from anywhere in the code. I could not determine the type of the parameter "apps" in the subscribe function, but for some reason it cannot be assigned and the best answer given is that it is a function (See my first update) in one of my tries. Currently it returns in the console "[object Object]", but apps[0] gives undefined, so it is an empty Array.
This is the console output, just starting the application:
Angular is running in the development mode. Call enableProdMode() to enable the production mode.
Refreshing apps cache calling http://development:4300/api/v1/atbc-apps
Apps subscriber
Apps subscriber Ends [object Object]
I was trying this solution among many others that I forget (to use the more modern HttpClient instead the Http I used before), so what I'm doing wrong?
Update 1
I changed the constructor to this:
constructor(private _http: HttpClient) {
this.getAllApplications().subscribe(apps => {
console.log('apps length ' + apps.length);
this._apps = apps; // Remember private _apps: Application[] = [];
console.log('Apps subscriber Ends ' + apps.toString);
},
err => {
console.log(err.status); // 401
console.log(err.error.error); // undefined
console.log(JSON.parse(err.error).error); // unauthorized
});
}
and the declaration of the function called into this:
private getAllApplications(): Observable<Application[]> {
// the exactly the same as before
}
And now I got from the console this:
apps length undefined
Apps subscriber Ends
function () {
if (this instanceof Promise) {
return PROMISE_OBJECT_TO_STRING;
}
return originalObjectToString.apply(this, arguments);
}
That is the function I was talking about. Any ideas about why even though there is no errors (nor at compile time, neither at runtime), the returning object is not a real Application array?
Change this line:
private _apps: Application[] = [];
to:
_apps: Application[] = [];
Which will default to making it public. Then this line will see it:
this._apps = apps;
At the end I suppose is a mindset to work with Observables, and I tried to build a kind of cache, so the only way I could do it (let me know if there is a better way) was using the view to fill-out the cache. I could not do it from the service itself because the calling the function from the view is synchronous and to fill out the array is async. So I had to create a public setApplicationCache procedure which is filled out after calling the service from the view, it call the setApplicationCache( Application[] ) function and the rest works because it takes just the cache to do filtering and other operations or use it from other pages w/o calling the database again and again.
This is the code from the first view called (main page)
ngOnInit() {
this._myService.getAllApplications().subscribe(arrObjApps => {
this._myService.setApplicationsCache(arrObjApps)
this.listApps = this._myService.getApplications(true);
});
And the service has this functions:
private _apps: Application[] = [];
getAllApplications(): Observable<Application[]> {
return this._http.get('http://development:4300/api/v1/atbc-apps').pipe(
map( (response: Response) => {
let results = response.json().data.map( app => {
return new Application(app.name, app.baseUrl, app.deprecated);
});
return results;
})
);
}
getApplication(appName: string): Application {
return this._apps.find(app => app.name == appName);
}
getApplications(onlyActives: boolean): Application[] {
if (onlyActives) {
return this._apps.filter(app => app.deprecated == false);
} else {
return this._apps;
}
}
And as I stated the solution should be obvious. Just again the async mindset required to work with observables.