Sign Ethereum transactions offline using Node-RED - ethereum

I am trying to create a flow in Node-RED to sign for Etherem transactions (in this primary case, just messages) off-line and am struggling.
I have a Python code working fine (meaning doing well this off-line signing) and posting via an API but the Node-RED is not happy doing what I want (it returns a different signature than the one I get on the Web3.py and thus the EVM reverts the transaction).
Can anyone help? I tried several approaches, including the ones commented out, but none will match the signature generated at the Python code...
PS - the Python code implements it with signed_hash = self.web3.eth.account.signHash(hash, self._fetch_signer_key())
Thanks!
var Web3 = global.get('web3');
var web3 = new Web3();
var account = "0x9EfEe29e8fDf2cXXXXXXXde5502ABDf3f13ADac9a";
var privateKey = "0xbde16f62dadb43dad898e182e4e798baXXXXae46458d4939361d2494e11ce9e4";
var password = "12XXX8";
var message = "SMX1 10 1527596375";
var _hash = web3.utils.sha3(message);
//var signedObject = web3.eth.accounts.sign(_hash, privateKey);
var signedObject = web3.eth.accounts.sign(message, account);
//var signedObject = web3.eth.personal.sign(message, account, password);
//const signature = web3.eth.sign(message,account);
var msg = {};
msg.payload = {
//"message": message,
"message": signedObject.message,
"hash": _hash,
//"hash": signedObject.messageHash,
"signature": web3.utils.toHex(signedObject.signature),
//"signature": web3.eth.personal.sign(message, account, password),
"identity": identity,
};
/*
//msg.payload = signedObject.message;
msg.payload.message = signedObject.message;
msg.payload.hash = signedObject.messageHash;
msg.payload.signature = signedObject.signature;
msg.payload.identity = identity;
*/
//return signedObject.messageHash;
//return msg;
return signedObject;

Related

How to pull data from multiple Mailchimp endpoints?

