solc inside firebase functions causes deployment error - firebase

I need to compile a smart contract inside a firebase function. I am using solc "solc": "^0.8.13", in my package.json.
Code of the firebase function responsible to create the contact is;
const functions = require("firebase-functions");
const admin = require('firebase-admin');
const solc = require('solc');
const fs = require("fs");
const Web3 = require('web3');
exports.createSC = functions.https.onCall((data, context) => {
if (!context.auth) return { data: data, status: 'error', code: 401, message: 'Not signed in' }
return new Promise((resolve, reject) => {
admin.auth().getUser(data.owner)
.then(userRecord => {
//below function compiles the contract and returns abi/binary code etc
let contract = instantiateContract('sources/SnaphashToken.sol');
//use web3 to actually deploy contract
let web3;
//...code emitted...
resolve(contractDeployResult)
})
.catch(error => {
console.error('Error fetching user data:', error)
reject({ status: 'error', code: 500, error })
})
});
})
this works perfectly well when deployed on functions simulator locally but when deployed on firebase cloud I get this exception;
i functions: updating Node.js 16 function ethereum-createSC(us-central1)...
Function failed on loading user code. This is likely due to a bug in the user code. Error message: Error: please examine your function logs to see the error cause: https://cloud.google.com/functions/docs/monitoring/logging#viewing_logs. Additional troubleshooting documentation can be found at https://cloud.google.com/functions/docs/troubleshooting#logging. Please visit https://cloud.google.com/functions/docs/troubleshooting for in-depth troubleshooting documentation.
Functions deploy had errors with the following functions:
ethereum-createSC(us-central1)
When I comment out const solc = require('solc') in my function, it deploys without problems. I really need to be able to deploy smart contracts after modifying based on user input and would appreciate a help on it

I found out that the subject function was causing deployment error because of Error: memory limit exceeded. I was able to see that after I checked detailed logs of the functions with firebase functions:log
2022-05-27T17:09:27.384860094Z E ethereum-createNft: Function cannot be initialized. Error: memory limit exceeded.
In order to fix this issue, I increased memory for the function from default 256MB to 1GB and timeoutSeconds from default 60 seconds to 120 seconds
exports.createNft = functions.runWith({memory:'1GB',timeoutSeconds:120}).https.onCall((data, context) => {
..code here..
});
And then deployment was successful

Related

Calling an API using Axios and Firebase Cloud Functions

