I've been studying solidity and I've been looking at similar projects already on mainnet ethereum via etherscan. I'm trying to understand what values were used for certain functions of a contract. When I look at it I see stuff like this
Function: someUintFunction1(uint256 maxTxAmount)
MethodID: 0xec28438a
[0]: 0000000000000000000000000000000000000000000000a2a15d09519be00000
Function: someUintFunction2(uint256 _minimumTokensBeforeSwap)
MethodID: 0xf0f165af
[0]: 000000000000000000000000000000000000000000000002b5e3af16b1880000
=
Function: someBoolFunction(bool _enabled)
MethodID: 0xc49b9a80
[0]: 0000000000000000000000000000000000000000000000000000000000000000
I guess 0000000000000000000000000000000000000000000000000000000000000000 as a bool is false?
but how can I decode 0000000000000000000000000000000000000000000000a2a15d09519be00000 and 000000000000000000000000000000000000000000000002b5e3af16b1880000 to a readable value?
but how can I decode 0000000000000000000000000000000000000000000000a2a15d09519be00000 and 000000000000000000000000000000000000000000000002b5e3af16b1880000 to a readable value?
These are values of the uint256 params, converted to hexadecimal, displayed using big endian (the largest value is left).
You can convert it to decimal manually, or here's an example using web3 JS library:
const Web3 = require('web3');
const web3 = new Web3();
const decimal = web3.eth.abi.decodeParameter('uint256', '0000000000000000000000000000000000000000000000a2a15d09519be00000');
console.log(decimal); // prints 3000000000000000000000
Related
msg := ethereum.CallMsg{
To: &contractAddr,
Gas: uint64(21000),
GasTipCap: tipCap,
GasFeeCap: feeCap,
Data:
}
client.CallContract(context.Background(), msg, nil)
I want to use this API to call smart contracts, but I don't know what to fill in the "Data" field. Can someone provide an example for my reference? Thans very much!
Here ethereum.CallMsg is struct this
type CallMsg struct {
From common.Address // the sender of the 'transaction'
To *common.Address // the destination contract (nil for contract creation)
Gas uint64 // if 0, the call executes with near-infinite gas
GasPrice *big.Int // wei <-> gas exchange ratio
GasFeeCap *big.Int // EIP-1559 fee cap per gas.
GasTipCap *big.Int // EIP-1559 tip per gas.
Value *big.Int // amount of wei sent along with the call
Data []byte // input data, usually an ABI-encoded contract method invocation
AccessList types.AccessList // EIP-2930 access list.
}
and here Data have a type of []byte
but I don't know what to fill in the "Data" field
in the Data field, you need to provide ABI-encoded contract method invocation
To create ABI-encoded contract method you need to import
"github.com/ethereum/go-ethereum/accounts/abi" and use abi.Pack method
for your reference, you can look at test cases abi_test.go
Consider the following interface within TypeScript
interface IApiCall<TResponse> {
method: string;
url: string;
}
Which is then used within the following method;
const call = <TResponse>(api: IApiCall<TResponse>): void => {
// call to API via ajax call
// on response, grab data
// use JSON.parse(data) to convert to json object
return json as TResponse;
};
Now we use this for Type safety within our methods so we know what objects are being returned from the API. However, when we are returning a single string from the API, JSON.parse is converting the string '12345' into a number, which then breaks further down the line when we are trying to treat this as a string and use value.trim() yet it has been translated into a number.
So ideas to solve this so that we are not converting a string into a number.
How can we stop JSON.parse from converting a single string value into a number?
If using JSON.parse, we check the type of TResponse and compare it against the typeof of json generated.
if (typeof (json) !== typeof(TResponse))...
However there doesn't seem to be an obvious way to determine the generic type.
Question 1: How can we stop JSON.parse() from converting a single string value into a number?
JSON is a text format, so in JSON.parse(x), x needs to be a string. But JSON text represents data of not-necessarily-string types. It sounds like you might be making a category mistake, by confusing a thing with its representation.
If you convert the number 12345 to JSON (JSON.stringify(12345)) you will get the string "12345". If you parse that string, (JSON.parse("12345")), you will get the number 12345 back. If you wanted to get the string "12345", you need to encode it as JSON ( JSON.stringify("12345")) as the string "\"12345\"". If you parse that ( JSON.parse('"12345"') you will get the string "12345" out.
So the straightforward answer to the question "How can we stop JSON.parse() from converting a single string value into a number" is "by properly quoting it". But maybe the real problem is that you are using JSON.parse() on something that isn't really JSON at all. If you are given the string "12345" and want to treat it as the string "12345", then you don't want to do anything at all to it... just use it as-is without calling JSON.parse().
Hope that helps. If for some reason either of those don't work for you, you should post more details about your use case as a Minimal, Complete, and Verifiable example.
Question 2: How do we determine that the returned JSON-parsed object matches the generic type?
In TypeScript, the type system exists only at design time and is erased in the emitted JavaScript code that runs later. So you can't access interfaces and type parameters like TResponse at runtime. The general solution to this is to start with the runtime solution (how would you do this in pure JavaScript) and help the compiler infer proper types at design time.
Furthermore, the interface type IApiCall
interface IApiCall<TResponse> {
method: string;
url: string;
}
has no structural dependence on TResponse, which is not recommended. So even if we write good runtime code and try to infer types from it, the compiler will never be able to figure out what TResponse is.
In this case I'd recommend that you make the IApiCall interface include a member which is a type guard function, and then you will have to write your own runtime test for each type you care about. Like this:
interface IApiCall<TResponse> {
method: string;
url: string;
validate: (x: any) => x is TResponse;
}
And here's an example of how to create such a thing for a particular TResponse type:
interface Person {
name: string,
age: number;
}
const personApiCall: IApiCall<Person> = {
method: "GET",
url: "https://example.com/personGrabber",
validate(x): x is Person {
return (typeof x === "object") &&
("name" in x) && (typeof x.name === "string") &&
("age" in x) && (typeof x.age === "number");
}
}
You can see that personApiCall.validate(x) should be a good runtime check for whether or not x matches the Person interface. And then, your call() function can be implemented something like this:
const call = <TResponse>(api: IApiCall<TResponse>): Promise<TResponse | undefined> => {
return fetch(api.url, { method: api.method }).
then(r => r.json()).
then(data => api.validate(data) ? data : undefined);
};
Note that call returns a Promise<Person | undefined> (api calls are probably asynchronous, right? and the undefined is to return something if the validation fails... you can throw an exception instead if you want). Now you can call(personApiCall) and the compiler automatically will understand that the asynchronous result is a Person | undefined:
async function doPersonStuff() {
const person = await call(personApiCall); // no <Person> needed here
if (person) {
// person is known to be of type Person here
console.log(person.name);
console.log(person.age);
} else {
// person is known to be of type undefined here
console.log("File a missing Person report!")
}
}
Okay, I hope those answers give you some direction. Good luck!
Type annotations only exist in TS (TResponse will be nowhere within the output JS), you cannot use them as values. You have to use the type of the actual value, here it should be enough to single out the string, e.g.
if (typeof json == 'string')
In the Current code i'm using json-c. i'm migrating to jansson.
need an equivalent api in jansson which converts the json_object_to_json_string.
i found one but it needs a json string object otherwise it is returning null.
const char *json_string_value(const json_t *string) - not working
but my input is a JSON object not a JSON string
sample:
json_object *jobj = json_object_new_object();
....
const char *final_string = json_object_to_json_string(jobj);
Thanks.
I was in your position just recently, I believe the function you are looking for is:
char *json_dumps(const json_t *json, size_t flags)
Returns the JSON representation of json as a string, or NULL on error. flags is described above. The return value must be freed by the caller using free().
https://jansson.readthedocs.io/en/2.8/apiref.html#c.json_dumps
This question is multipart-
(1a) JSON is fundamental to JavaScript, so why is there no JSON type? A JSON type would be a string that is formatted as JSON. It would be marked as parsed/stringified until the data was altered. As soon as the data was altered it would not be marked as JSON and would need to be re-parsed/re-stringified.
(1b) In some software systems, isn't it possible to (accidentally) attempt to send a plain JS object over the network instead of a serialized JS object? Why not make an attempt to avoid that?
(1c) Why can't we call JSON.parse on a straight up JavaScript object without stringifying it first?
var json = { //JS object in properJSON format
"baz":{
"1":1,
"2":true,
"3":{}
}
};
var json0 = JSON.parse(json); //will throw a parse error...bad...it should not throw an error if json var is actually proper JSON.
So we have no choice but to do this:
var json0= JSON.parse(JSON.stringify(json));
However, there are some inconsistencies, for example:
JSON.parse(true); //works
JSON.parse(null); //works
JSON.parse({}); //throws error
(2) If we keep calling JSON.parse on the same object, eventually it will throw an error. For example:
var json = { //same object as above
"baz":{
"1":1,
"2":true,
"3":{}
}
};
var json1 = JSON.parse(JSON.stringify(json));
var json2 = JSON.parse(json1); //throws an error...why
(3) Why does JSON.stringify infinitely add more and more slashes to the input? It is not only hard to read the result for debugging, but it actually puts you in dangerous state because one JSON.parse call won't give you back a plain JS object, you have to call JSON.parse several times to get back the plain JS object. This is bad and means it is quite dangerous to call JSON.stringify more than once on a given JS object.
var json = {
"baz":{
"1":1,
"2":true,
"3":{}
}
};
var json2 = JSON.stringify(json);
console.log(json2);
var json3 = JSON.stringify(json2);
console.log(json3);
var json4 = JSON.stringify(json3);
console.log(json4);
var json5 = JSON.stringify(json4);
console.log(json5);
(4) What is the name for a function that we should be able to call over and over without changing the result (IMO how JSON.parse and JSON.stringify should behave)? The best term for this seems to be "idempotent" as you can see in the comments.
(5) Considering JSON is a serialization format that can be used for networked objects, it seems totally insane that you can't call JSON.parse or JSON.stringify twice or even once in some cases without incurring some problems. Why is this the case?
If you are someone who is inventing the next serialization format for Java, JavaScript or whatever language, please consider this problem.
IMO there should be two states for a given object. A serialized state and a deserialized state. In software languages with stronger type systems, this isn't usually a problem. But with JSON in JavaScript, if call JSON.parse twice on the same object, we run into fatal exceptions. Likewise, if we call JSON.stringify twice on the same object, we can get into an unrecoverable state. Like I said there should be two states and two states only, plain JS object and serialized JS object.
1) JSON.parse expects a string, you are feeding it a Javascript object.
2) Similar issue to the first one. You feed a string to a function that needs an object.
3) Stringfy actually expects a string, but you are feeding it a String object. Therefore, it applies the same measures to escape the quotes and slashes as it would for the first string. So that the language can understand the quotes, other special characters inside the string.
4) You can write your own function for this.
5) Because you are trying to do a conversion that is illegal. This is related to the first and second question. As long as the correct object types are fed, you can call it as many times as you want. The only problem is the extra slashes but it is in fact the standard.
We'll start with this nightmare of your creation: string input and integer output.
IJSON.parse(IJSON.stringify("5")); //=> 5
The built-in JSON functions would not fail us this way: string input and string output.
JSON.parse(JSON.stringify("5")); //=> "5"
JSON must preserve your original data types
Think of JSON.stringify as a function that wraps your data up in a box, and JSON.parse as the function that takes it out of a box.
Consider the following:
var a = JSON.stringify;
var b = JSON.parse;
var data = "whatever";
b(a(data)) === data; // true
b(b(a(a(data)))) === data; // true
b(b(b(a(a(a(data)))))) === data; // true
That is, if we put the data in 3 boxes, we have to take it out of 3 boxes. Right?
If I put my data in 2 boxes and take it out of 1, I'm not holding my data yet, I'm holding a box that contains my data. Right?
b(a(a(data))) === data; // false
Seems sane to me...
JSON.parse unboxes your data. If it is not boxed, it cannot unbox it. JSON.parse expects a string input and you're giving it a JavaScript object literal
The first valid call to JSON.parse would return an object. Calling JSON.parse again on this object output would result in the same failure as #1
repeated calls to JSON.stringify will "box" our data multiple times. So of course you have to use repeated calls to JSON.parse then to get your data out of each "box"
Idempotence
No, this is perfectly sane. You can't triple-stamp a double-stamp.
You'd never make a mistake like this, would you?
var json = IJSON.stringify("hi");
IJSON.parse(json);
//=> "hi"
OK, that's idempotent, but what about
var json = IJSON.stringify("5");
IJSON.parse(json);
//=> 5
UH OH! We gave it a string each time, but the second example returns an integer. The input data type has been lost!
Would the JSON functions have failed us here?
var json = JSON.stringify("hi");
JSON.parse(json);
//=> "hi"
All good. And what about the "5" ?
var json = JSON.stringify("5");
JSON.parse(json));
//=> "5"
Yay, the types have been preseved! JSON works, IJSON does not.
Maybe a more real-life example:
OK, so you have a busy app with a lot of developers working on it. It makes
reckless assumptions about the types of your underlying data. Let's say it's a chat app that makes several transformations on messages as they move from point to point.
Along the way you'll have:
IJSON.stringify
data moves across a network
IJSON.parse
Another IJSON.parse because who cares? It's idempotent, right?
String.prototype.toUpperCase — because this is a formatting choice
Let's see the messages
bob: 'hi'
// 1) '"hi"', 2) <network>, 3) "hi", 4) "hi", 5) "HI"
Bob's message looks fine. Let's see Alice's.
alice: '5'
// 1) '5'
// 2) <network>
// 3) 5
// 4) 5
// 5) Uncaught TypeError: message.toUpperCase is not a function
Oh no! The server just crashed. You'll notice it's not even the repeated calling of IJSON.parse that failed here. It would've failed even if you called it once.
Seems like you were doomed from the start... Damned reckless devs and their careless data handling!
It would fail if Alice used any input that happened to also be valid JSON
alice: '{"lol":"pwnd"}'
// 1) '{"lol":"pwnd"}'
// 2) <network>
// 3) {lol:"pwnd"}
// 4) {lol:"pwnd"}
// 5) Uncaught TypeError: message.toUpperCase is not a function
OK, unfair example maybe, right? You're thinking, "I'm not that reckless, I
wouldn't call IJSON.stringify or IJSON.parse on user input like that!"
It doesn't matter. You've fundamentally broken JSON because the original
types can no longer be extracted.
If I box up a string using IJSON, and then unbox it, who knows what I will get back? Certainly not you, and certainly not the developer using your reckless function.
"Will I get a string type back?"
"Will I get an integer?"
"Maybe I'll get an object?"
"Maybe I will get cake. I hope it's cake"
It's impossible to tell!
You're in a whole new world of pain because you've been careless with your data types from the start. Your types are important so start handling them with care.
JSON.stringify expects an object type and JSON.parse expects a string type.
Now do you see the light?
I'll try to give you one reason why JSON.parse cannot be called multiple time on the same data without us having a problem.
you might not know it but a JSON document does not have to be an object.
this is a valid JSON document:
"some text"
lets store the representation of this document inside a javascript variable:
var JSONDocumentAsString = '"some text"';
and work on it:
var JSONdocument = JSON.parse(JSONDocumentAsString);
JSONdocument === 'some text';
this will cause an error because this string is not the representation of a JSON document
JSON.parse(JSONdocument);
// SyntaxError: JSON.parse: unexpected character at line 1 column 1 of the JSON data
in this case how could have JSON.parse guessed that JSONdocument (being a string) was a JSON document and that it should have returned it untouched ?
I have the following NancyFX unit test. I use the Shouldly assertion library to give the set of extensions methods that start .Should---
[Fact]
public void Assessment__Should_return_assessment_state_for_specified_user()
{
const AssessmentState assessmentState = AssessmentState.Passed;
var user = Fake.Mentor();
using (var db = Fake.Db())
{
db.Save(user);
Fake.Assessment(user.Id, db, assessmentState);
db.ClearStaleIndexes();
}
var response = Fake.Browser(user.UserName, user.Password)
.Get("/assessment/state/" + user.Id, with => with.HttpRequest());
//var result = (dynamic)body.DeserializeJson<ExpandoObject>();
var result = (dynamic) JsonConvert.DeserializeObject<ExpandoObject>(response.Body.AsString());
result.ShouldNotBe(null);
((AssessmentState) result.State).ShouldBe(assessmentState);
}
This test calls a AssessmentService uri defined as /assessment/state/" + user.Id which returns a simple JSON object definition that has a single property State of type (enum) AssessmentState, either Passed, Failed or NotStarted.
Here is the service handler so you can see there are no tricks.
Get["/assessment/state/{userid}"] = parameters =>
{
var assessment = AssessmentService.GetByUserId(Db, (string)parameters.userid);
return assessment == null ? HttpStatusCode.NotFound : Response.AsJson(new
{
assessment.State
});
};
And here is an example the JSON this service call returns:
{"State":1}
Everything works fine until I try to Deserialize the JSON returned by the fake Nancy browser. First I tried to use the built in method provided by Nancy's BrowserResponse.Body object:
var result = (dynamic)response.Body.DeserializeJson<ExpandoObject>();
This deserializes to an empty object. Which is no good. However, if we use the Newtonsoft equivalent then everything is fine (almost).
var result = (dynamic) JsonConvert.DeserializeObject<ExpandoObject>(response.Body.AsString());
The JSON deserialization now works and so the following Shouldly assertion passes with flying colours:
((AssessmentState) result.State).ShouldBe(assessmentState);
However, for reasons that I suspect have to do with anonymous types, the following line fails at run-time (it compiles fine).
result.ShouldNotBe(null);
That is quite a lot of information. Let me distil it down to two questions:
Why does Nancy's built in JSON deserializer not work given that the Newtonsoft version does?
How do I work with the dynamic types generated by the JSON de-serialisation so that the Shouldly extension methods do not cause a run-time exception?
Thanks
I can't answer the first question, but WRT Shouldly and dynamic types, Shouldly's ShouldNotBe method is an extension method on object. The DLR doesn't allow you to call extension methods on objects typed as dynamic (hence the runtime binder exception you're seeing)
I'd suggest that if you want to call ShouldNotBe(null) on result, you'd have to cast it to an object first (ie: ((object)result).ShouldNotBe(null))
-x