The code below pulls data from the Mailchimp API Reports endpoint and adding it to Sheets.
I would like to add some more data from other endpoints (like fields from the "List/Audience" endpoint: member_count, total_contacts i.e.) but don't have a slick solution to this.
What's the best practice/solution here? Can this task be kept in the same function or is a separate function preferable?
I'm new in this area so bear with me :)
function chimpCampaigns() {
var API_KEY = 'X'; // MailChimp API Key
var REPORT_START_DATE = '2018-01-01 15:54:00'; // Report Start Date (ex. when you sent your first MailChimp Newsletter)
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("CampaignData");
var dc = API_KEY.split('-')[1];
var api = 'https://'+ dc +'.api.mailchimp.com/3.0';
var count = 100; // Max rows to return
var campaignList = '/campaigns?&count='+count+'&since_send_time='+REPORT_START_DATE
var options = {"headers": {"authorization": 'apikey '+API_KEY}};
var apiCall = function(endpoint){
apiResponseCampaigns = UrlFetchApp.fetch(api+endpoint,options);
json = JSON.parse(apiResponseCampaigns);
return json
}
var campaigns = apiCall(campaignList);
var total = campaigns.total_items;
var campaignData = campaigns.campaigns;
if (campaignData) {
sheet.clear(); // Clear MailChimp data in Spreadsheet
// Append Column Headers
sheet.appendRow(["Sent Time", "Campaign ID", "Audience", "Campaign Title", "Subject Line", "Emails Sent", "Abuse Reports", "Unsubscribed", "Unsubscribe Rate", "Hard Bounces", "Soft Bounces", "Bounces Total", "Syntax Errors", "Forwards Count", "Forwards Opens", "Opens Total", "Unique Opens", "Open Rate", "Last Open", "Clicks Total", "Unique Clicks","Unique Subscriber Clicks", "Click Rate", "Last Click"]);
}
for (i=0; i< campaignData.length; i++){
var c = campaignData[i];
var cid = c.id;
var title = c.title;
var subject = c.subject;
var send_time = c.send_time;
if (send_time){
apiResponseReports = UrlFetchApp.fetch('https://'+ dc +'.api.mailchimp.com/3.0/reports/'+cid,options);
reports = JSON.parse(apiResponseReports);
reportsSendTime = reports.send_time;
if(reportsSendTime){
var campaign_title = c.settings.title;
var subject_line = c.settings.subject_line;
var emails_sent = reports.emails_sent;
var list_name = reports.list_name;
var fields = reports.fields;
var abuse_reports = reports.abuse_reports;
var unsubscribed = reports.unsubscribed;
var unsubscribe_rate = unsubscribed/emails_sent;
var hard_bounces = reports.bounces.hard_bounces;
var soft_bounces = reports.bounces.soft_bounces;
var bounces = hard_bounces+soft_bounces;
var syntax_errors = reports.bounces.syntax_errors;
var forwards_count = reports.forwards.forwards_count;
var forwards_opens = reports.forwards.forwards_opens;
var opens_total = reports.opens.opens_total;
var unique_opens = reports.opens.unique_opens;
var open_rate = reports.opens.open_rate;
var last_open = reports.opens.last_open;
var clicks_total = reports.clicks.clicks_total;
var unique_clicks = reports.clicks.unique_clicks;
var unique_subscriber_clicks = reports.clicks.unique_subscriber_clicks;
var click_rate = reports.clicks.click_rate;
var last_click = reports.clicks.last_click;
// the report array is how each row will appear on the spreadsheet
var report = [send_time, fields, cid, list_name, campaign_title, emails_sent, subject_line, abuse_reports, unsubscribed, unsubscribe_rate, hard_bounces, soft_bounces, bounces, syntax_errors, forwards_count, forwards_opens, opens_total, unique_opens, open_rate, last_open, clicks_total, unique_clicks, unique_subscriber_clicks, click_rate, last_click];
sheet.appendRow(report);
}
}
}
}
You can call each endpoint in succession using the error-first pattern. More on this here.
If your previous call returns data and doesn't error out, you pass the next function as a callback, etc.
In the example below, I've omitted the logic that builds URL endpoint, query-string, and the 'options' object as these can simply be borrowed from your code.
Basically, you define a function with a callback parameter for each API endpoint.
Whenever you need to call multiple endpoints, you create a 3rd function that calls them in succession, passing each new function call as a parameter to the previous one.
The inner functions will still have access to the outer scope so you can combine data from multiple endpoints after the last call is executed (provided you assign unique names to the returned data - 'campaigns', 'reports', etc)
//function for the 'campaings' endpoint
function getCampaings(options, callback) {
//API call
var response = UrlFetchApp.fetch(campaignsEndpoint, options);
if (res.getStatusCode() == 200) {
var campaigns = JSON.parse(res.getContentText());
callback(false, campaigns);
} else {
callback("Error: Server responded with the status code of " + res.getStatusCode());
}
}
After creating the function for calling the 'reports' endpoint using the same approach, combine calls in the 3rd function.
function getCampaignsAndReports(){
var combinedData = {};
getCampaigns(options, function(err, campaigns){
if (!err && campaigns) {
//Call is successful - proceed to the next call
getReports(options, function(err, reports){
//Call successful
if (!err && reports) {
//Proceed to the next call or combine data from
//multiple endpoints
combinedData.campaigns = campaigns.campaigns;
combinedData.reports = reports.reports;
//write to sheet
//...
} else {
//Error calling reports endpoint
throw err;
}
});
} else {
//Error calling 'campaigns' endpoint. Throw error or write
//another function to show it to the user
throw err;
}
});
}
This may vary depending on how the MailChimp API data is structured so please change the code accordingly. Also, if you need to call the 'reports' endpoint multiple times for each entry in the 'campaings' endpoint, you can change your function to handle multiple request (options) object using UrlFetchApp.fetchAll(request[]). More on this here. Calling this method will return multiple response objects that you can iterate over.

Hmac256 Signature invalid error Google App Script