I want to make a Google Cloud Function calling an external API for me. After some research on Google I found the way using Axios. The call is actually working, when I'm using it on my own nodejs but when I want to deploy the function to Google Cloud functions I'm always getting an error (Function cannot be initialized. Error: function terminated.)
I'm on the Blaze plan.
const functions = require("firebase-functions");
const axios = require("axios");
exports.getData = functions.https.onRequest((req, res) => {
return axios.get("http://api.marketstack.com/v1/eod?access_key='myAccessKey'&symbols=AAPL")
.then((response) => {
const apiResponse = response.data;
if (Array.isArray(apiResponse["data"])) {
apiResponse["data"].forEach((stockData) => {
console.log(stockData["symbol"]);
});
}
}).catch((error) => {
console.log(error);
});
});
Could someone please help me?
EDIT: I finally fixed it: the mistake was, that I ended up with two package.json files (one in the directory where it should be and one which I actually didn't need). When I was installing the dependencies with npm install, axios was added into the wrong package.json file. Unfortunately the other package.json file made it up to the server and I ended up with a package.json file without the necessary dependencies on the server and thus this made the error occur.
I didn’t test your code but you should return "something" (a value, null, a Promise, etc.) in the then() block to indicate to the Cloud Function platform that the asynchronous work is complete. See here in the doc for more details.
exports.getData = functions.https.onRequest((req, res) => {
return axios.get("http://api.marketstack.com/v1/eod?access_key='myAccessKey'&symbols=AAPL")
.then((response) => {
const apiResponse = response.data;
if (Array.isArray(apiResponse["data"])) {
apiResponse["data"].forEach((stockData) => {
console.log(stockData["symbol"]);
});
}
return null;
}).catch((error) => {
console.log(error);
});
});
You probably want do more than just logging values in the then() e.g. call an asynchronous Firebase method to write to a database (Firestore or the RTDB): in this case take care to return the Promise returned by this method.

Firebase Storage with Google Actions

I am having some issues connecting my firebase storage with my google action. I need to be able to "download" the json files inside in order to be able to read and pick out what a user may need given data that they provide when they call the action.
Below is the code that I currently have, complied from the different APIs and other stackoverflow questions I have found.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const Firestore = require('#google-cloud/firestore');
const firestore = new Firestore();
var storage = require('#google-cloud/storage');
const gcs = storage({projectId: 'aur-healthcare-group'});
const bucket = gcs.bucket('gs://aur-healthcare-group');
admin.storage().bucket().file('aur-healthcare-group/aur_members.json').download(function(errr, contents){
if(!err){
var jsObjext = JSON.parse(contents.toString('utf8'));
}
});
The current error I am receiving is "code":3,"message":"Function failed on loading user code. This is likely due to a bug in the user code. Error message: Error: please examine your function logs to see the error cause. When I check the logs I only get the above mentioned message again.
I believe that I am not accessing my firebase storage correctly and have trouble finding a good resource on how to access this correctly. Would somebody be able to give me an example of how to access the storage correctly so I will be able to apply it to my project?
Since you're running in Firebase Functions, you shouldn't need to require the #google-cloud/storage dependency directly. Rather, you can get the correctly authenticated storage component via admin.storage()
Following that, you shouldn't download the file to your function, as you would be better off reading directly into memory via a readStream.
With regards to your existing code error, it may be because you're checking if (!err) when the callback variable is errr.
I've done this in the past and here's a code snippet of how I achieved it. It's written in Typescript specifically, but I think you should be able to port it to JS if you're using that directly.
import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin'
import { Bucket } from '#google-cloud/storage';
admin.initializeApp()
const db = admin.firestore()
const bucket = admin.storage().bucket('project-id.appspot.com') // Use your project-id here.
const readFile = async (bucket: Bucket, fileName: string) => {
const stream = bucket.file(fileName).createReadStream();
return new Promise((resolve, reject) => {
let buffer = '';
stream.on('data', function(d: string) {
buffer += d;
}).on('end', function() {
resolve(buffer)
});
})
}
app.handle('my-intent-handler', async (conv) => {
const contents = await readArticle(bucket, 'filename.txt')
conv.add(`Your content is ${contents}`)
})
exports.fulfillment = functions.https.onRequest(app)

Firebase Scheduled Function failing after first schedule

This is my function. It simply requests a weather report every 5 minutes from an API and writes the data into a Firestore collection/document:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const fetch = require('node-fetch');
admin.initializeApp();
const db = admin.firestore();
const cors = require("cors")({ origin: true });
exports.getWeather = functions
.region('europe-west2')
.pubsub.schedule('every 5 minutes').onRun((context) => {
const weatherApiKey = '**************************';
const weatherLocation = {
'Winchelsea Beach': '354689',
'Rye': '310224',
'Hastings': '310087'
};
const weatherApiUrl = 'http://datapoint.metoffice.gov.uk/public/data/val/wxfcs/all/json/' + weatherLocation["Winchelsea Beach"] + '?res=3hourly&key=' + weatherApiKey;
fetch(weatherApiUrl)
.then(res => res.json())
.then(json => {
db.collection("weather").doc("forecast").set({
data: json,
updatedLast: new Date().getTime()
});
});
});
The function added data to the DB successfully the very first time it ran but it doesn't run every 5 minutes, in fact it's stopped completely, and it says there's an error in my logs.
This is what is displayed in my logs:
There are no more logs after this when there should be logs every 5 minutes. If I re-deploy, it runs again with logs but then stops.
I've also checked Cloud Scheduler and it shows Result: Failed:
I'm getting the idea that scheduled functions won't again run with bugs but I don't know what's wrong with my function to get a Function return undefined, expected Promise or value error.
How can I resolve this?
The error message is telling you that your function should return a promise that resolves when all the async work is complete. If you don't, then the function will terminate early, and the async work is highly unlikely to complete, as Cloud Functions shuts down right afterward.
Is suggest reading the documentation to better understand how it works.
The solution is pretty straightforward. Return a promise that resolves after both the fetch and Firestore write are fully complete by chaining promises correctly.
return fetch(weatherApiUrl)
.then(res => res.json())
.then(json => {
return db.collection("weather").doc("forecast").set({
data: json,
updatedLast: new Date().getTime()
});
});

Firebase Cloud Message and Cloud Function Error

I´m trying to use Firebase Cloud Message to send notifications through
Firebase Cloud Functions.
So i´ve deployed a function on Cloud Functions which calls the function admin.sendMessageToTopic(),
but I´m getting the warning:
"Billing account not configured. External network is not accessible and quotas are severely limited. Configure billing account to remove these restrictions"
after this, the function proceeds with no error, but the Cloud Message doesn´t send the Message.
Here is the code which i´m getting this error:
exports.sendMessageToTopic = functions.https.onRequest((req, res) => {
const topic = req.query.topic
const title = req.query.title
const body = req.query.body
const message = {
data: {
title: title,
body: body
}
}
admin.messaging().sendToTopic(topic , message)
.then(result => {
console.log("sendMessageToTopic ok:", result)
res.send("sendMessageToTopic ok")
})
.catch(error => {
console.log("sendMessageToTopic ERROR:", error)
res.send("sendMessageToTopic ERROR: " + error)
})
})
If you guys know what should be the problem, please share the solution with us.
Thank you guys.

"TypeError: functions.firestore.collection is not a function"

Looking through the Firestore documentation, I see many examples of functions.firestore.document but I don't see any examples of functions.firestore.collection. Firestore syntax is
firebase.firestore().collection('...').doc('...')
I get an error message with
firebase.firestore().document('...')
Yet in Cloud Functions with this code:
exports.myFunction = functions.firestore.collection('...').doc('...').onUpdate(event => {
on deploy I get an error message:
TypeError: functions.firestore.collection is not a function
When I change the code to
exports.getWatsonTokenFirestore = functions.firestore.document('...').onUpdate(event => {
I don't get an error message on deploy.
Why does Cloud Functions appear to have a different data structure than Cloud Firestore?
Here's my full Cloud Function. My collection is User_Login_Event and my document is Toggle_Value:
exports.getWatsonTokenFS = functions.firestore.document('User_Login_Event/{Toggle_Value}').onUpdate(event => {
var username = 'TDK',
password = 'swordfish',
url = 'https://' + username + ':' + password + '#stream.watsonplatform.net/authorization/api/v1/token?url=https://stream.watsonplatform.net/speech-to-text/api';
request({url: url}, function (error, response, body) {
admin.firestore().collection('IBM_Watson_Token').document('Token_Value').update('token');
});
return 0; // prevents an error message "Function returned undefined, expected Promise or value"
});
The function deploys without error but when it executes I get this error message:
TypeError: firebase.firestore is not a function
I'm confused as firebase.firestore isn't in my Cloud Function. It's in my Angular front-end code in various places, without a problem. What is this error message referring to? I tried changing the line
admin.firestore().collection('IBM_Watson_Token').document('Token_Value').update('token');
to
firebase.firestore().collection('IBM_Watson_Token').document('Token_Value').update('token');
and to
console.log("getWatsonTokenFS response");
but I got the same error message.
Yes. You should format it as...
exports.getWatsonTokenFirestore = functions.firestore.document('myCollection/{documentId}').onUpdate(event => {
// code
});
collection and doc are methods within firebase.firestore. To access them via functions.firestore, you must use document.
You can see a full list of Classes for Cloud Firestore and the latest SDK for Cloud Functions for Firebase
Update
I've been working on your code. I've added in all of the dependencies and initialization, which I assume that you have in your code. I can't see where you're using any data from Firestore in your IBM Watson request and I can't see how you're writing any of the returned data back to Firestore. As I'm not familiar with your request method, I've commented it out, to give you what should be a working example of an update to Firestore and writes something back. I also edited some of your code to make it more readable and changed the Cloud Functions code to reflect v1.0.0, released today (I've been testing it for a while) :)
const admin = require('firebase-admin');
const functions = require('firebase-functions');
admin.initializeApp();
const firestore = admin.firestore();
exports.getWatsonTokenFS = functions.firestore
.document('User_Login_Event/{Toggle_Value}')
.onUpdate((snap, context) => {
let username = 'TDK';
let password = 'swordfish';
let url = `https://${username}:${password}#stream.watsonplatform.net/authorization/api/v1/token?url=https://stream.watsonplatform.net/speech-to-text/api`;
// request({url}, function (error, response, body) {
// firestore.doc(`${IBM_Watson_Token}/${Token_Value}`).update('token');
// });
return firestore.doc(`IBM_Watson_Token/Token_Value`).update('token')
.then(response => {
return Promise.resolve();
})
.catch(err => {
return Promise.reject(err);
});
});
Now that Firebase has updated firebase-admin to 5.12.0 and firebase-functions to 1.0.1 my test function is working. The function that Jason Berryman wrote is correct except for two lines. Jason wrote:
.onUpdate((snap, context) => {
That should be
.onUpdate((change, context) => {
Secondly, Jason wrote:
return firestore.doc(`IBM_Watson_Token/Token_Value`).update('token')
The corrected line is:
return firestore.collection('IBM_Watson_Token').doc('Token_Value').update({
token: 'newToken'
})
I made two changes in Jason's code. First, I changed the location syntax; more on this below. Second, update() requires an object as the argument.
To show the syntax for locations, I wrote a simple Cloud Function that triggers when a value at a location in Cloud Firestore changes, and then writes a new value to a different location in Cloud Firestore. I removed the line const firestore = admin.firestore(); to make the code more clear:
const admin = require('firebase-admin');
const functions = require('firebase-functions');
admin.initializeApp();
exports.testFunction = functions.firestore.document('triggerCollection/{documentID}').onUpdate((change, context) => {
return admin.firestore().collection('writeCollection').doc('documentID').update({
token: 'newValue'
})
.then(response => {
return Promise.resolve();
})
.catch(err => {
return Promise.reject(err);
});
});
Let's compare three syntaxes for locations in Cloud Firestore. First, in the browser I use this syntax:
firebase.firestore().collection('myCollection').doc('documentID')
Next, in a Cloud Function trigger I use this syntax:
functions.firestore.document('myCollection/{documentID}')
Third, in the Cloud Function return I use this syntax:
admin.firestore().collection('myCollection').doc('documentID')
The first and last lines are the same except that from the browser you call Firebase with firebase, when from the server you call Firebase using the firebase-admin Node package, here aliased to admin.
The middle line is different. It's calling Firebase using the firebase-functions Node package, here aliased to functions.
In other words, Firebase is called using different libraries, depending on whether you're calling from the browser or the server (e.g., a Cloud Function), and whether in a Cloud Function you're calling a trigger or a return.
Cloud functions is triggered based on events happening in Firebase example in realtime database, authentication.
Cloud firestore is triggered based on events happening in Firestore which uses the concept of documents and collections.
As explained here:
https://firebase.google.com/docs/functions/firestore-events
The cloud firestore triggers are used when there is a change in a document.
I had the same problem. I used following
const getReceiverDataPromise = admin.firestore().doc('users/' + receiverUID).get();
const getSenderDataPromise = admin.firestore().doc('users/' + senderUID).get();
return Promise.all([getReceiverDataPromise, getSenderDataPromise]).then(results => {
const receiver = results[0].data();
console.log("receiver: ", receiver);
const sender = results[1].data();
console.log("sender: ", sender);
});

Resources