I want to create an RPC call (study purpose) from my app using metamask.
My method on my contract is:
function sayHelloMyName(string memory _name) external pure returns (string memory) {
require(bytes(_name).length != 0, "insert your name");
return string(abi.encodePacked("hello ", _name));
}
its hash is:
$ web3.utils.keccak256("sayHelloMyName(string)").substr(0, 10)
> '0x10a7b27a'
I want to pass my name foo where the hex decimal is 0x666f6f
web3.utils.toHex('foo')
'0x666f6f'
So my call is:
ethereum
.request({
method: 'eth_call',
params: [
{
from: currentAccount,
to: contractAddress,
data: 0x10a7b27a0000000000000000000000000000000000000000000000000000000000666f6f
}, "latest"],
})
.then((txHash) => {
console.log(txHash);
$('#info').html(txHash);
})
.catch((error) => {
console.error;
$('#info').text(JSON.stringify(error.message));
});
where the data is the method signature, and my hex name and padding (total 32 bytes)
Unfortunately, I get a revert of it.
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32000,
"message": "execution reverted"
}
}
the code works, I can use it with web3 library.
Solidity string is encoded as a dynamic-length array of bytes including the first 32byte slot pointing to the location of the array beginning and the second 32byte slot containing the length of the string. Meaning: Not just the hex value of the string that you're passing.
It works with web3js because the JS library decodes the hex back to string (web3 assumes the input is hex as it starts with 0x), and then encodes it correctly to the byte array while filling the data field.
See output of this web3js snippet
const data = web3.eth.abi.encodeFunctionCall({
name: 'sayHelloMyName',
type: 'function',
inputs: [{
type: 'string',
name: '_name'
}]
}, [
'foo',
]);
console.log(data);
that prints
# formatted for readability
0x10a7b27a
0000000000000000000000000000000000000000000000000000000000000020
0000000000000000000000000000000000000000000000000000000000000003
666f6f0000000000000000000000000000000000000000000000000000000000
First slot is a pointer to the location where the array starts. In this case (hex20 == dec32) that's beginning of the second slot.
Second slot (value 3) is the length of the values
And the third slot contains the actual hex-encoded value of the string
Solution: This long value (without the newlines) is what you need to pass instead.
Related
I have written a REST class that sets a url as an endpoint to call an API. The API returns a timezone value. The url passes longitude and latitude as a parameter. The response I am getting is a complicated JSON list and I just need the Id value of the Timezone object. This is what I get as a JSON response for a lat/long value I passed:
{
"Version":"2019c",
"ReferenceUtcTimestamp":"2019-12-10T21:14:23.7869064Z",
"TimeZones":[
{
"Id":"America/Los_Angeles",
"Aliases":[
"US/Pacific",
"US/Pacific-New"
],
"Countries":[
{
"Name":"United States",
"Code":"US"
}
],
"Names":{
"ISO6391LanguageCode":"No supported language code supplied",
"Generic":"",
"Standard":"",
"Daylight":""
},
"ReferenceTime":{
"Tag":"PST",
"StandardOffset":"-08:00:00",
"DaylightSavings":"00:00:00",
"WallTime":"2019-12-10T13:14:23.7869064-08:00",
"PosixTzValidYear":2019,
"PosixTz":"PST+8PDT,M3.2.0,M11.1.0",
"Sunrise":"2019-12-10T07:42:22.383-08:00",
"Sunset":"2019-12-10T16:18:49.095-08:00"
},
"RepresentativePoint":{
"Latitude":34.05222222222222,
"Longitude":-118.24277777777777
},
"TimeTransitions":[
{
"Tag":"PST",
"StandardOffset":"-08:00:00",
"DaylightSavings":"00:00:00",
"UtcStart":"2019-11-03T09:00:00Z",
"UtcEnd":"2020-03-08T10:00:00Z"
},
{
"Tag":"PDT",
"StandardOffset":"-08:00:00",
"DaylightSavings":"01:00:00",
"UtcStart":"2020-03-08T10:00:00Z",
"UtcEnd":"2020-11-01T09:00:00Z"
},
{
"Tag":"PST",
"StandardOffset":"-08:00:00",
"DaylightSavings":"00:00:00",
"UtcStart":"2020-11-01T09:00:00Z",
"UtcEnd":"2021-03-14T10:00:00Z"
}
]
}
]
}
Here is my REST Service in APEX:
public class LPP_AccountTimeZone {
public static List<String> getTimeZone(String latitude, String longitude){
Http http = new Http();
HttpRequest req=new HttpRequest();
String url = 'https://atlas.microsoft.com/timezone/byCoordinates/json?subscription-key=XXXXXXXXXXXXXXXXXXXXXXXXXXXX&api-version=1.0&options=all&query='+latitude+','+longitude;
req.SetEndPoint(url);
req.setMethod('GET');
HttpResponse res=http.send(req);
if (res.getStatusCode() == 200) {
List<String> TimeZone = new List<String>();
TimeZoneJSON result = TimeZoneJSON.parse(res.getBody());
for(TimeZoneJSON.TimeZones t : result.timeZones){
System.debug('TimeZone is' + t.Id);
TimeZone.add(t.Id);
}
}
else{
System.debug('The status code returned was not expected: ' + res.getStatusCode() + ' ' + res.getStatus());
}
return TimeZone[0];
}
The response I got with this code (when I ran it in anonymous window) was:
TimeZone is{Aliases=(US/Pacific, US/Pacific-New), Countries=({Code=US, Name=United States}), Id=America/Los_Angeles, Names={Daylight=Pacific Daylight Time, Generic=Pacific Time, ISO6391LanguageCode=en, Standard=Pacific Standard Time}, ReferenceTime={DaylightSavings=00:00:00, PosixTz=PST+8PDT,M3.2.0,M11.1.0, PosixTzValidYear=2019, StandardOffset=-08:00:00, Sunrise=2019-12-12T07:44:13.44-08:00, Sunset=2019-12-12T16:18:47.934-08:00, Tag=PST, WallTime=2019-12-12T11:49:25.0802593-08:00}, Representativ
That is a lot of info. I just want the America/Los_Angeles part which is what Id equals (I have that bold in the response).
Another problem with this code is that it is not returning anything/is void class.
I need t return that value because a trigger is calling that method and will use this value to update a field.
Can anyone please correct my code so that it passes the correct json value and returns the value?
EDIT/UPDATE: The error I am now getting is "Variale does not exist: TimeZone (where the return statement is)
You could use JSON2APEX to easily generate an apex class from your JSON response. Just paste your full response in and click 'Create Apex'. This creates a class that represents your response so that you can easily retrieve fields from it (Keep in mind this will only really work if your response is static meaning the structure and naming stay the same). Have a look through the class that it generates and that will give you an idea of what to do. The class has a parse(String JSON) method which you can call passing in your JSON response to retrieve an instance of that class with your response values. Then it's just a matter of retrieving the fields you want as you would with any object.
Here is how getting the timezone id code would look if you take this route.
(Note: This assumes you keep the name of the class the standard 'JSON2Apex')
if (res.getStatusCode() == 200) {
JSON2Apex result = JSON2Apex.parse(res.getBody());
for(JSON2Apex.TimeZones t: result.timeZones){
System.debug('TimeZone is' + t.id);
tId.add(t);
}
}
To return a value just change void in the method signature to List<String> and return the tId list as follows return tId;
I'm trying to make API request with using Apps Script. API is GraphQL based.
I'm using JSON.stringify function. But API returns an error.
Request:
payload={"query":"{mutation {change_column_value (board_id: 177955, item_id: 287466289, column_id:\"phone\", value: \"{phone : 15065332974, countryShortName: \"US\" }\") {name column_values{ id value}}}"}}
I'm getting error;
{"errors":[{"message":"Parse error on \" }\" (STRING) at [1, 148]","locations":[{"line":1,"column":148}]}]}
Apps Script Code:
var us = "US"
var column_values = '{ \
mutation {change_column_value (board_id: 177955, item_id: '+ 287466289 +', column_id:"phone", value: "{phone : 15065332974, countryShortName: \"' + us +'\" }") {name column_values{ id value}} \
}';
settings = {
"method": "POST",
"headers": {"Content-Type" : "application/json","Authorization" : "eyJhbGciOiJIXXXXXXXXXX"},
"payload" : JSON.stringify({query:column_values})
}
response= UrlFetchApp.fetch(url, settings);
Brackets in a GraphQL operation indicate a selection set -- one or more fields that are being requested. The operation itself (whether a query or a mutation) is not a field, so you don't wrap the whole operation in brackets.
Correct:
mutation {
doSomething
}
Incorrect:
{
mutation {
doSomething
}
}
The only time you will see brackets that look like they are on the "outside" is when using query shorthand.
Provided that value is a String, then your use of backslashes to escape the double quotes inside that string should be fine.
You might also consider using variables, which would clean up your code considerably and make it less error prone.
You do not need to escape special characters inside a JSON request.
The syntax implies using key-value pairs assigned to each other with a colon, whereby strings are located inside quotes (not escaped with a backslash!), while numbers and variable names are not.
The brackets should be used in function of your desired API request as documented.
I have fetch() setup to retrieve historical webinar details from GotoWebinar using their API. It returns a JSON body with this structure, an array of objects:
[{
"webinarKey":5653814518315977731,
"webinarID":"562189251",
"subject":"Sample Webinar",
"organizerKey":100000000000331530
},
{
"webinarKey":9999814518315977731,
"webinarID":"999989251",
"subject":"Sample Webinar",
"organizerKey":999900000000331530
}]
My code is being implented in a Zapier action(node.js) and the important bit looks like this:
//Handle errors from fetch call
function handleFetchStatus(response){
console.log('Fetch Response Status: ' + response.status);
switch(response.status){
case 200: //Request executed properly
break;
default:
throw Error(response.status + ':' + JSON.stringify(response));
}
return response.json();
}
function handleFetchBody(oResponse){
if (oResponse) {
console.log('handleFetchBody: ' + JSON.stringify(oResponse));
}
callback(null, oResponse);
}
//Send POST request.
fetch(getFetchURL(), getFetchOptions())
.then(handleFetchStatus)
.then(handleFetchBody)
.catch(function(error) {
callback(error);
});
The problem I've got is that the 'webinarKey', a long number, is being truncated from "5653814518315977731" to "5653814518315978000". I believe it's the json() function that isn't handling large numbers.
How can I stop this?
I believe I need to turn the webinarKey into a string before I use json(), but I'm not sure how to access all elements of the object. Is that even possible in a fetch response.
It has to do with the number of digits of precision in JavaScript. In JavaScript, all numbers are stored as floating-point, which is like scientific notation in base 2 instead of base 10. There is a fixed number of bits (53 in this case) available to represent a in a * (2 ** b). If you look at Number.MAX_SAFE_INTEGER, the greatest integer that can be represented with 53 bits of precision, you will see that it has 16 base-10 digits. Your number has 19. If you just type the number into a JavaScript console, you will see that it prints out the rounded version (with 16 digits of precision). If you need to be storing such large numbers, it is usually best to store them as strings.
How to send a transaction using RPC on ethereum.?
On documentation I see this:
params: [{
"from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155",
"to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567",
"gas": "0x76c0", // 30400,
"gasPrice": "0x9184e72a000", // 10000000000000
"value": "0x9184e72a", // 2441406250
"data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675"
}]
Where do I find this:
data": "0xd46e.......72445675
Is there any simple library that I can input only amount and the address of transaction?
Web3.js is the de-facto library for interacting with ethereum which you can use. There's great documentation on how to send transactions.
web3.eth.sendTransaction(transactionObject [, callback])
The data field is either a byte string containing the associated data of the message, or in the case of a contract-creation transaction, the initialization code. Data of messages are encoded methods and their argument values. You can use the library ethereumjs-abi to encode and decode data fields.
"data" parameter is not for you, if you just want to make a simple transaction.
To make simple transaction, you can simply ignore all parameters except "from", "to" and "value" and call the method in this way:
params: [{
"from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155",
"to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567",
"value": "0x9184e72a", // 2441406250
}]
You asked for simple library with just amount and address. It seems by address you mean "to" address. Unlike bitcoin rpc, which simply uses all addresses belongs to wallet as source of transaction, you should define exact "from" address with ethereum rpc.
I have written a couple of wrappers around Web3 for NodeJS and Web browsers.
The latter one helps abstracting connections and contract development.
const { connect, useConnection, sendTransaction } = require("web3-wrap");
try {
if(window.web3){
await useConnection(window.web3);
}
else {
await connect();
}
const receipt = await sendTransaction({
to: "0x1234...",
value: 10 // amount to transfer, in wei
});
console.log("Receipt:", receipt);
}
catch(err){
console.error(err && err.message || err);
}
Hope it heps
{ hash: '0xcc871efa64631ff57b6c4cdf9e9c52dce299956cc0bc2cdf6781dbd647a80926',
nonce: 34,
blockHash: '0xf4e21dabc0d6f99ae9a3fd128b9f25462110ed0e2811c0d5f91ffce9ac85594a',
blockNumber: 49,
transactionIndex: 0,
from: '0x9c3fe8bc6d259d44e80fb728e74727bfbe58e988',
to: '0xb22ab8533936d6d75c1ecf229c1ad68410ea8ee3',
value: { [String: '0'] s: 1, e: 0, c: [ 0 ] },
gas: 3141592,
gasPrice: { [String: '1'] s: 1, e: 0, c: [ 1 ] },
input: '0x1f5c1d9800000000000000000000000000000000000000000000000000000000000000400000000000000000000000007fa543843e2f5766ad623b2155d639d73635824400000000000000000000000000000000000000000000000000000000000000134f70656e20412042616e6b204163636f756e7400000000000000000000000000’ }
I am getting a transaction back from a x.send(1) whose JSON looks like the above. I can see in the value of the input attribute that there was a “7fa543843e2f5766ad623b2155d639d736358244” that matches the address of the account I provided for x. The Solidity snippet is:
function do(string _description, address x) {
if ( msg.sender != owner )
throw;
description = _description;
x.send(1);
}
However, the to: attribute in the JSON is wrong. My environment is using tests run in Truffle against TestRPC. Does anyone recognize this as a known issue or a problem on my part?
The appropriate part of my test code is:
.then(
function (_bool0) {
assert.isTrue(_bool0,"whoops");
return contract.do("a test", accounts[4], {from: accounts[0]} );
}).then(
function (tx_id) {
var transaction = web3.eth.getTransaction(tx_id);
/* debugging my test */
console.log(transaction);
assert.strictEqual(transaction.to,accounts[4],"transaction \"to:\" was not provided address");
done();
}
).catch(done);
The behaviour was actually consistent with how Ethereum works. When the test calls:
contract.do("a test", accounts[4], {from: accounts[0]} );
it causes a transaction that is recorded on the blockchain. A transaction FROM the accounts[0] account and to the Contract: which is also a type of account. The object returned to my test represents this transaction: shown as the JSON above.
I had mistakenly thought the returned object represented the transaction between the Contract to the second account.
To achieve my test objective I need to check for the existence of a transaction from the second account. I'll update this once I have figured it out.