I am attempting to retrieve trades from a service called 3Commas in Google Apps Script. I've worked with public endpoints before, but this is the first time I've attempted to work with signed endpoints. I'm currently receiving an error that states:
[19-01-09 16:46:24:592 EST] {"error":"signature_invalid","error_description":"Provided signature is invalid"}
I'm guessing this is a formatting issue on my part. I'm using jsSHA to build the HMAC part. I've tried following the example in the docs. But I haven't quite got it yet. Any suggestions on what it could be?
3Commas Docs: https://github.com/3commas-io/3commas-official-api-docs#signed--endpoint-security
function main() {
var key = 'apikey';
var secret = 'apisecret';
var baseUrl = "https://3commas.io/public/api";
var endPoint = "/ver1/smart_trades";
var pointParams = "?limit=10&offset=&account_id=&scope=&type="
//base url + end point + params
var queryString = baseUrl+endPoint+pointParams;
var message = queryString;
var secret = secret;
var shaObj = new jsSHA("SHA-256", "TEXT");
shaObj.setHMACKey(secret, "B64");
shaObj.update(message);
var signature = shaObj.getHMAC("B64");
//headers
var hparams = {
'method': 'get',
'headers': {'APIKEY': key,
'Signature': signature},
'muteHttpExceptions': true
};
//call
var data = UrlFetchApp.fetch(queryString , hparams).getContentText();
Logger.log(data)
}
How about this modification? From 3Commas Docs in your question, I propose the modification points as follows.
Modification points:
It seems that the value which is required to encrypt is after https://3commas.io.
You can encrypt the values using the method of computeHmacSha256Signature() in Class Utilities of GAS. In this case, jsSHA is not required to be used.
But when computeHmacSha256Signature() is used, the value becomes the bytes array of the signed hexadecimal. So it is required to convert it to the unsigned hexadecimal.
Modified script:
function main() {
var key = 'apikey';
var secret = 'apisecret';
var baseUrl = "https://3commas.io"; // Modified
var endPoint = "/public/api/ver1/smart_trades"; // Modified
var pointParams = "?limit=10&offset=&account_id=&scope=&type="; // or "?limit=10"
var queryString = endPoint + pointParams; // Modified
var signature = Utilities.computeHmacSha256Signature(queryString, secret); // Added
signature = signature.map(function(e) {return ("0" + (e < 0 ? e + 256 : e).toString(16)).slice(-2)}).join(""); // Added
//headers
var hparams = {
'method': 'get',
'headers': {'APIKEY': key,
'Signature': signature},
'muteHttpExceptions': true
};
//call
var data = UrlFetchApp.fetch(baseUrl + queryString , hparams).getContentText(); // Modified
Logger.log(data)
}
Note:
About var pointParams = "?limit=10&offset=&account_id=&scope=&type=", in the case of the endpoint you use, limit, offset, account_id, scope and type are no mandatory. So it might be var pointParams = "?limit=10". If the error occurs, please try it.
References:
computeHmacSha256Signature(value, key)
Public Rest API for 3commas.io (2018-10-26)
This document is more detail.
I cannot confirm whether this modified script works. I'm sorry for this situation. So if it didn't work, I apologize. At that time, can you provide the detail information of the situation?

Server Return error 404 while uploading documents on oracle cloud infrastructure

