I'm starting to learn Solidity and how to interact with smart contracts within a Dapp, and I would like to know how can I log/handle errors from the contract in my Dapp.
For example, in the Dapp I have a button, when you click it, it runs a function in the contract, I have a require() there with a specific message: "Wait 15m", basically, the user can only click this button once every 15 minutes.
The button interaction is fired in a try/catch, if the user already clicked the button, so the next time will catch the error. My problem is the error that I'm getting in the console (to debug), it is like:
Error: transaction failed (transactionHash="0xe95c970115f5fe55202d04c2e0cd83c2ff67d3653f5397d7739764d685249da6", transaction={"hash":"0xe95c970115f5fe55202d04c2e0cd83c2ff67d3653f5397d7739764d685249da6","type":2,"accessList":null,"blockHash":null,"blockNumber":null,"transactionIndex":null,"confirmations":0,"from":"0xc6682cDC60a1e4603D051e48A2970E4859C62521","gasPrice":{"type":"BigNumber","hex":"0x59682f11"},"maxPriorityFeePerGas":{"type":"BigNumber","hex":"0x59682f00"}......
It is just a string with this bunch of code, without the error message specified in the require() which is the thing that is firing the error. This is the transaction in Rinkeby.
So, I imagine, in the future, I could have many functions in my contract with many require() with different messages. How can I handle these errors and show valuable information to the user in the UI? Is there any function that could help to get the proper messages?
I'm using:
Ethers - React
Thanks in advance for any help!
To extract error message from revert and require in the contract, please use provider.call to retrieve the error data. The following code works well with alchemy provider and goerli net. (ethers.js version 5.6.8)
const alchemyProvider = new ethers.providers.AlchemyProvider(
(network = "goerli"),
API_KEY
)
const tx = await alchemyProvider.getTransaction("0x5479d838aa66169470a61a8347727b75264e1d5d68d6749e5bd59b44d525dc21");
const code = await alchemyProvider.call(
{
to: tx.to,
from: tx.from,
nonce: tx.nonce,
gasLimit: tx.gasLimit,
gasPrice: tx.gasPrice,
data: tx.data,
value: tx.value,
chainId: tx.chainId,
type: tx.type ?? undefined,
accessList: tx.accessList,
},
tx.blockNumber,
)
const decodedError = SimpleAuction.interface.parseError(code)
console.log(decodedError)
console.log(`Transaction failed: ${decodedError.name}`)
The transaction info in etherscan which error encountered during contract revert execution. So the "code" is detailed and raw error info, and can be parsed and fromatted by interface. The output of error showed in the following.
ErrorDescription {
args: [
BigNumber { _hex: '0x01', _isBigNumber: true },
highestBid: BigNumber { _hex: '0x01', _isBigNumber: true }
],
errorFragment: {
type: 'error',
name: 'BidNotHighEnough',
inputs: [ [ParamType] ],
_isFragment: true,
constructor: [Function: ErrorFragment] {
from: [Function (anonymous)],
fromObject: [Function (anonymous)],
fromString: [Function (anonymous)],
isErrorFragment: [Function (anonymous)]
},
format: [Function (anonymous)]
},
name: 'BidNotHighEnough',
signature: 'BidNotHighEnough(uint256)',
sighash: '0x4e12c1bb'
}
The "BidNotHighEnough" is an reverted error defined in the contract. So that all the detailed erro info.
Related
I am trying to measure download (and later upload) speed using Axios in React Native. My question's relate to the most appropriate way to do so, and understanding if the _performanceLogger in the response from XMLHttpRequest is accurate for this purpose. There surprisingly doesn't seem to be much out there about _performanceLogger ... Appreciate the help!
A MWE is:
import React from "react";
import axios from 'axios';
// Dummy API endpoint
let requestURL = 'https://jsonplaceholder.typicode.com/users/2'
// Getting timestamp immediately before request is started
var startTimestamp = (new Date()).getTime()
// Make request through Axios
// Make a request for a user with a given ID
axios.get(requestURL)
.then(function (response) {
// Handle success
console.log("And I am burdened with glorious purpose.")
// Getting timestamp immediately after request is completed
let endTimestamp = (new Date()).getTime()
console.log("Request start time stamp: " + startTimestamp)
console.log("Request completed time stamp: " + endTimestamp)
// Logging the whole _performanceLogger object
let responsePerformanceLogger = response.request._performanceLogger
console.log('Perf Logger:')
console.log(responsePerformanceLogger)
// Logging the key/value specific to the actual request within the _performanceLogger object
let requestPerfString = 'network_XMLHttpRequest_' + requestURL
let responseTimespans = responsePerformanceLogger._timespans[requestPerfString]
console.log('Specific Request Timings:')
console.log(responseTimespans)
})
.catch(function (error) {
// Handle error
console.log("This is a mistake! I shouldn't be here!")
console.log(error);
})
.then(function () {
// always executed
console.log('For all time! Always!')
});
I use Expo to run this code on my device, and I get the following output to the console:
And I am burdened with glorious purpose.
Request start time stamp: 1640229022039
Request completed time stamp: 1640229022156
Perf Logger:
PerformanceLogger {
"_closed": false,
"_extras": Object {},
"_pointExtras": Object {},
"_points": Object {
"initializeCore_end": 194691080.25150004,
"initializeCore_start": 194691027.19495836,
},
"_timespans": Object {
"network_XMLHttpRequest_http://192.168.1.40:19000/logs": Object {
"endExtras": undefined,
"endTime": 194692352.89145836,
"startExtras": undefined,
"startTime": 194691383.22187504,
"totalTime": 969.6695833206177,
},
"network_XMLHttpRequest_http://192.168.1.40:19001/symbolicate": Object {
"endExtras": undefined,
"endTime": 194694267.96945837,
"startExtras": undefined,
"startTime": 194694100.6875417,
"totalTime": 167.2819166779518,
},
"network_XMLHttpRequest_https://jsonplaceholder.typicode.com/users/2": Object {
"endExtras": undefined,
"endTime": 194699846.2774167,
"startExtras": undefined,
"startTime": 194699738.0606667,
"totalTime": 108.21674999594688,
},
},
}
Specific Request Timings:
Object {
"endExtras": undefined,
"endTime": 194699846.2774167,
"startExtras": undefined,
"startTime": 194699738.0606667,
"totalTime": 108.21674999594688,
}
For all time! Always!
My specific questions are:
What are the different objects under the _timespans key? There are two (log, symbolicate) to the local IP for Expo ... what's happening here? Presumably these won't there when I deploy outside of Expo.
Is the third, the one that contains the actual URL made in the request, the actual timing of the download? (I've extracted this one as Specific Request Timings.) This is total download time (latency + file download), correct?
Is relying on the XMLHttpRequest timing less/more accurate than just using the simple getTime() timestamps that I have called before the request and after successful completion? I would expect that they should be close, but the getTime()'s require some finite execution time that could be variable by device/load.
I was following tutorial: https://blog.bitsrc.io/make-a-simple-react-app-with-using-youtube-api-68fa016e5a03.
I'm a noob so please, if you can help, pretend my name is Layman
I had actually finished, made it beautiful and was adding some additional things (like dislike buttons that dont do anything but look the part)
But now none of it works. I've even reverted my commits really far back to points where I know for show it works but I'm getting the same error. I can only assume its with youtube's api key but I dont know whats wrong. I noticed the issue when i tried to change the maxResults from 5 to 20. It did like that and it hasnt worked since.
I've made a new key, and used a try catch (i think ive done it right) and it still doesnt work
handle submit in App.jsx
handleSubmit = async (termFromSearchbar) => {
// alert(termFromSearchbar);
try {
const res = await youtube.get("/search", {
params: {
part: "snippet",
maxResults: 5,
key: process.env.REACT_APP_API_KEY,
q: termFromSearchbar,
},
});
this.setState({
videos: res.data.items,
selectedVideo: res.data.items[0],
});
} catch (error) {
console.error(error);
}
};
my .env file
import axios from "axios";
export default axios.create({
baseURL: "https://www.googleapis.com/youtube/v3",
params: {
part: "snippet",
maxResults: 5,
key: `${process.env.REACT_APP_API_KEY}`,
},
});
One possible explanation is that you've exceeded your quota (https://developers.google.com/youtube/v3/docs/errors)
You should verify that first. This document describes how you can do that (https://developers.google.com/youtube/v3/getting-started#quota)
I think this could be a bug or a misconfiguration.
I get this error:
networkError: ServerError: Response not successful: Received status code 500
Submitting the form the first time I would get the desired result, but if I hit the submit button again, I get the networkError message above.
const client = useApolloClient();
const [val, setValu] = useState({
email: 'example#example.com',
password: 'password',
texterror: {
status: false,
text: ''
},
})
//the submit function below:
const handleLogin = async (e) => {
e.preventDefault();
await client.query({
query: Handle_Login,
variables: {
email: val.email,
password: val.password
}
}).then((e) => {
console.log(e)
}).catch((e) => {
setValu({
texterror: {
status: true,
text: 'it be broke yo'
}
})
})
}
However, if I remove setValu({texterror: {status: true, text: 'it be broke yo'}}) in the catch, The 500 error goes away. I could spam the submit button and I wouldn't receive the 500 error.
For right now I'm going to not have a setState inside just to avoid this problem, but I would like to know if there is a solution to this issue.
Any help would be appreciated. Thank you in advance!
At the top level, it's clear that you're causing some kind of error on the server side. A 500 code means there is an "internal server error". So you should really look at your server code and figure out how to safeguard against unexpected input so that it won't error and/or crash but instead return code 400 which is "bad request".
So the question becomes, what about your frontend code is causing a malformed server request?
I suspect that you're trying to use setValu() (which is a React hook) the same way one might use a traditional setState call in a class component. However, they behave quite differently.
In a class component, setState performs a "shallow merge" of the old state and the new state. So if you did:
setState({
texterror: {
status: true,
text: 'it be broke yo'
}
});
If would find only the field texterror on the state object and update that.
However, React hooks work differently. useState() creates a single atomic value, and when you call setValu() it complete replaces the previous value stored in val.
So:
const [val, setValu] = useState({
email: 'example#example.com',
password: 'password',
texterror: {
status: false,
text: ''
},
});
// val is now an object with 'email', 'password', and 'texterror' fields
setValu({
texterror: {
status: true,
text: 'it be broke yo'
}
});
// val is now an object with only a 'texterror' field
In your code when you are using setValu you are wholly replacing the value of val to something that doesn't have an email or password field, which when sent to the server causes an internal server error. A simple fix would be to simply merge the old value of val with the desired new object when you update it:
setValu({
...val,
texterror: {
status: true,
text: 'it be broke yo'
}
});
Remember that with the useState hook you are overwriting the entire state val. This means that you have no longer got a email and password. It is a common oversight when moving from class based components to hooks. Add the parts of the state that didn't change too, and it should all work again.
I am trying send a request with openrouteservice, when I sent the coordinates I receive a error response
I am using react with the openrouteservice-js library
getResponse = async () => {
try {
const response = await Directions.calculate({
coordinates: [[9.930808,-84.052448],[9.933302,-84.056493]],
attributes: ['detourfactor'],
profile: 'driving-car',
extra_info: ['waytype', 'steepness'],
avoidables: ['highways', 'tollways', 'ferries', 'fords'],
instructions: true,
instructions_format: 'text',
preference: 'recommended',
units: 'km',
language: 'es',
format: 'json'
});
console.log(response);
this.setState({ response })
} catch (e) {
console.log(e, 'error');
}
}
Example map: https://maps.openrouteservice.org/directions?n1=9.933376&n2=-84.05461&n3=17&a=9.930808,-84.052448,9.933302,-84.056493&b=0&c=0&k1=en-US&k2=km
If I put the same coordinates in the example map the route is work fine, but when I sent the request I get this response error:
{
"error": {
"code": 2010,
"message": "Point 0 is out of bounds: -84.052448,9.930808"
}, "info": {
"engine": {
"version": "5.0.1",
"build_date": "2019-05-24T16:27:11Z",
},
"timestamp": 1558719789443
}
}
Not sure if this is still active, but I'll leave this answer here for others who might have this problem...
Many Geographical APIs accept [latitude, longitude] pairs, but Open Routes Service accepts [longitude, latitude]. If you swap them, the locations will snap to completely different locations, and if they happen to be in the ocean or outside of an inhabited area (mine was in Antarctica), this error message will be returned.
As the comment on the question said, though, it's also possible to get this error message through using the wrong map on your local version of the Open Routes Service.
The UI should have a global error handler that shows a popup message whenever an error is received through the API. I'm trying but I'm not getting, I did not find any example too. This should be done Marionette.js. Please help
I got a json file:
{
"errorcodes": [{
"message": "Invalid Email/Password Combination",
"reason": "com.catt.exceptions.catttCustomerPreferencesException: Invalid Email/Password Combination\r\n\tat com.catt.v1.controller.CustomersController.customerLogin(CustomersController.java:303)\r\n\tat sun.reflect.GeneratedMethodAccessor1008.invoke(Unknown Source)\r\n\tat sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\r\n\tat java.lang.reflect.Method.invoke(Method.java:606)\r\n\tat org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerM...",
"type": "tCustomerPreferencesError"
}]
}
You can use $.ajaxError to listen for any error happening in $.ajax.
From there, you can make make a Marionnette Application (for example), handle the error, and display an alert
var App = new Marionette.Application();
App.vent.on('error', function(event, jqxhr){
alert(jqxhr.responseText);
});
$(document).ajaxError(function(event, jqxhr, settings, thrownError){
App.vent.trigger('error', event, jqxhr, settings, thrownError);
});
Fiddle Here : http://jsfiddle.net/8ff4n9ut/