How to ensure invitation code is unique in Redis - javascript

I need to generate an unique user friendly code and save it into Redis either until the invited user grads it or it expires.
Since the code has to be user friendly I have decided to use 6 digit number, which the frontend will divide into two groups e.g. xxx-xxx.
Now, on the backend, I have NodeJS and node_redis.
This is how I generate the random string and save it into the Redis:
var invCode = Math.floor(Math.random() * 90000) + 100000;
var key = "invitation-code:" + invCode;
const TTL = 3 * 24 * 60 * 60; // 3 days
redis.client.existsAsync(key)
.then(res => {
if (!res) {
// ok, I can add the key, value pair
return redis.client.setAsync(key, value, 'EX', TTL);
} else {
// I have to generate new key and check it again
// how can I re-iterate the process???
return null;
}
})
.then(res => {
logger.info('InvitationCodeController::generate added <' + key + ', ' + value + '> pair');
})
.catch(error => {
logger.error('InvitationCodeController::generate Error ' + error);
});
Now, the point I cannot figure out is - in case the generated code already exists, how can I re-iterate the process, i.e. to generate another random string, format it, check in Redis and etc.
Since I have async call, I don't think any kind of loop can work for me?
Any ideas?

You could utilize a "attempts" process like the following.
You could also make a while loop similarly by removing the --n portion.
Additionally I think you should use the "NX" parameter for "SETNX" --- Set when the value does not exist. Otherwise it is possible that between the time you check redis for whether the key exists and the time you actually set it, you can overwrite some other key. You might even rewrite it at this point so rely on SETNX throwing an error on failing to set rather than checking the value each time.
const process = require('process');
const redis = require("redis");
const Bluebird = require('bluebird')
Bluebird.promisifyAll(redis.RedisClient.prototype)
Bluebird.promisifyAll(redis.Multi.prototype)
const winston = require('winston');
const logger = winston.createLogger({
level: 'silly',
format: winston.format.json(),
transports: [new winston.transports.Console({
format: winston.format.simple()
})]
});
const client = redis.createClient({
host:'redis-19141.c16.us-east-1-3.ec2.cloud.redislabs.com',
port:'19141'
});
client.auth('I6C2ISvac4suTbxSYcbsjWiz635NK8Wv');
// client.set("string key", "string val", redis.print);
var invCode = Math.floor(Math.random() * 90000) + 100000;
// test invCode being the same --- retry.
invCode = 111111;
var key = "invitation-code:" + invCode;
const TTL = 3 * 24 * 60 * 60; // 3 days
let value = "test";
const trySet = function(key,n){
const used = process.memoryUsage().heapUsed / 1024 / 1024;
logger.info(`The script uses approximately ${Math.round(used * 100) / 100} MB`);
return client.existsAsync(key)
.then(res => {
logger.info("existsAsync res",res);
if (!res) {
logger.info("Key does not exist!");
return client.setAsync(key, value, 'NX','EX', TTL)
.then(res => {
logger.info('InvitationCodeController::generate added <' + key + ', ' + value + '> pair');
return true;
})
} else {
logger.info("Key already exists!");
if(n > 0){
return trySet(key,--n);
}else{
return false;
}
}
})
.catch(error => {
logger.error('InvitationCodeController::generate Error ' + error);
return false;
});
}
trySet(key,50).then(function(res){
if(res){
logger.info('trySet::success');
}else{
logger.info('trySet::failed');
}
}).catch(error => {
logger.error('trySet::error');
});
https://repl.it/repls/ImmediateSufficientCoin

Since code generation is sync process, I figure out, I can do it other way around. Here is the code:
const TTL = 3 * 24 * 60 * 60; // 3 days
var invCode = '';
const pattern = "invitation-code:";
var prepKey = '';
redis.client.keysAsync(pattern + "*")
.then(keys => {
// these are all keys / invitation codes
var isFound = false;
do {
invCode = Math.floor(Math.random() * 90000) + 100000;
prepKey = pattern + invCode;
// traverse keys to check if the invitation code matches
keys.forEach(key => {
if (key === prepKey) {
isFound = true;
}
});
} while (isFound);
return prepKey;
})
.then(key => {
return redis.client.setAsync(key, value, 'EX', TTL);
})
.then(res => {
logger.info('InvitationCodeController::generate added <' + prepKey + ', ' + value + '> pair');
})
.catch(error => {
logger.error('InvitationCodeController::generate Error ' + error);
});
Hope this help anyone else on the topic...

Related

Is there a way to select only certain fields from Firestore?

