Signed token approvals with web3.js: ERC20Permit signature valid but allowance remains 0 - web3js

My app lets users make a signed token approval to transfer an ERC20Permit token, using web3.js injected.
Token contract is canonical from OpenZeppelin.
Signature seems to be valid (permit call does not revert) but calling allowance on the token afterward shows an allowance of 0.
Also, I notice that subsequent signature calls still have a nonces of 0 for the address, so the nonce seemingly does not increase even though permit calls go through.
Here is the react app code:
contract = // my contract
contractAddress = 0x... // my contract address
amount = BigNumber(amount) // with confirmed decimal job
deadline = Date.now() + 20 * 60 * 1000 // valid deadline
await contract.methods.permit(account.address, contractAddress, amount, deadline, v, r, s).call();
console.log(await contract.methods.allowance(account.address, contractAddress).call()) // 0
Here is the detail of the signature built:
const EIP712Domain = [
{ name: "name", type: "string" },
{ name: "version", type: "string" },
{ name: "chainId", type: "uint256" },
{ name: "verifyingContract", type: "address" },
];
const contractName = await contract.methods.name().call();
const domain = {
name: contractName,
version: "1",
chainId: 5,
verifyingContract: contractAddress
};
const Permit = [
{ name: "owner", type: "address" },
{ name: "spender", type: "address" },
{ name: "value", type: "uint256" },
{ name: "nonce", type: "uint256" },
{ name: "deadline", type: "uint256" },
];
const nonce = await contract.methods.nonces(account.address).call();
const deadline = Date.now() + 20 * 60;
const message = {
owner: account.address,
spender: contractAddress,
value: amount,
nonce: nonce.toString(16),
deadline: deadline,
};
const params = JSON.stringify({
types: {
EIP712Domain,
Permit,
},
domain,
primaryType: "Permit",
message,
});
const _signMessage = async (userAddress, web3, dataToSign, signatureMethod = 'eth_signTypedData_v4') => {
const signature = await web3.currentProvider.send(signatureMethod, [userAddress, dataToSign]);
return signature;
};
const signature = await _signMessage(account.address, web3, params);
const getSignatureParameters = signature => {
if (!web3.utils.isHexStrict(signature)) {
throw new Error(
'Given value "'.concat(signature, '" is not a valid hex string.')
);
}
var r = signature.slice(0, 66);
var s = "0x".concat(signature.slice(66, 130));
var v = "0x".concat(signature.slice(130, 132));
v = web3.utils.hexToNumber(v);
if (![27, 28].includes(v)) v += 27;
return {
r: r,
s: s,
v: v
}
}
const { r, s, v } = getSignatureParameters(signature.result);

Related

How to recall getServerSideProps value without reload the page when the value changed

