Trying to susbcribe to PairCreated events from UniswapV2 Factory in Nodejs - javascript

I have previously used this code to get events from PancakeSwapV2 factory on the Binance Smart Chain. I'd like now to use this code to get events from UniswapV2 factory on the Ethereum blockchain but I get the following error :
(node:3544) UnhandledPromiseRejectionWarning: Error: resolver or addr is not configured for ENS name (argument="name", value="0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f ", code=INVALID_ARGUMENT, version=contracts/5.4.0)
at Logger.makeError (C:\Users\aaaa\WebstormProjects\web3test\node_modules\#ethersproject\logger\lib\index.js:187:21)
at Logger.throwError (C:\Users\aaaa\WebstormProjects\web3test\node_modules\#ethersproject\logger\lib\index.js:196:20)
at Logger.throwArgumentError (C:\Users\aaaa\WebstormProjects\web3test\node_modules\#ethersproject\logger\lib\index.js:199:21)
at C:\Users\aaaa\WebstormProjects\web3test\node_modules\#ethersproject\contracts\lib\index.js:101:32
at step (C:\Users\aaaa\WebstormProjects\web3test\node_modules\#ethersproject\contracts\lib\index.js:48:23)
at Object.next (C:\Users\aaaa\WebstormProjects\web3test\node_modules\#ethersproject\contracts\lib\index.js:29:53)
at fulfilled (C:\Users\aaaa\WebstormProjects\web3test\node_modules\#ethersproject\contracts\lib\index.js:20:58)
at processTicksAndRejections (internal/process/task_queues.js:95:5)
(Use `node --trace-warnings ...` to show where the warning was created)
(node:3544) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To
terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
(node:3544) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
Process finished with exit code 0
Here's the source code I'm trying to reuse :
const Web3 = require('web3');
const ethers = require('ethers');
const INFURA_BASE_URL = 'https://mainnet.infura.io/v3/';
const INFURA_API_KEY = 'REPLACE';
web3 = new Web3(new Web3.providers.HttpProvider(INFURA_BASE_URL + INFURA_API_KEY));
const privateKey = "REPLACE";
const account = web3.eth.accounts.privateKeyToAccount(privateKey)
console.log(account.address)
const provider = new ethers.providers.JsonRpcProvider('https://mainnet.infura.io/v3/REPLACE');
const wallet = new ethers.Wallet(privateKey);
const account2 = wallet.connect(provider);
const addresses = {
WETH: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
factory: '0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f ',
router: '0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D ',
recipient: account.address
};
//console.log(provider)
const factory = new ethers.Contract(
addresses.factory,
[
'event PairCreated(address indexed token0, address indexed token1, address pair, uint)',
'function getPair(address tokenA, address tokenB) external view returns (address pair)'
],
account2
);
factory.on('PairCreated', async (token0, token1, pairAddress) => { }
Would you know what I'm doing wrong ? Thank you.

It seems that I'm approaching the solution with this piece of code :
const Web3 = require("web3");
let web3 = new Web3(
new Web3.providers.WebsocketProvider("wss://mainnet.infura.io/ws/v3/cc44823998a0412294a47680xxxxxxxx")
);
let abi = JSON.parse('[{"inputs":[{"internalType":"address","name":"_feeToSetter","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token0","type":"address"},{"indexed":true,"internalType":"address","name":"token1","type":"address"},{"indexed":false,"internalType":"address","name":"pair","type":"address"},{"indexed":false,"internalType":"uint256","name":"","type":"uint256"}],"name":"PairCreated","type":"event"},{"constant":true,"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"allPairs","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"allPairsLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"}],"name":"createPair","outputs":[{"internalType":"address","name":"pair","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"feeTo","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"feeToSetter","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"getPair","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_feeTo","type":"address"}],"name":"setFeeTo","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_feeToSetter","type":"address"}],"name":"setFeeToSetter","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}]')
const instance = new web3.eth.Contract(abi, '0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f');
//web3.eth.getBlockNumber().then(console.log)
instance.getPastEvents(
"allEvents",
{fromBlock: "12908000", toBlock: "12908094"},
(errors, events) => {
if (!errors) {
//console.log('it is ok')
//console.log(events)
}
}
).then(r => {
console.log(r)
});

Related

Firebase: Cloud function changing data in firestore - Permission denied on resource project [X:YYYYYYYYYY:web:KKKKKKKKKKKK]

I am trying to add data to my firestore collection via firebase cloud functions. However, when executing the function the following error appears.
My cloud function code is:
import * as functions from "firebase-functions";
import * as admin from "firebase-admin";
admin.initializeApp();
const Firestore = require("#google-cloud/firestore");
const PROJECTID = "[...]";
const firestore = new Firestore({
projectId: PROJECTID,
timestampsInSnapshots: true,
});
exports.createNewChat = functions.region("europe-west1")
.https.onRequest((request, response) => {
console.log("!!!!!!");
console.log("THe BodY: " + request);
console.log("THe BodY: " + request.rawBody);
console.log("THe BodY: " + request.body);
try {
const groupName = request.body.groupName;
const members: string[] = request.body.members;
firestore.collection("chats").add({
groupName: groupName,
members: members,
lastMessage: new Date(),
messages: [],
}).then((doc: any) => {
const chatId = doc.id;
members.forEach((member) => {
firestore.collection("myChats/" + member).add({
chatKey: chatId,
});
});
return response.status(200).send(doc);
});
} catch (e) {
functions.logger.log("catch clause " + e);
response.status(500).send(e);
}
});
My postman request looks like this:
Header: Content-Type -> application/json
Body (raw, json):
{
"groupName": "someGroupName",
"members": [
"123321aklslasl"
]
}
The exception which is thrown is:
! Google API requested!
URL: "https://oauth2.googleapis.com/token"
Be careful, this may be a production service.
(node:23224) UnhandledPromiseRejectionWarning: Error: 7 PERMISSION_DENIED: Permission denied on resource project [...].
at Object.callErrorFromStatus (E:\Workspaces\cloudFunctions\functions\node_modules#grpc\grpc-js\build\src\call.js:31:26)
at Object.onReceiveStatus (E:\Workspaces\cloudFunctions\functions\node_modules#grpc\grpc-js\build\src\client.js:179:52)
at Object.onReceiveStatus (E:\Workspaces\cloudFunctions\functions\node_modules#grpc\grpc-js\build\src\client-interceptors.js:336:141)
at Object.onReceiveStatus (E:\Workspaces\cloudFunctions\functions\node_modules#grpc\grpc-js\build\src\client-interceptors.js:299:181)
at E:\Workspaces\cloudFunctions\functions\node_modules#grpc\grpc-js\build\src\call-stream.js:145:78
at processTicksAndRejections (internal/process/task_queues.js:79:11)
Caused by: Error
at WriteBatch.commit (E:\Workspaces\cloudFunctions\functions\node_modules#google-cloud\firestore\build\src\write-batch.js:414:23)
at DocumentReference.create (E:\Workspaces\cloudFunctions\functions\node_modules#google-cloud\firestore\build\src\reference.js:291:14)
at CollectionReference.add (E:\Workspaces\cloudFunctions\functions\node_modules#google-cloud\firestore\build\src\reference.js:1967:28)
at E:\Workspaces\cloudFunctions\functions\lib\index.js:39:39
at C:\Users\XXXXX\AppData\Roaming\npm\node_modules\firebase-tools\lib\emulator\functionsEmulatorRuntime.js:560:16
at runFunction (C:\Users\XXXXX\AppData\Roaming\npm\node_modules\firebase-tools\lib\emulator\functionsEmulatorRuntime.js:533:15)
at runHTTPS (C:\Users\XXXXX\AppData\Roaming\npm\node_modules\firebase-tools\lib\emulator\functionsEmulatorRuntime.js:559:11)
at handler (C:\Users\XXXXX\AppData\Roaming\npm\node_modules\firebase-tools\lib\emulator\functionsEmulatorRuntime.js:479:23)
at Layer.handle [as handle_request] (C:\Users\XXXXX\AppData\Roaming\npm\node_modules\firebase-tools\node_modules\express\lib\router\layer.js:95:5)
at next (C:\Users\XXXXX\AppData\Roaming\npm\node_modules\firebase-tools\node_modules\express\lib\router\route.js:137:13)
(node:23224) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). T
o terminate the node process on unhandled promise rejection, use the CLI flag --unhandled-rejections=strict (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 3)
(node:23224) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
In a Cloud Function for Firebase you should use the Admin SDK for Node.js and not the standard JS SDK.
In addition, you need to use Promise.all() to execute in parallel the calls to the asynchronous add() method in the forEach() loop.
So the following set of adaptations should solve your problem:
import * as functions from "firebase-functions";
import * as admin from "firebase-admin";
admin.initializeApp();
exports.createNewChat = functions.region("europe-west1")
.https.onRequest((request, response) => {
const groupName = request.body.groupName;
const members: string[] = request.body.members;
const db = admin.firestore();
db.collection("chats").add({
groupName: groupName,
members: members,
lastMessage: new Date(),
messages: [],
}).then((doc: any) => {
const chatId = doc.id;
const promises = [];
members.forEach((member) => {
promises.push(db.collection("myChats/" + member).add({
chatKey: chatId,
}));
});
return Promise.all(promises);
})
.then(() => {
response.status(200).send(JSON.stringify(doc));
})
.catch(e => {
functions.logger.log("catch clause " + e);
response.status(500).send(JSON.stringify(e));
});
});

Error picking up the user's avatar (discord.js)

Code
module.exports.run = async (bot, message, args) =>{
// at the top of your file
const discord = require('discord.js');
const config = require("../config");
// inside a command, event listener, etc.
const embed = new discord.MessageEmbed()
.setColor('RANDOM')
.setTitle('Créditos do Os Profissionais')
.setURL('https://discord.gg/')
.setAuthor('Effy', 'https://i.imgur.com/wSTFkRM.png', 'https://stackoverflow.com/users/15303029/meredithgrey')
.setDescription('Criador do grupo e desenvolvedor do BOT')
.setThumbnail('https://i.imgur.com/1oHJJZQ.png')
.addFields(
{ name: 'Créditos de equipe', value: 'Equipe gestora' },
{ name: '\u200B', value: '\u200B' },
{ name: 'Snoot', value: 'Owner', inline: true },
{ name: 'Texugo', value: 'Owner', inline: true },
)
.addField('Leo', 'Owner', true)
.setImage('https://i.imgur.com/1oHJJZQ.png')
.setTimestamp()
.setFooter(message.author.username, message.author.avatar);
message.channel.send(embed);
}
module.exports.config = {
name: "credits",
aliases: ["creditos"]
}
Error:
(node:5676) UnhandledPromiseRejectionWarning: DiscordAPIError: Invalid Form Body
embed.footer.icon_url: Scheme "fbea7946b1cf05e3bfbff344733ba775" is not supported. Scheme must be one of ('http', 'https').
at RequestHandler.execute (C:\Users\Pc\Desktop\RemakeTO\node_modules\discord.js\src\rest\RequestHandler.js:154:13)
at processTicksAndRejections (internal/process/task_queues.js:93:5)
at async RequestHandler.push (C:\Users\Pc\Desktop\RemakeTO\node_modules\discord.js\src\rest\RequestHandler.js:39:14)
(Use `node --trace-warnings ...` to show where the warning was created)
(node:5676) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
(node:5676) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
Since when I started using discord.js V12 i walk with many doubts and one of them is, how to catch the avatar of a user? Can someone help me? I know it's a stupid doubt but I really don't know
message.author.avatar is not a method. You need message.author.avatarURL
//author's avatar:
.setThumbnail(message.author.avatarURL);
As mentioned above, try using the docs
// In discord.js v12
message.author.displayAvatarURL()
I was having a similar issue. The problem was that Discord was being sent the embed before the promise of fetching the user and image was fulfilled. This is the code I used to get this functionality.
const client = new Discord.Client();
let thanos = client.users.fetch('IDHERE');
thanos.then(function(result1) {
//put your code that uses the result1 (the user object) here
//for example, you could put your entire embed in here and
//in setFooter you could use result1.displayAvatarURL()
});

Discord bot wont run seemingly correct code

So I'm writing an unban script for my discord bot - it keeps throwing errors when ONLY this command is used - it looks correct to me and my mate, is there something I am overlooking?
const discord = require("discord.js");
module.exports = {
name: 'unban',
category: 'moderation',
description: 'unban a members',
aliases: [''],
run: async (message, client, args) => {
let unbanned = message.mentions.users.first() || client.users.resolve(args[0]);
let reason = args.slice(1).join(" ");
let member = await client.users.fetch(unbanned);
let ban = await message.guild.fetchBans();
// MESSAGES
if (!unbanned) {
let unbaninfoembed = new Discord.MessageEmbed()
.setTitle("Command: unban")
.setDescription(
`**Description:** Unban a user. \n` +
"**Sub Commands:**\n" +
"" +
"**Usage:**\n" +
"-unban [user] (limit) (reason) \n" +
"**Examples:** \n" +
"-unban <#759175803409530932> good guy \n" +
"-unban 759175803409530932 good guy "
)
.setColor("#2C2F33");
message.channel.send(unbaninfoembed);
return;
}
if (!ban.get(member.id)) {
let notbannedembed = new Discord.MessageEmbed()
.setDescription("This user is not banned")
.setColor("#2C2F33");
message.channel.send(notbannedembed);
return;
}
if (!message.guild.me.permissions.has("BAN_MEMBERS")) {
let botnoperms = new Discord.MessageEmbed()
.setDescription(
"I do not have permissions, please contact an administrator"
)
.setColor("#2C2F33");
message.channel.send(botnoperms);
return;
}
if (!message.member.permission.has("BAN_MEMBERS")) {
let nopermsembed = new Discord.MessageEmbed()
.setDescription(
"You do not have permission `BAN MEMBERS` contact an administrator"
)
.setColor("#2C2F33");
message.channel.send(nopermsembed);
return;
}
var user = ban.get(member.id);
message.guild.members.unban(member);
let successfullyembed = new Discord.MessageEmbed()
.setTitle(`Successfully Unbaned!`)
.setDescription(`${member.tag} has been successfully unbanned`)
.addField(`Reason: ${reason}`)
.setColor("#2C2F33");
message.channel.send(successfullyembed);
},
};
Gives the error:
UnhandledPromiseRejectionWarning: DiscordAPIError: 404: Not Found
at RequestHandler.execute (E:\Lyckas\node_modules\discord.js\src\rest\RequestHandler.js:170:25)
at processTicksAndRejections (internal/process/task_queues.js:93:5)
(Use `node --trace-warnings ...` to show where the warning was created)
(node:17316) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
(node:17316) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
Recently Discord has changed the main website from "discordapp.com" to "discord.com", try reinstalling the discord.js module and see if it helps.

Discord.js embed image not working. (Could not interpret "{'url': 'https://cdn.nekos.life/boobs/boobs105.gif'}" as string.)

My command.js
based on some other bots like HarutoHiroki Bot thst open source on Github and the Nekos.life documentation
const Discord = require('discord.js');
const client = require('nekos.life');
const neko = new client();
module.exports.run = async (bot, message, args) => {
if (message.channel.nsfw === true) {
link = await neko.nsfw.boobs()
console.log(link)
const embed = new Discord.MessageEmbed()
.setAuthor(`Some neko boobs`)
.setColor('#00FFF3')
.setImage(link)
.setFooter(`Bot by`);
message.channel.send(embed);
}
else {
message.channel.send("This isn't NSFW channel!")
}
};
module.exports.config = {
name: "boobs",
description: "",
usage: "*boobs",
accessableby: "Members",
aliases: []
}
Error:
> node .
(node:25052) ExperimentalWarning: Conditional exports is an experimental feature. This feature could change at any time
Logged in and connected as Bot#1234
{ url: 'https://cdn.nekos.life/boobs/boobs105.gif' }
(node:25052) UnhandledPromiseRejectionWarning: DiscordAPIError: Invalid Form Body
embed.image.url: Could not interpret "{'url': 'https://cdn.nekos.life/boobs/boobs105.gif'}" as string.
at RequestHandler.execute (C:\Users\User\Documents\GitHub\Bot\node_modules\discord.js\src\rest\RequestHandler.js:170:25)
at processTicksAndRejections (internal/process/task_queues.js:97:5)
(node:25052) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)(node:25052) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
My Question
how to fix this? (I tried the hole day but didn't got it working need help soon as possible)
I fixed it:
const Discord = require('discord.js');
const client = require('nekos.life');
const neko = new client();
module.exports.run = async (bot, message, args) => {
if (message.channel.nsfw === true) {
link = await neko.nsfw.boobs()
const embed = new Discord.MessageEmbed()
.setAuthor(`Some neko boobs`)
.setColor('#00FFF3')
.setImage(link.url)
.setFooter(`Bot by`);
message.channel.send(embed);
}
else {
message.channel.send("This isn't NSFW channel!")
}
};
module.exports.config = {
name: "boobs",
description: "",
usage: "*boobs",
accessableby: "Members",
aliases: []
}
It looks like neko.nsfw.boobs() returns an object, not a string. Try this instead:
const { url } = await neko.nsfw.boobs(); // get only the URL, not the whole object
const embed = new Discord.MessageEmbed()
.setAuthor(`Some neko boobs`)
.setColor('#00FFF3')
.setImage(link.url)
.setFooter(`Bot by`);
message.channel.send(embed);
Here's a code snippet example:
const link = {
url: 'https://cdn.nekos.life/boobs/boobs105.gif'
};
console.log(link); // this will not give the link. it will give the whole object
/* using object destructuring, you can use brackets to capture one
property of an array. in this case, the url.
Object Destructuring - https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment
*/
const {
url
} = link;
console.log(url); // this will give *only* the link
console.log(link.url); // another method to give only the link

Web-scraping images with JavaScript Cheerio

I receive an error when attempting to web-scrape images off amazon. The goal is to download the book cover.
(node:26904) UnhandledPromiseRejectionWarning: Error: undefined is not a valid uri or options object.
at request (C:\Users\Isaac\Desktop\webscrape_with_cheerio\node_modules\request\index.js:44:11)
at PromisseHandle._RejectOrResolve (C:\Users\Isaac\Desktop\webscrape_with_cheerio\node_modules\node-image-downloader\src\image-downloader.js:86:5)
at new Promise (<anonymous>)
at ImageDownloader (C:\Users\Isaac\Desktop\webscrape_with_cheerio\node_modules\node-image-downloader\src\image-downloader.js:98:26)
at Request._callback (C:\Users\Isaac\Desktop\webscrape_with_cheerio\index.js:22:13)
at Request.self.callback (C:\Users\Isaac\Desktop\webscrape_with_cheerio\node_modules\request\request.js:185:22)
at Request.emit (events.js:210:5)
at Request.<anonymous> (C:\Users\Isaac\Desktop\webscrape_with_cheerio\node_modules\request\request.js:1154:10)
at Request.emit (events.js:210:5)
at IncomingMessage.<anonymous> (C:\Users\Isaac\Desktop\webscrape_with_cheerio\node_modules\request\request.js:1076:12)
(node:26904) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 2)
(node:26904) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with
a non-zero exit code.
Here is my code thus far. I believe my error stems from the line;
var imagesrc = $(".a-row center-align litb-on-click img").attr('src')
I need to access the Class over where the book url is, though i might be wrong.
const express = require('express');
const cheerio = require('cheerio');
const download = require('node-image-downloader');
const request = require('request');
const app = express();
app.get('/', (req, res) => {
var url = "https://www.amazon.com/Black-Swan-Improbable-Robustness-Fragility/dp/081297381X"
// makeing a request
request(url,(error, response,html) => {
if (!error){
//console.log(html);
var $ = cheerio.load(html)
var imagesrc = $(".a-row center-align litb-on-click img").attr('src')
//download the image
download({
imgs: [
{
uri:imagesrc
}
],
dest:'./downloads'
})
.then((info) => {
console.log("Download Complete")
process.exit(1)
})
}
})
})
app.listen(5000)
Here, a-row center-align litb-on-click are classes
To select a class we need to use class-selector . (dot)
please try to update your imagesrc variable as below
var imagesrc = $(".a-row.center-align.litb-on-click img").attr('src');
Spaces are not compulsory.

Categories

Resources