I am working on a performance issue of a function, that takes 15sec to response, which makes a request to firebase for all documents that are
"ErrorID" "==" "0"
The problem is that there are many documents and they are kind of very large objects, and I only need TWO FIELDS (Order and Amount) of each document, there are any way to request only those two fields that accomplish the condition?
Something like :
firestore.collection("purchases").where("ErrorID", "==", "0").get(Order, Amount);
The function that im talking about:
const totalEarn = async (req, res, next) => {
const DAY = 86400000;
const WEEK = 604800016;
const MONTH = 2629800000;
try {
let snap = await firestore.collection("purchases").where("ErrorID", "==", "0").get(); // CONDITION
let totalToday = 0;
let totalYesterday = 0;
let totalLastWeek = 0;
let totalLastMonth = 0;
let now = Date.now();
let Yesterday = now - 86400000;
await snap.forEach((doc) => { // THIS FOR EACH TAKES TOO MUCH TIME
let info = doc.data();
let time = info.Order.split("-")[2]; // FIRESTORE FIELD -> ORDER
let amount = info.AmountEur * 1; // FIRESTORE FIELD -> AMOUNT
if (time > now - DAY) {
totalToday = totalToday + amount;
}
if (time < now - DAY && time > Yesterday - DAY) {
totalYesterday = totalYesterday + amount;
}
if (time > now - WEEK) {
totalLastWeek = totalLastWeek + amount;
}
if (time > now - MONTH) {
totalLastMonth = totalLastMonth + amount;
}
});
res.send({
status: true,
data: {
totalToday: totalToday.toFixed(2),
totalYesterday: totalYesterday.toFixed(2),
totalLastWeek: totalLastWeek.toFixed(2),
totalLastMonth: totalLastMonth.toFixed(2),
},
});
} catch (error) {
res.status(410).send({
status: false,
error: "some error occured counting the numbers",
e: error.message,
});
}
};
The document im talking about
If you use Firestore Node.JS client or Firebase Admin SDK, then you can use select() to select fields:
import { Firestore } from "#google-cloud/firestore";
const firestore = new Firestore();
const snap = firestore
.collection("purchases")
.where("ErrorID", "==", "0")
.select("Order", "Amount")
.get();

Can't use Uniswap V3 SwapRouter for multihop swaps, SwapRouter.exactInput(params) throws 'UNPREDICTABLE_GAS_LIMIT'