I want to change languageId value without the page reloading. When the languageId value changes the title of products is changed.
export const getServerSideProps = async ({ req, res, query }) => {
try {
const languageId = getCookie('appLanguageId', { req, res })
const countryid = getCookie('countryId', { req, res })
const appLanguage = getCookie('appLanguage', { req, res })
const { id, page, brandId } = query
var fields = [{ condition: 'contains', value: id, dataField: 'categoryPath' }];
// FILTERS
let flag = true;
if (query.brandIds) {
flag = false;
fields.push({ condition: "equal", value: query.brandIds, dataField: 'brandId' });
}
if (query.priceStart) {
flag = false;
fields.push({ condition: ">=", value: query.priceStart, dataField: 'listPrice' });
}
if (query.priceEnd) {
flag = false;
fields.push({ condition: "<=", value: query.priceEnd, dataField: 'listPrice' });
}
const response = await axios.post(urlHelper + '/wapi/v1/product/listSearch',
{
filter: {
fields,
page: { number: page || 1, size: 20, },
}
},
{
headers: { languageId, countryid }
}
);
//
const products = response?.data;
const filterOptions = (await axios.get(`${urlHelper}/wapi/v1/category/filterOptions/${id}`)).data
return {
props: {
products: products?.data || [],
totalCount: products?.totalCount || 0,
filterOptions: filterOptions,
filterObj: {
brandIds: query.brandIds ? query.brandIds : null,
priceStart: query.priceStart ? query.priceStart : null,
priceEnd: query.priceEnd ? query.priceEnd : null,
isAllNull: flag
},
loader: false
},
};
};
I use getServerSideProps. How can I fix it? I have to access product titles correctly when the language change.

Args cannot read proprety of undefined ("0")

So basically I have that command that use modals to create giveaways but there is one line bothering me. It is right under the run: async line. I have been trying to search the discord documentation about a change that happened from v13 to v14 (yes this code uses discord.js v1.14.5) but I have found nothing. The error is the following: <rejected> TypeError: Cannot read properties of undefined (reading '0'). If you need anymore code, ask me (like the sub files or the rest of the code). Thanks in advance!
Code:
const {
ChannelType,
ButtonBuilder,
ActionRowBuilder,
ComponentType,
TextInputStyle,
TextInputBuilder,
ModalBuilder,
ButtonStyle,
ApplicationCommandOptionType,
} = require("discord.js");
const ems = require("enhanced-ms");
const start = require("./sub/start");
const pause = require("./sub/pause");
const resume = require("./sub/resume");
const end = require("./sub/end");
const reroll = require("./sub/reroll");
const list = require("./sub/list");
const edit = require("./sub/edit");
module.exports = {
name: "giveaway",
description: "Start a giveaway!",
type: 1,
id: "Giveaway",
options: [
{
name: "start",
description: "start a giveaway",
type: ApplicationCommandOptionType.Subcommand,
options: [
{
name: "channel",
description: "the channel to start the giveaway in",
type: ApplicationCommandOptionType.Channel,
channelTypes: [ChannelType.GuildText],
required: true,
},
],
},
{
name: "pause",
description: "pause a giveaway",
type: ApplicationCommandOptionType.Subcommand,
options: [
{
name: "message_id",
description: "the message id of the giveaway",
type: ApplicationCommandOptionType.String,
required: true,
},
],
},
{
name: "resume",
description: "resume a paused giveaway",
type: ApplicationCommandOptionType.Subcommand,
options: [
{
name: "message_id",
description: "the message id of the giveaway",
type: ApplicationCommandOptionType.String,
required: true,
},
],
},
{
name: "end",
description: "end a giveaway",
type: ApplicationCommandOptionType.Subcommand,
options: [
{
name: "message_id",
description: "the message id of the giveaway",
type: ApplicationCommandOptionType.String,
required: true,
},
],
},
{
name: "reroll",
description: "reroll a giveaway",
type: ApplicationCommandOptionType.Subcommand,
options: [
{
name: "message_id",
description: "the message id of the giveaway",
type: ApplicationCommandOptionType.String,
required: true,
},
],
},
{
name: "list",
description: "list all giveaways",
type: ApplicationCommandOptionType.Subcommand,
},
{
name: "edit",
description: "edit a giveaway",
type: ApplicationCommandOptionType.Subcommand,
options: [
{
name: "message_id",
description: "the message id of the giveaway",
type: ApplicationCommandOptionType.String,
required: true,
},
{
name: "add_duration",
description: "the number of minutes to add to the giveaway duration",
type: ApplicationCommandOptionType.Integer,
required: false,
},
{
name: "new_prize",
description: "the new prize",
type: ApplicationCommandOptionType.String,
required: false,
},
{
name: "new_winners",
description: "the new number of winners",
type: ApplicationCommandOptionType.Integer,
required: false,
},
],
},
],
permissions: {
DEFAULT_MEMBER_PERMISSIONS: "ManageMessages" // User permissions needed
},
/**
* #param {ChatInputCommandInteraction} interaction
*/
run: async (client, interaction, config, db, args, message) => {
const sub = args[0]?.toLowerCase();
//line above
let response;
//
if (sub === "start") {
if (!args[1]) return message.safeReply("Incorrect usage! Please provide a channel to start the giveaway in");
const match = message.guild.findMatchingChannels(args[1]);
if (!match.length) return message.safeReply(`No channel found matching ${args[1]}`);
return await runModalSetup(message, match[0]);
}
//
else if (sub === "pause") {
const messageId = args[1];
response = await pause(message.member, messageId);
}
//
else if (sub === "resume") {
const messageId = args[1];
response = await resume(message.member, messageId);
}
//
else if (sub === "end") {
const messageId = args[1];
response = await end(message.member, messageId);
}
//
else if (sub === "reroll") {
const messageId = args[1];
response = await reroll(message.member, messageId);
}
//
else if (sub === "list") {
response = await list(message.member);
}
//
else if (sub === "edit") {
const messageId = args[1];
if (!messageId) return message.safeReply("Incorrect usage! Please provide a message id");
return await runModalEdit(message, messageId);
}
//
else response = "Not a valid sub command";
await message.safeReply(response);
},
async interactionRun(interaction) {
const sub = interaction.options.getSubcommand();
let response;
//
if (sub === "start") {
const channel = interaction.options.getChannel("channel");
return await runModalSetup(interaction, channel);
}
//
else if (sub === "pause") {
const messageId = interaction.options.getString("message_id");
response = await pause(interaction.member, messageId);
}
//
else if (sub === "resume") {
const messageId = interaction.options.getString("message_id");
response = await resume(interaction.member, messageId);
}
//
else if (sub === "end") {
const messageId = interaction.options.getString("message_id");
response = await end(interaction.member, messageId);
}
//
else if (sub === "reroll") {
const messageId = interaction.options.getString("message_id");
response = await reroll(interaction.member, messageId);
}
//
else if (sub === "list") {
response = await list(interaction.member);
}
//
else if (sub === "edit") {
const messageId = interaction.options.getString("message_id");
const addDurationMs = ems(interaction.options.getInteger("add_duration"));
if (!addDurationMs) {
return interaction.followUp("Not a valid duration");
}
const newPrize = interaction.options.getString("new_prize");
const newWinnerCount = interaction.options.getInteger("new_winners");
response = await edit(interaction.member, messageId, addDurationMs, newPrize, newWinnerCount);
}
//
else response = "Invalid subcommand";
await interaction.followUp(response);
},
};
//...
Command handler:
const client = require("../index");
const { PermissionsBitField, Routes, REST, User } = require('discord.js');
const fs = require("fs");
const colors = require("colors");
module.exports = (client, config) => {
console.log("0------------------| Application commands Handler:".blue);
let commands = [];
// Slash commands handler:
fs.readdirSync('./commands/slash/').forEach((dir) => {
console.log('[!] Started loading slash commands...'.yellow);
const SlashCommands = fs.readdirSync(`./commands/slash/${dir}`).filter((file) => file.endsWith('.js'));
for (let file of SlashCommands) {
let pull = require(`../commands/slash/${dir}/${file}`);
if (pull.name, pull.description, pull.type == 1) {
client.slash_commands.set(pull.name, pull);
console.log(`[HANDLER - SLASH] Loaded a file: ${pull.name} (#${client.slash_commands.size})`.brightGreen);
commands.push({
name: pull.name,
description: pull.description,
type: pull.type || 1,
options: pull.options ? pull.options : null,
default_permission: pull.permissions.DEFAULT_PERMISSIONS ? pull.permissions.DEFAULT_PERMISSIONS : null,
default_member_permissions: pull.permissions.DEFAULT_MEMBER_PERMISSIONS ? PermissionsBitField.resolve(pull.permissions.DEFAULT_MEMBER_PERMISSIONS).toString() : null
});
} else {
console.log(`[HANDLER - SLASH] Couldn't load the file ${file}, missing module name value, description, or type isn't 1.`.red)
continue;
};
};
});
// User commands handler:
fs.readdirSync('./commands/user/').forEach((dir) => {
console.log('[!] Started loading user commands...'.yellow);
const UserCommands = fs.readdirSync(`./commands/user/${dir}`).filter((file) => file.endsWith('.js'));
for (let file of UserCommands) {
let pull = require(`../commands/user/${dir}/${file}`);
if (pull.name, pull.type == 2) {
client.user_commands.set(pull.name, pull);
console.log(`[HANDLER - USER] Loaded a file: ${pull.name} (#${client.user_commands.size})`.brightGreen);
commands.push({
name: pull.name,
type: pull.type || 2,
});
} else {
console.log(`[HANDLER - USER] Couldn't load the file ${file}, missing module name value or type isn't 2.`.red)
continue;
};
};
});
// Message commands handler:
fs.readdirSync('./commands/message/').forEach((dir) => {
console.log('[!] Started loading message commands...'.yellow);
const UserCommands = fs.readdirSync(`./commands/message/${dir}`).filter((file) => file.endsWith('.js'));
for (let file of UserCommands) {
let pull = require(`../commands/message/${dir}/${file}`);
if (pull.name, pull.type == 3) {
client.message_commands.set(pull.name, pull);
console.log(`[HANDLER - MESSAGE] Loaded a file: ${pull.name} (#${client.user_commands.size})`.brightGreen);
commands.push({
name: pull.name,
type: pull.type || 3,
});
} else {
console.log(`[HANDLER - MESSAGE] Couldn't load the file ${file}, missing module name value or type isn't 2.`.red)
continue;
};
};
});
// Registering all the application commands:
if (!config.Client.ID) {
console.log("[CRASH] You need to provide your bot ID in config.js!".red + "\n");
return process.exit();
};
const rest = new REST({ version: '10' }).setToken(config.Client.TOKEN || process.env.TOKEN);
(async () => {
console.log('[HANDLER] Started registering all the application commands.'.yellow);
try {
await rest.put(
Routes.applicationCommands(config.Client.ID),
{ body: commands }
);
console.log('[HANDLER] Successfully registered all the application commands.'.brightGreen);
} catch (err) {
console.log(err);
}
})();
};
InteractionCreate.js:
const { EmbedBuilder } = require("discord.js");
const client = require("../../index");
const config = require("../../config/config.js");
const { QuickDB } = require("quick.db");
const db = new QuickDB();
module.exports = {
name: "interactionCreate"
};
client.on('interactionCreate', async (interaction) => {
if (interaction.isChatInputCommand()) {
const command = client.slash_commands.get(interaction.commandName);
if (!command) return;
try {
command.run(client, interaction, config, db);
} catch (e) {
console.error(e)
};
};
if (interaction.isUserContextMenuCommand()) { // User:
const command = client.user_commands.get(interaction.commandName);
if (!command) return;
try {
command.run(client, interaction, config, db);
} catch (e) {
console.error(e)
};
};
if (interaction.isMessageContextMenuCommand()) { // Message:
const command = client.message_commands.get(interaction.commandName);
if (!command) return;
try {
command.run(client, interaction, config, db);
} catch (e) {
console.error(e)
};
};
if (interaction.isModalSubmit()) { // Modals:
const modal = client.modals.get(interaction.customId);
if (!modal) return interaction.reply({
embeds: [
new EmbedBuilder()
.setDescription('Something went wrong... Probably the Modal ID is not defined in the modals handler.')
.setColor('Red')
],
ephemeral: true
});
try {
modal.run(client, interaction, config, db);
} catch (e) {
console.error(e)
};
}
});

How to destructure object in React with special characters in key

Backend returns an object, and one of the key-value, I would handle differently, how can I split up object?
useEffect(() => {
const tempUserShortId = getTempUserShortId()
axios({
method: 'get',
url: `questionList?tempUserId=${tempUserShortId}&questionIds[]=6f50dbea-196b-4fb6-82ee-fbade62aab98&questionIds[]=6f50dbea-196b-4fb6-82ee-fbade62aab99&questionIds[]=6f50dbea-196b-4fb6-82ee-fbade62aab9a`,
headers: { crossDomain: true },
}).then((res) => {
const {'6f50dbea-196b-4fb6-82ee-fbade62aab9a', ...rest} = res.data;
setQuestions(rest)
})
}, [ind])
It says: ':' expected.ts(1005
Found this online:
const removeKey = () => {
setEmployee(current => {
// 👇️ remove salary key from object
const {salary, ...rest} = current;
return rest;
});
};
https://bobbyhadz.com/blog/react-remove-key-from-state-object
It does not work.
Got back from backend a structure like this:
{
"6F50DBEA-196B-4FB6-82EE-FBADE62AAB99": {
"text": "Mennyire forradalmi az ETH 2.0 bevezetése?",
"options": {
"1CD0D2C0-7494-4BE6-ADE6-E454816B584E": {
"text": "Forradalmi mert PoW helyett PoS lesz. Kevesebb energiát fog fogyasztani a rendszer. Viszont ez még mindig nem skálázható megoldás, a shardingot még nem tudja, és még hosszú évekig nem is fogja tudni, így a tranzakciós díj ugyanúgy magas fog maradni.",
"rating": {}
}
}
},
"6F50DBEA-196B-4FB6-82EE-FBADE62AAB98": {
You have to focus the key of your object and set it to "_" to tell that you will not use it
const obj = {
"6f50dbea-196b-4fb6-82ee-fbade62aab9a": "cool",
name: "test",
value: 3
}
const {"6f50dbea-196b-4fb6-82ee-fbade62aab9a": _, ...rest} = obj // rest equal {name: "test", value: 3}

Update state that depends on other calculated state in React-Hooks

I want to update a state (data) that depends on other calculated state (server)
setServer(prevTweets =>
[...json, ...prevTweets].filter(
(e, i, arr) => i === arr.findIndex(t => t.tweetId === e.tweetId)
)
)
The data above will be used to set the state below (data) :
let totalPositive = 0;
let totalNegative = 0;
let totalNeutral = 0;
server.forEach(tweet => {
if(tweet.sentiment >0) totalPositive++;
if(tweet.sentiment < 0) totalNegative++;
if(tweet.sentiment ===0) totalNeutral++;
})
setData([
{ name: 'Positive', value: totalPositive },
{ name: 'Negative', value: totalNegative },
{ name: 'Neutral', value: totalNeutral },
])
Since it's asynchronous, the setData operation is always late. I know that I can use useEffect but apparently it will make an infinite loop and it's not right to use it in this case.
If you set the new data before you set the server you'd skip one render:
//still defaults to server so if you do't pass anything it;s still the same
const setNewData = (newServer = server) => {
const [
totalPositive,
totalNegative,
totalNeutral,
] = newServer.reduce(
([pos, neg, neu], { sentiment }) =>
sentiment > 0
? [pos + 1, neg, neu]
: sentiment < 0
? [pos, neg + 1, neu]
: [pos, neg, neu + 1],
[0, 0, 0]
);
setData([
{ name: 'Positive', value: totalPositive },
{ name: 'Negative', value: totalNegative },
{ name: 'Neutral', value: totalNeutral },
]);
};
setServer(prevTweets => {
const newServer = uniqueByTweetId([
...json,
...prevTweets,
]);
setNewData(newServer);
return newServer;
});
Unrelated to the question but could be important is that the way you get unique values could be improved. You could get unique values in one pass without having to call find index many times:
const uniqueBy = getter => arr => {
const items = new Map();
return arr.filter(item => {
const key = getter(item);
const ret = items.get(key);
items.set(key,true);
return !ret;
});
};
const data = [
{ id: 1 },
{ id: 2 },
{ id: 3 },
{ id: 4 },
{ id: 5 },
{ id: 1 },
{ id: 7 },
{ id: 1 },
{ id: 7 },
{ id: 8 },
{ id: 1 },
];
const uniqueById = uniqueBy(i => i.id);
console.log(uniqueById(data));

Mongoose/Mongo: Update Not Saving

I'm extremely perplexed by this issue that I'm having with mongo/mongoose. I'm essentially trying to get an array of products, delete a certain product from the array, and then update the shopping chart with the new array that omits the selected product. Here's the snippet of code I'm dealing with:
const remove = (req, res, next) => {
console.log('here is the product id ' + req.body.cart.product)
delete req.body._owner // disallow owner reassignment.
Cart.find({_id: req.user.cartId})
.then((products1) => {
console.log("array of products: " + products1[0].product)
const index = products1[0].product.indexOf(req.body.cart.product)
console.log("index valeu: " + index)
if (index > -1) {
products1[0].product.splice(index, 1)
return products1[0].product
}
return products1[0].product
})
.then((products2) => {
console.log('Second Promise Input: ' + products2)
Cart.update({_id: req.user.cartId}, {$set: {product: products2}})
})
.then(() => res.sendStatus(204))
.catch(next)
}
And here's the output from my server:
Server listening on port 4741
here is the product id 5952b57ea52d092b8d34c6b0
array of products: 5952b57ea52d092b8d34c6b0,5952b57ea52d092b8d34c6b0,5952b57ea52d092b8d34c6b0,5952b57ea52d092b8d34c6b0,5952b57ea52d092b8d34c6b0
index valeu: 0
Second Promise Input: 5952b57ea52d092b8d34c6b0,5952b57ea52d092b8d34c6b0,5952b57ea52d092b8d34c6b0,5952b57ea52d092b8d34c6b0
PATCH /carts-decrease/595b037e128cfd37e0c864d7 204 38.773 ms
According to my console.logs, I'm getting the array just the way I want it but it simply does not update the shopping cart with the new array. I've been staring at this code for far too long and I'd appreciate a second set of eyes on this. Thanks.
P.S. Ignore the fact that the product ids are all the same, its just a testing variable
Cart Schema:
'use strict'
const mongoose = require('mongoose')
const cartSchema = new mongoose.Schema({
product: {
type: Array,
required: false
},
owner: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
required: false
}
}, {
timestamps: true,
toJSON: {
virtuals: true,
transform: function (doc, ret, options) {
const userId = (options.user && options.user._id) || false
ret.editable = userId && userId.equals(doc._owner)
return ret
}
}
})
const Cart = mongoose.model('Cart', cartSchema)
module.exports = Cart
Product Schema:
'use strict'
const mongoose = require('mongoose')
const productSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
price: {
type: Number,
required: true
},
description: {
type: String,
required: true
}
}, {
toJSON: {
virtuals: true
}
})
const Product = mongoose.model('Product', productSchema)
module.exports = Product
Show request:
const show = (req, res) => {
const product = {}
product.array = []
// console.log(req.cart.product)
const promises = []
Promise.all(req.cart.product.map(function (id) {
return Product.find({_id: ObjectId(id)})
})).then(function (products) {
console.log(products)
req.cart.product = products
return res.json({
cart: req.cart.toJSON({virtuals: true, user: req.user})
})
}).catch(function (err) {
console.log(err)
return res.sendStatus(500)
})
}
I would recommend you to slightly modify your cartSchema and store products in the form of an array of embedded documents:
const cartSchema = new mongoose.Schema({
products: [{
name: { type: String },
price: { type: Number }
...
}]
...
});
If you do this you can simply use the $pull update operator to remove products from your cart:
{ $pull: { <field1>: <value|condition>, <field2>: <value|condition>, ... } }
In your case the query should then look like this:
Cart.update(
{ _id: req.user.cartId },
{ $pull: { products: { '_id': req.body.cart.product } }}
);
As the embedded documents will have their own ObjectId there will only be one document matching the query.

Resources