I can list the object of the bucket using API from oracle cloud infrastructure.But when I try to upload file it shows an error 404.I am not sure whether URI is correct or I am not authorized.
here the code snippet:
FileInfo f = new FileInfo(FileUpload1.FileName);
byte[] filebyte =FileUpload1.FileBytes;
var myfirstobject = FileUpload1.FileName;
Response.Write(myfirstobject);
var postdata = Encoding.UTF8.GetBytes(filebyte.ToString());
var tenancyId = ConfigurationManager.AppSettings["BMCTenancyId"];
var userId = ConfigurationManager.AppSettings["BMCUserId"];
var fingerprint = ConfigurationManager.AppSettings["BMCFingerprint"];
var privateKeyPath = ConfigurationManager.AppSettings["BMCPrivateKeyPath"];
var privateKeyPassphrase = ConfigurationManager.AppSettings["BMCPrivateKeyPassphrase"];
var bucket= ConfigurationManager.AppSettings["BMCBucket"];
var Namespace= ConfigurationManager.AppSettings["BMCNamespace"];
var signer = new RequestSigner(tenancyId, userId, fingerprint, privateKeyPath, privateKeyPassphrase);
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
var uri = new Uri($"https://objectstorage.us-phoenix-1.oraclecloud.com/n/{Namespace}/b/{bucket}/{myobject}");
var request = (HttpWebRequest)WebRequest.Create(uri);
request.Method = "PUT";
request.ContentType = "application/json";
request.ContentLength = postdata.Length;
request.Headers["x-content-sha256"] = Convert.ToBase64String(SHA256.Create().ComputeHash(bytes));
using (var stream = request.GetRequestStream())
{
stream.Write(bytes, 0, bytes.Length);
}
signer.SignRequest(request);
ExecuteRequest(request);
Can you do the same action from the OCI console, using the same user as you are using from the code above (BMCUserId)? If not, then you are not authorized to perform the action, and its not an issue with your code. Otherwise, its an issue with your code.

How can I present customer data from spreadsheet into form in app maker for update?

