Web3.eth.abi.encodeFunctionCall Is not working - javascript

I'm creating a dapp that charges users a specific amount of eth depending on their input.
Whenever I attempt to create the transaction, I specify the amount of Eth in Wei. It throws an Error with no description as to why it couldn't fulfill the transaction
Error: Error Minting New NFT
at MintNewNFT (Transactions.js:68)
at Object.onClick (index.js:62)
(Line 62 is the catch block)
AmountIn is 0.02166 ETH
Here is my code :
export const MintNewNFT = async (WalletABI,address, network, mediaID, amountIn) => {
try {
//adjust this to take an argument for media id
const web3 = new Web3('https://rinkeby.infura.io/v3/key');
const weiValue = Web3.utils.toWei(amountIn.toString(), 'ether');
console.log(weiValue , mediaID);
const transactionParameters = {
to: WalletABI._address, // Required except during contract publications.
from: address, // must match user's active address.
value: weiValue.toString(),
data: web3.eth.abi.encodeFunctionCall(
{
"inputs": [
{
"internalType": "bytes32",
"name": "mediaID",
"type": "bytes32"
}
],
"name": "mintNewNFT",
"outputs": [],
"stateMutability": "payable",
"type": "function",
"payable": true
},[mediaID]),
chainId: `0x${network}`, // Used to prevent transaction reuse across blockchains. Auto-filled by MetaMask.
};
// txHash is a hex string
// As with any RPC call, it may throw an error
await window.ethereum.request({
method: 'eth_sendTransaction',
params: [transactionParameters],
}).then((result) => {
// The result varies by by RPC method.
// For example, this method will return a transaction hash hexadecimal string on success.
console.log(`Transaction Result ${result}`)
})
.catch((error) => {
// If the request fails, the Promise will reject with an error.
console.log(`Transaction ERROR : ${error.message}`)
});
} catch (error) {
throw Error("Error Minting New NFT", error)
}
}
Any indication as to what I may be doing wrong would be very much appreciated

It was failing because I was using a bytes32 instead of a string for the argument. I don't know why this was a problem.

Related

web3js says method is not found in ERC20 contract