I'm trying to implement swap with new Uniswap V3 contracts.
I'm using Quoter contract for getting the quotes out and SwapRouter for making the swaps.
If I'm using methods for direct swap (when tokens have pools) for example - -
ethersProvider = new ethers.providers.Web3Provider(web3.currentProvider, 137);
uniSwapQuoter = new ethers.Contract(uniSwapQuoterAddress, QuoterAbi.abi, ethersProvider);
uniSwapRouterV3 = new ethers.Contract(uniSwapRouterAddress, RouterAbi.abi,
ethersProvider.getSigner());
uniSwapQuoter.callStatic.quoteExactInputSingle(.....)
uniSwapQuoter.callStatic.quoteExactOutputSingle(.....)
uniSwapRouterV3.exactInputSingle(params)
everything works fine, but when I try to use the multihop quotes and multihop swaps if fails with
"reason": "cannot estimate gas; transaction may fail or may require manual gas limit",
"code": "UNPREDICTABLE_GAS_LIMIT",
"error": {
"code": -32000,
"message": "execution reverted"
},
"method": "estimateGas",
"transaction": {
"from": "0x532d647481c20f4422A8331339D76b25cA569959",
"to": "0xE592427A0AEce92De3Edee1F18E0157C05861564",
"data": "0xc04b8d59000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000002a6b82b6dd3f38eeb63a35f2f503b9398f02d9bb0000000000000000000000000000000000000000000000000000000861c468000000000000000000000000000000000000000000000000000000000000002710000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000422791bca1f2de4661ed88a30c99a7a9449aa841740005007ceb23fd6bc0add59e62ac25578270cff1b9f619003000c26d47d5c33ac71ac5cf9f776d63ba292a4f7842000000000000000000000000000000000000000000000000000000000000",
"accessList": null
}
for encoding the params I'm using the uniswap example from tests:
function encodePath(tokenAddresses, fees) {
const FEE_SIZE = 3
if (path.length != fees.length + 1) {
throw new Error('path/fee lengths do not match')
}
let encoded = '0x'
for (let i = 0; i < fees.length; i++) {
// 20 byte encoding of the address
encoded += path[i].slice(2)
// 3 byte encoding of the fee
encoded += fees[i].toString(16).padStart(2 * FEE_SIZE, '0')
}
// encode the final token
encoded += path[path.length - 1].slice(2)
return encoded.toLowerCase()
}
and finally my example code I'm doing for quotes:
const routeAndFees = await getAddressPath(path);
const encodedPath = await encodePath(routeAndFees.path, routeAndFees.fees);
const usdcWithDecimals = parseFloat(usdcAmount) * 1000000
const tokenDecimals = path[path.length - 1].tokenOut.decimals;
try {
const amountOut = await uniSwapQuoter.callStatic.quoteExactInput(encodedPath, usdcWithDecimals.toString());
console.log("Token amount out:", parseFloat(amountOut) / (10 ** tokenDecimals));
return {
tokenOut: parseFloat(amountOut) / (10 ** tokenDecimals),
usdcIn: parseFloat(usdcAmount)
};
} catch (e) {
console.log(e);
return e;
}
}
and swapping:
async function multiSwap(path, userAddress, usdcAmount) {
const usdcWithDecimals = parseFloat(usdcAmount) * 1000000
const routeAndFees = await getAddressPath(path);
const encodedPath = await encodePath(routeAndFees.path, routeAndFees.fees);
const params = {
path: encodedPath,
recipient: userAddress,
deadline: Math.floor(Date.now() / 1000) + 900,
amountIn: usdcWithDecimals.toString(),
amountOutMinimum: 0,
}
try {
return await uniSwapRouterV3.exactInput(params);
} catch (e) {
console.log(e);
return e;
}
}
The path is [address,fee,address,fee,address] like it should be, I not sure about the encoding of that, but didn't find any other example. Actually didn't find any example for doing uniswap v3 multihop swaps, even in the UniDocs there is Trade example and single pool swap...
Can someone point what could I have done wrong here?
The same error is in quoting and when swapping :/
I'm testing on Polygon Mainnet and I can make the same path swap directly on uniswap but it fails when I trigger the script...
You should hash the fee value. Instead of 0 add 6. This should work for you:
async function encodePath(path, fees, exactInput) {
const FEE_SIZE = 6
if (path.length !== fees.length + 1) {
throw new Error('path/fee lengths do not match')
}
if (!exactInput) {
path = path.reverse();
fees = fees.reverse();
}
let encoded = '0x'
for (let i = 0; i < fees.length; i++) {
encoded += path[i].slice(2)
let fee = web3.utils.toHex(parseFloat(fees[i])).slice(2).toString();
encoded += fee.padStart(FEE_SIZE, '0');
}
encoded += path[path.length - 1].slice(2)
return encoded
}

Discord.js DND Dice Roll Input Change Number