I have struggling to present available data for selected customer from spreadsheet into app maker form incase staff want to change it or update empty fields.
Client side code:
function getDetails() {
var props = app.currentPage.properties;
var page = app.pages.Search;
var Channel = app.datasources.Update.items;
var Customer = page.descendants.Sheets.value;
props.Loading = true;
props.Error = null;
google.script.run
.withFailureHandler(function(error) {
props.Loading = false;
props.Error = JSON.stringify(error);
console.error(error);
})
.withSuccessHandler(function(Channel) {
props.Loading = false;
page.Channel = Channel;
var items = [];
items = getChannels(props.SelectedSheet);
Channel.items.load(); // this line dosen't work and it doesn't load the data into form
if (Channel && Channel.length > 0) {
page.SelectedSheet = Channel[0];
} })
.getDetails(props.SelectedSheet);
}
Server side code:
function getDetails()(customer){
var spreadSheet = SpreadsheetApp.openById("***").getSheetByName('TRACKER');
var data=spreadSheet.getDataRange().getValues();
var channels = [];
var Name = customer;
var string1 = Name;
var array1 = string1.split(";"); // in here I extract row number belong to customer to get data
var destrow = [];
destrow.push(data[array1[0]][0],data[array1[0]][1],data[array1[0]][2],data[array1[0]][3],data[array1[0]][4],data[array1[0]][5]);
channels.push(destrow);
// return channels;
return channels.map(function(Channel){
return Channel;}); // return array of field data to presented in app maker form
}
Thank you for any answer or suggestion.
Cheers
In theory, this code should throw exception, since Channel is array and array doesn't have load method:
function getDetails() {
...
var Channel = app.datasources.Update.items;
...
// your first Channel variable is never used and is overridden with
// Channel callback parameter
.withSuccessHandler(function(Channel) {
// this line does nothing, since all App Maker objects are sealed
page.Channel = Channel;
// TypeError: load is not a function
Channel.items.load();
...
}
It is not clear from you code, what you are trying to do... Try to debug it and look into browser console more often (F12 or Ctrl + Shift + J).
Further reading:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/seal

Edit user signature, but can't use Oauth

I'm tryng to edit the users signature via GAS.
I have found lots of examples and tutorials, and one of the Waqar Ahmad's answers looks very good - Email Settings APIs Authentication.
However, it doesn't work for me.
I don't really understand OAuth autorization, but couldn't find a tutorial for it.
With what should I replace anonymous?
oAuthConfig.setConsumerKey("anonymous");
oAuthConfig.setConsumerSecret("anonymous");
I go to https://console.developers.google.com/ create a project and use clientID for key and client secret for secret, is it right?
Adding more information:
This is the Waqar's code I'm using:
/*-----------------------------------------------------------------------------
This function will update the HTML signature of a user.
Input will be jason data
To disable signature, pass an empty string as signature value
sample parameter
ob = {user='hps', signature='<b>Regards</b><br>Waqar'}
To disable signature
ob = {user='hps', signature=''}
-----------------------------------------------------------------*/
function updateSignature(ob) {
//ob = {};
//ob.user = "hps";
//ob.signature = "<b>Regards</b><br>Waqar";
ob = {};
ob.user = "test#xxxx.it";
ob.signature = "<b>Regards</b><br>Waqar";
var base = 'https://apps-apis.google.com/a/feeds/emailsettings/2.0/';
var xmlRaw = '<?xml version="1.0" encoding="utf-8"?>'+
'<atom:entry xmlns:atom="http://www.w3.org/2005/Atom" xmlns:apps="http://schemas.google.com/apps/2006">'+
'<apps:property name="signature" value="'+htmlEncode(ob.signature)+'" />'+
'</atom:entry>';
var fetchArgs = googleOAuth_('emailSetting',base);
fetchArgs.method = 'PUT';
fetchArgs.payload = xmlRaw;
fetchArgs.contentType = 'application/atom+xml';
var domain = UserManager.getDomain();
var url = base+domain+'/'+ob.user+'/signature';
var urlFetch = UrlFetchApp.fetch(url, fetchArgs);
var status = urlFetch.getResponseCode();
return status;
}
//--------------------------------------------------------------------------
//This function will retreive Signature settings as json.
/*Sample returned object
{user=hps, signature=<b>Regards</b><br>Waqar}
*/
//-----------------------------------------------------------------
function retrieveSignature(user) {
var user = 'hps';
var base = 'https://apps-apis.google.com/a/feeds/emailsettings/2.0/';
var fetchArgs = googleOAuth_('emailSetting',base);
fetchArgs.method = 'GET';
var domain = UserManager.getDomain();
var url = base+domain+'/'+user+'/signature?alt=json';
var urlFetch = UrlFetchApp.fetch(url, fetchArgs);
var jsonString = urlFetch.getContentText();
var jsonArray = Utilities.jsonParse(jsonString).entry.apps$property;
var ob = {};
ob.user = user;
for(var i in jsonArray){
ob[jsonArray[i].name] = jsonArray[i].value;
}
return ob;
}
//Google oAuthConfig..
function googleOAuth_(name,scope) {
var oAuthConfig = UrlFetchApp.addOAuthService(name);
oAuthConfig.getAccessTokenUrl()
oAuthConfig.setRequestTokenUrl("https://www.google.com/accounts/OAuthGetRequestToken?scope="+scope);
oAuthConfig.setAuthorizationUrl("https://www.google.com/accounts/OAuthAuthorizeToken");
oAuthConfig.setAccessTokenUrl("https://www.google.com/accounts/OAuthGetAccessToken");
oAuthConfig.setConsumerKey("xxxxxxxxxxxx.apps.googleusercontent.com");
oAuthConfig.setConsumerSecret("xxxxxxxx-xxxxxx-xxxxx");
return {oAuthServiceName:name, oAuthUseToken:"always"};
}
//This function will escape '<' and '>' characters from a HTML string
function htmlEncode(str){
str = str.replace(/</g,'<');
return str.replace(/>/g,'>')
}
to get the oAuthConfig.setConsumerKey and oAuthConfig.setConsumerSecret I have created a new project in the google developer console, modified to on the Admin SDK API status, created a "Client ID for native application" and used the CLIENT ID in setConsuperKey, and CLIENT SECRET in setConsumerSecret.
Executing the script updateSignature the test#xxxx.it's signature should be changed,
I can see the box "autorization required" clik ok, and appear the request access box,
i click on "grant access" but nothing happes, and no error are shown.
Executing the same function in debug mode, i have the same boxes and a red box with "Errore OAuth" at the end.
I'm doing something wrong... please help me to find the mistake!!
Thanks again.
Marco
Finally i found the clue!!
Thank's to mike's quastions and answers
who let me discover This example
I Finally understand!!
in consumerkey you have to set your domain (p.e. "mydomain.it",
in consumerSecret you have to set the "Secret data of the customer to OAuth:" from admin google console->secority->advanced settings->Manage data OAuth key and secret for this domain.
I was confusing by the secret key in the google developer console, may be this answer can help