The Open Zeppelin ERC20 contract has a name(), symbol, and totalSupply() function. This is my smart contract that inherits the ERC20 contract
pragma solidity ^0.8.0;
import "#openzeppelin/contracts/token/ERC20/ERC20.sol";
import "#openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
contract QUAD_EK is ERC20, ERC20Burnable {
constructor(address[] memory wallets, uint256[] memory amounts) ERC20("Quadency Token", "QUAD") {
require(wallets.length == amounts.length, "QUAD: wallets and amounts mismatch");
for (uint256 i = 0; i < wallets.length; i++){
_mint(wallets[i], amounts[i]);
if(i == 10){
break;
}
}
}
I've tested that the name() function works in Truffle.
describe('quad ek methods', async () => {
it('gets token name', async () => {
let quadEK = await QUAD_EK.new([account_0, account_1, account_2, account_3, account_4], amounts);
let quadName = await quadEK.name();
assert.equal(quadName.toString(), 'Quadency Token')
})
})
But when I tried to call the name() method in web3, I get the following error: TypeError: quad_ek.methods.name is not a function
My code below (the contract is loaded correctly):
module.exports = async function(callback) {
try{
const web3 = new Web3(new Web3.providers.HttpProvider(
`process.env.INFURA)`
)
);
const signInfo = web3.eth.accounts.privateKeyToAccount('process.env.QUAD_TEST0')
console.log("signingInfo", signInfo)
const nonce = await web3.eth.getTransactionCount(signInfo.address)
console.log("nonce", nonce)
const quad_ek = new web3.eth.Contract(token_abi, token_address )
**
quad_ek.methods.name().call({from: '0x72707D86053cb767A710957a6b9D8b56Dd7Fd835'}, function(error, result){
});**
}
catch(error){
console.log(error)
}
callback()
}
I'm using the documentation from web3:
myContract.methods.myMethod(123).call({from: '0xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe'}, function(error, result){
...
});
My token abi does have the name function:
"name()": {
"details": "Returns the name of the token."
},
"symbol()": {
"details": "Returns the symbol of the token, usually a shorter version of the name."
},
"totalSupply()": {
"details": "See {IERC20-totalSupply}."
},
"transfer(address,uint256)": {
"details": "See {IERC20-transfer}. Requirements: - `to` cannot be the zero address. - the caller must have a balance of at least `amount`."
},
"transferFrom(address,address,uint256)": {
"details": "See {IERC20-transferFrom}. Emits an {Approval} event indicating the updated allowance. This is not required by the EIP. See the note at the beginning of {ERC20}. NOTE: Does not update the allowance if the current allowance is the maximum `uint256`. Requirements: - `from` and `to` cannot be the zero address. - `from` must have a balance of at least `amount`. - the caller must have allowance for ``from``'s tokens of at least `amount`."
}
In your post, you used the the full JSON file (or just its devDocs section, can't tell from the context) as the value of token_abi. This whole file is an output from the compilation process, it contains the ABI as well as other data produced by the compilator (bytecode, list of warnings, ...).
You need to pass just the abi section to new web3.eth.Contract();
const compileOutput = JSON.parse(fs.readFileSync("./QUAD_EK.JSON"));
const token_abi = compileOutput[0].abi;
const quad_ek = new web3.eth.Contract(token_abi, token_address);

TrustSet - Error: Invalid type to construct an Amount

I would like to set up a trust line to buy XRPL3DAPES, but I get the error "Error: Invalid type to construct an Amount".
I don't understand why this error is displayed.
I looked at the doc many times, but each time I have a different error.
This is my code:
const xrpl = require("xrpl")
const SERVER = "wss://xrplcluster.com/"
const WebSocket = require("ws")
const ws = new WebSocket(SERVER)
ws.on("close", (code, reason) => {
console.log("Disconnected. \nCode :", code, "\nReason: ", reason)
})
async function main() {
const xrpl3dapes = {
addressIssuerWallet: "rLBW9d9cfEY4ZFPbgqKzEpoEHjKeLrotWZ",
currency: "5852504C33444150455300000000000000000000"
}
let response;
const client = new xrpl.Client(SERVER)
await client.connect()
seed = "sXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
const wallet = xrpl.Wallet.fromSeed(seed)
try{
response = await client.submitAndWait(wallet.sign(
{
"TransactionType": "TrustSet",
"Account": wallet.address,
"Fee": base_fee,
"LimitAmount":
{
"currency": xrpl3dapes.currency,
"issuer": xrpl3dapes.addressIssuerWallet,
"value": "10000"
},
"sequence": 1
}).tx_blob)
}
catch (error){
console.log(error)
}
client.disconnect()
process.exit(1)
}
main()
Thanks for your help !
EDIT:
Base_fee must be a string. Else, we get this error.
tx_json = await client.autofill({
"TransactionType": "OfferCreate",
"Account": wallet.address,
"Fee": base_fee,
"Flags": 262144,
"TakerGets": takerGets,
"TakerPays": takerPays
})
response = await client.submitAndWait(wallet.sign(tx_json).tx_blob)
Your payload seems correct. Can you post couple of other error messages you are getting?
The variable/constant seed isn't declared in above code. But I'm sure it's not the issue, as you might have already fixed it after seeing the error message in the console. If you can post other error messages you are getting, maybe it can help me understand more.

Trust wallet dapp connect and sign transfer message

I connect trust wallet like this:
//**Connect wallet:**
import WalletConnect from "#walletconnect/client";
import QRCodeModal from "#walletconnect/qrcode-modal";
const connector = new WalletConnect({
bridge: "https://bridge.walletconnect.org", // Required
qrcodeModal: QRCodeModal,
});
document.onreadystatechange = () => {
// Create a connector
// Check if connection is already established
if (!connector.connected) {
// create new session
connector.createSession();
}
}
When wallet connected I tried to sign a transfer message to transfer coins (I've tried with binance chain and thorchain - not working)
This is example how I sign msg:
const network = 931; // thorchain(rune)
const tx = {
fee: {
amounts: [
{
denom: "rune",
amount: "0.01"
}
],
gas: "2000000"
},
memo:"test",
"messages":[{
"sendCoinsMessage": {
fromAddress: 'thor1mkda02h8hsevykxwnnxs93tgtvgtz5djxteat0',
toAddress: "thor1mkda02h8hsevykxwnnxs93tgtvgtz5djxteat0",
amounts: [
{
denom: "rune",
amount: "1"
}
],
}
}]//end
};
Then I format request and sign it:
const request = self.connector._formatRequest({
method: 'trust_signTransaction',
params: [
{
network,
transaction: JSON.stringify(tx),
},
],
});
connector
._sendCallRequest(request)
.then(result => {
// Returns transaction signed in json or encoded format
console.log(result);
})
.catch(error => {
// Error returned when rejected
console.log('error')
console.error(error);
});
},
This is what I see in my trust wallet:
As a response I get from console this:
{"mode":"block","tx":{"fee":{"amount":[],"gas":"0"},"memo":"","msg":[],"signatures":[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A2yB9NhfIeEwTEDbs0ssZQcqtL/OWGuHqooeFllERot3"},"signature":"+kO2W2MfcSBwgLUF3zJUQK4e01YvIGXK8juzojEkE/RrVgrZJPRsthweuto4FJ1QK/MjUWuGlJiC+MjktlBexA=="}]}}
But the transaction is not sent to blockchain (If I go to blockchain exlorer I will not find it)
Also as you can notice in the response from console fee and gas always 0
What do I do?
UPD I have also tried method trust_sendTransaction instead of trust_signTransaction but didn't help

AWS DynamoDB not returning response to Lambda function

I am working with serverless and NodeJS using the aws-sdk module, and I have been getting a 502 internal server error on the frontend of the app, previously it was shown as a cors error but so far it is the same issue in only one specific table when performing update or delete. Here is the Lambda function itself:
export async function createUserSegment(event, context, callback) {
const { data, timestamp, identityId } = extractRequestData(event)
console.log('data', JSON.stringify(data));
if (!verifyParamsExist(callback, ["Data", "IdentityId"], [data, identityId]) ||
!verifyParamsExist(callback, ["Name", "Pattern", "Type"], [data.name, data.type, data.pattern]) ||
!verifyValidParams(callback, [data.name, ...data.pattern], ["Name", "Pattern"], [{ value: data.type, enums: USER_SEGMENT_TYPES }], ["Type"])) {
console.log('no good params');
return false
}
let cleanedParams
let params
try {
cleanedParams = cleanUserSegment(data.pattern, data.type)
console.log('cleaned params', cleanedParams);
} catch (e) {
console.error(e)
callback(null, badRequest({ type: "InvalidRule", status: false, msg: e.message }));
return
}
params = {
TableName: process.env.USER_SEGMENT_TABLE,
Key: {
"cognitoUsername": fetchSubFromEvent(event),
"segmentName": data.name.trim(),
},
// Performs an insert (fails if already exists)
// - 'cognitoUsername': cognito username (NOT IDENTITY ID!) of the segment creator.
// - 'segmentName': user-provided name to identify the segment
// - 'createdAt': unix timestamp for when segment is created
// - 'pattern': user-supplied pattern that the segment refers to
// - 'segmentType': either VISITED, NOT_VISITED, GOAL, NOT_GOAL to indicate what our pattern is matching (URL visits or goal completion)
// - 'identityId': cognito identity id of the segment creator. necessary for querying ES
UpdateExpression: 'SET pattern = :pattern, segmentType = :segmentType, createdAt = :createdAt, identityId = :identityId',
ConditionExpression: 'attribute_not_exists(cognitoUsername)',
ExpressionAttributeValues: {
":pattern": cleanedParams.pattern,
":segmentType": cleanedParams.type,
":createdAt": timestamp,
":identityId": identityId,
},
ReturnValues: "ALL_NEW"
};
writeToDynamoAndCatchDuplicates(params, callback, transformUserSegment)
}
async function writeToDynamoAndCatchDuplicates(params, callback, transformFn){
try {
if(params.ConditionExpression){
console.log(params);
let result = await dynamoDbLib.call("update", params);
console.log('this is res',result);
result.Attributes = transformFn(result.Attributes)
callback(null, success({ status: true, result: result }));
} else {
await dynamoDbLib.call("update", params);
const paramsForAll = {
TableName: params.TableName,
KeyConditionExpression: "#cognitoUsername = :username",
ExpressionAttributeNames: {
"#cognitoUsername": "cognitoUsername",
},
ExpressionAttributeValues: {
":username": params.Key.cognitoUsername
},
}
try {
let result = await dynamoDbLib.call("query", paramsForAll);
result.Items = result.Items.map(transformFn)
console.log(result);
callback(null, success({ status: true, result: result}));
} catch (e) {
console.error(e)
callback(null, failure({ status: false }));
}
}
} catch (e) {
console.error(e)
if(e.code === 'ConditionalCheckFailedException'){
callback(null, badRequest({ type: "Duplicate", status: false }));
} else {
callback(null, failure({ status: false }));
}
}
}
The call function for dynamoDbLib is simply:
import AWS from "aws-sdk";
AWS.config.update({ region: "us-east-1" });
export function call(action, params) {
const dynamoDb = new AWS.DynamoDB.DocumentClient();
return dynamoDb[action](params).promise();
}
The request goes through the Lambda up until within writeToDynamoAndCatchDuplicates the call function is made. it gets sent to dynamo but it never returns, neither a timeout nor an error, it simply ends. In cloudwatch the last thing I see is the params log from writeToDynamoAndCatchDuplicates, no error logged. I hadn't changed the IAM policies on the role assigned to all Lambdas with the following permissions for the table aforementioned:
...},
{
"Action": [
"dynamodb:UpdateItem",
"dynamodb:Query",
"dynamodb:Scan",
"dynamodb:DeleteItem"
],
"Resource": [
"arn:aws:dynamodb:us-east-1:xxxxxxxxxxxxx:table/staging-userSegmentTable"
],
"Effect": "Allow"
},
{ ...
If anyone spots anything that might be a possible reason for the issue, it will be much appreciated.
Also here is the last log shown before the END message on LOG events:
2020-01-07T23:42:03.715Z 4fbf0957-c8be-4520-8277-7b9a9bda8b67 INFO { TableName: 'staging-userSegmentTable',
Key:
{ cognitoUsername: '*******************************',
segmentName: 'cssfs' },
UpdateExpression:
'SET pattern = :pattern, segmentType = :segmentType, createdAt = :createdAt, identityId = :identityId',
ConditionExpression: 'attribute_not_exists(cognitoUsername)',
ExpressionAttributeValues:
{ ':pattern': [ '/' ],
':segmentType': [ 'CONTAINS' ],
':createdAt': 1578440523709,
':identityId': '****************************************' },
ReturnValues: 'ALL_NEW' }
which corresponds to console.log(params) before sending to DynamoDB inside the if statement of the writeToDynamoAndCatchDuplicates function.
Does your IAM have Read and Write Access to that Database and security group connected to the instance? Is this the only table that seems to be the issue?
I had a similar issue ended up having to give my DB instance full read access.

How to handle error and send response in GraphQL

I was starting with GraphQL and I was unable to comprehend how we can throw errors in GraphQL
I went through a couple of articles on the web but almost all of them use Apollo and the code-structure looks very different than how I work.
Consider this piece of code, here where I am making a mutation, now how can send a response message with error and change headers status message in case of error?
AddNewPersonalInfo: {
type: userDashboardType,
args: {
parameter: {
type: userCreationlInputType
}
},
resolve: async (parent, args, context) => {
args.parameter.userId = context.req.headers.userId
//Check if user info already exsist
const checkIfUserInformationExsist = await getSelectedThingFromTable('CatsWork_personal', 'userId', `${userId}`)
if (checkIfUserInformationExsist[0]) {
const error = {
code: 403,
message: 'User info Already exsist'
}
throw new Error(error)
} else {
try {
const addLinkedinUser = await insertIntheTable('personal', payload)
return true
} catch (err) {
console.error(err)
throw new Error(err)
}
}
}
}
What I have faced in one of my projects, it is hard to set the status code of the response. So, I made some custom error response to identify correct statusCode using express-graphql
Below is the example (What I have used in one of my projects):
--------app.js file--------
const graphqlHTTP = require('express-graphql')
app.use('/graphql', (req, res) => {
graphqlHTTP({
schema: GraphQLSchema, //A GraphQLSchema instance from GraphQL.js. A schema must be provided.
graphiql: true,
context: { req },
formatError: (err) => {
const error = getErrorCode(err.message)
return ({ message: error.message, statusCode: error.statusCode })
}
})(req, res)
})
--------getErrorCode function implementation--------
const { errorType } = require('../constants')
const getErrorCode = errorName => {
return errorType[errorName]
}
module.exports = getErrorCode
--------Constant.js file--------
exports.errorName = {
USER_ALREADY_EXISTS: 'USER_ALREADY_EXISTS',
SERVER_ERROR: 'SERVER_ERROR'
}
exports.errorType = {
USER_ALREADY_EXISTS: {
message: 'User is already exists.',
statusCode: 403
},
SERVER_ERROR: {
message: 'Server error.',
statusCode: 500
}
}
Now, we are ready to use our setup.
From your query or mutation, you need to require constant file and return custom error:
const { errorName } = require('../constant')
AddNewPersonalInfo: {
type: userDashboardType,
args: {
parameter: {
type: userCreationlInputType
}
},
resolve: async (parent, args, context) => {
args.parameter.userId = context.req.headers.userId
//Check if user info already exsist
const checkIfUserInformationExsist = await getSelectedThingFromTable('CatsWork_personal', 'userId', `${userId}`)
if (checkIfUserInformationExsist[0]) {
const error = {
code: 403,
message: 'User info Already exsist'
}
throw new Error(errorName.USER_ALREADY_EXISTS) // Here you can use error from constatnt file
} else {
try {
const addLinkedinUser = await insertIntheTable('personal', payload)
return true
} catch (err) {
console.error(err)
throw new Error(errorName.SERVER_ERROR) // Here you can use error from constatnt file
}
}
}
}
--------Error response--------
{
error: [{
"statusCode": 403,
"message": "User is already exists."
}],
data: null
}
We just need to write custom error handling from FS side too.
Note:- formatError: is deprecated and replaced by customFormatErrorFn. It will be removed in version 1.0.0. You can refer customFormatErrorFn.
graphql should be an application level layer that shouldn't (see last paragraph why shouldn't and not doesn't) require http to work. Although in 99% of cases it runs on top of http, because of how convenient it is to do so, graphql is itself a layer 7 protocol.
What does that mean in your case? Well, it means you should not mix concepts from HTTP/REST with concepts from graphql and focus on the latter. The headers error code is a HTTP/REST concept, graphql sends errors in the errors field of the response and the nodejs implementation already catches all your errors and adds them to the list. The HTTP status will be always 200, and your clients shouldn't care and consume your graphql api and not a mix of REST with graphql.
That being said, there are couple of things that REST over HTTP does better. So people, including the developers of Apollo, kinda mixed concepts too, mainly because the graphql standard is not complete (aka, it doesn't have a standard/rule for solving all the problems you might encounter while building an API), so people improvised. I wouldn't recommend graphql yet for any serious project.
Reference
You can specify an error function inside graphqlHTTP like this:
app.use("/graphql", graphqlHTTP({
schema,
graphiql: true,
customFormatErrorFn: err => {
try {
err.details = JSON.parse(err.message);
err.message = Array.isArray(err.details.error) ? err.details.error.join(",") : err.details.error;
return err;
} catch {
return err;
}
}
}));
where err.message might contain a JSON object or a string.
you can use those function to generate specific client and server error functions:
const clientError = error => new Error(JSON.stringify({
success: false,
code: 400,
error
}));
const serverError = ({ name, message, stack }) => new Error(JSON.stringify({
success: false,
error: "Server Error",
code: 500,
name,
message,
stack
}));
const userValidationError = err => {
if (err.name === "ValidationError") return clientError(Object.values(err.errors).map(({ message }) => message));
return serverError(err);
}
module.exports = {
clientError,
serverError,
userValidationError
};
userValidationError function is useful if you have a mongodb validation error.
so that you would use it inside resolve function like this:
try {
const createdDocument = await MongooseDoc.create(data);
return createdDocument;
} catch (err) {
throw userValidationError(err);
}
the response would be
{
"errors": [
{
"message": "error details 1,error details 2",
"locations": [
{
"line": 2,
"column": 3
}
],
"path": [
"document"
],
"details": {
"success": false,
"code": 400,
"error": [
"error details 1",
"error details 2"
]
}
}
],
"data": {
"document": null
}
}
if you want to throw a clientError you throw it outside try catch.
Hopefully this code helps someone send dynamic error messages in graphql.

Categories