I've made a "working" dice roll function on my bot. Works fine for what I need, but was wondering how I could make it so depending on what is said in discord it rolls.
So saying !rolld6 would use 6 in var response instead of 20.
if (message.content.toLowerCase().includes("!rolld20")) {
const ayy = client.emojis.cache.find(emoji => emoji.name === "diceroll");
var response = [Math.floor(Math.random() * ((20 - 1) + 1) + 1)];
message.channel.send(`${ayy}` + `Rolling.`)
.then(msg => {
setTimeout(function() {
msg.edit(`${ayy}` + `Rolling..`)
}, 1000);
setTimeout(function() {
msg.edit(`${ayy}` + `Rolling...`)
}, 2000)
setTimeout(function() {
msg.edit(`${ayy}` + `Rolling....`)
}, 3000)
setTimeout(function() {
msg.edit(`${ayy}` + " Rolled... " + response + " " + `${ayy}`).then().catch(console.error)
}, 4000)
})
return;
}
Not even sure what I would search to figure this out so any help is greatly appreciated!
Use a regular expression instead of .includes, and then extract the digits after the d to randomize.
You don't need to subtract 1, then add 1 right after that - those cancel out, nor do you need to put the result into an array - interpolating just the random number generated into the message will work fine.
const match = message.content.match(/!rolld(\d+)/i);
if (match) {
const die = match[1];
const response = 1 + Math.floor(Math.random() * die);
const ayy = client.emojis.cache.find(emoji => emoji.name === "diceroll");

Value Undefined: Loops in async await with Promise.race?

I am working on a project where I need to brute force PDF password.
For that PDF.js is used for verifying password and promise.race to run parallel functions to make the overall work fast.
and this is how i implemented it:
var sfile = "KRIS.pdf"
var dBuf = fs.readFileSync(sfile);
const tryCases = getCombos(alphaArray, 4); // permutation having total length 456976
var finishFunction = false;
async function getPass(startAt = Number(), endAt = Number()) {
var isDone = false;
var currentPass = ''
for (let index = startAt; index < endAt; index++) {
if (finishFunctions) { return; }
currentPass = tryCases[index].join("");
await pdfjsLib.getDocument({ data: dBuf, password: currentPass }).promise
.then((r) => {
console.log('Worked: ' + currentPass);
isDone = true;
pdfjsLib.distroy();
return new Promise.resolve();
})
.catch((e) => { })
if (isDone) {
finishFunctions = true;
return currentPass;
}
}
console.log(`Hey Nothing From ${startAt} - ${endAt}`);
}
console.time('Found ');
Promise.race([
getPass(0, 100000),
getPass(100000, 200000),
getPass(200000, 300000),
getPass(300000, 400000),
getPass(400000, 456976)
])
.then((s) => { console.timeEnd('Found '); console.log('HeyThen ' + s) })
.catch((e) => console.log('Hey Error ' + e));
now it works to get the 4 letter password but There are problems preventing it from being complete.
First Current Function is very very slow, it takes forever even after running parallel functions.
Second I added a flag to stop other parallel functions but it does not work as expected with 4 Letter Forcing.
Third Resource usage is really high. My Linux system stops responding.
for visualizing the time and flag issue I used 3 letters forcing and here is log of it:
Worked: KRIS
Found: 13950.743ms
HeyThen KRIS
Hey Nothing From 4934 - 8788
Hey Nothing From 0 - 4394
Hey Nothing From 13182 - 17576
(node:3068) [DEP0005] DeprecationWarning: Buffer() is deprecated due to security and usability issues. Please use the Buffer.alloc(), Buffer.allocUnsafe(), or Buffer.from() methods instead.
By calling return new Promise.resolve(currentPass); you return a result to then and not from ATryCase. Write the result of await myFunc.getDocument to some variable and return it
const res = await myFunc.getDocument({ data: dBuf, pos: currentPass })
.promise
.then(() => {
console.log('Data is: ' + currentPass);
console.timeEnd('GotData ');
return new Promise.resolve(currentPass);
}).catch(e => { });
return res;
if the currentPass is correct, i need to break the loop and return the value
None of your code snippets did that so far. You will want to use something like
async function ATryCase(indexStart, indexEnd) {
for (let index = indexStart; index < indexEnd; index++) {
var currentPass = startLetters + index;
try {
await myFunc.getDocument({ data: dBuf, pos: currentPass }).promise;
console.log('Data is: ' + currentPass);
return currentPass; // this breaks the loop and `return`s from ATryCase
} catch {
continue;
}
}
throw new Error("No pass did find a result");
}
for that.
Alternatively, have a look at the proposed Promise.any, which would do exactly what you are looking for, but concurrently. (You will also want to use it in place of Promise.race if you go for 4 concurrent sequential searches). You could use it like
function ATryCase(indexStart, indexEnd) {
const promises = [];
for (let index = indexStart; index < indexEnd; index++) {
var currentPass = startLetters + index;
promises.push(myFunc.getDocument({ data: dBuf, pos: currentPass }).promise.then(() => {
console.log('Data is: ' + currentPass);
return currentPass;
}));
}
return Promise.any(promises);
}

How Make Dynamic Zero Filled Number in Node js

so I was given the task to make a unique code by adding numbers after the code.
and I have tried and succeeded but there is one thing that blocks what if the number has reached its limit? 9999 value
how to reset it to 00001. Lift digit increased by 1
here's my snippet code
function getNewCode(value, callback){
let newCode = _.upperCase(value) + "-";
let lastCode = newCode + "0001"
Transaction.findOne({tr_number: new RegExp(newCode, 'i')}, (err, doc) => {
if(err) callback(err)
if (!_.isNil(doc)){
let arr = doc.tr_number.split("-");
// in this part, i want got some conditional to set a new value
//when it got max length = 9999 for an example
let inc = parseInt(arr[1]) + 1;
lastCode = newCode + ("0000"+inc).slice(-4);
console.log('lastCode', ciduk, lastCode);
return callback(lastCode);
}else{
return callback(lastCode);
}
}).sort({tr_number: -1})
};
sorry for my bad language :) grats.
You can get number size by converting it toString() and get it length.
function getNext(prevStr) {
let padSize = prevStr.length;
let next = parseInt(prevStr, 10) + 1;
if (next >= 10 ** padSize) {
padSize++;
next = 1;
}
return next.toString().padStart(padSize, '0');
}
console.log(getNext('0099')); // '0100'
console.log(getNext('9999')); // '00001'

Categories

Resources