How to authenticate and send contract method using web3.js 1.0 - javascript

I am confused about how I should be executing a contract's method using the web3 1.0 library.
This code works (so long as I manually unlock the account first):
var contract = new web3.eth.Contract(contractJson, contractAddress);
contract.methods
.transfer("0x0e0479bC23a96F6d701D003c5F004Bb0f28e773C", 1000)
.send({
from: "0x2EBd0A4729129b45b23aAd4656b98026cf67650A"
})
.on('confirmation', (confirmationNumber, receipt) => {
io.emit('confirmation', confirmationNumber);
});
I get this error (if I don't unlock manually first):
Returned error: authentication needed: password or unlock
The above code is an API endpoint in node.js, so I want it to unlock or authenticate programmatically.
There is no method in web3.js 1.0 to unlock the account.
I also don't think this is necessary (at least that's what I am confused about). Since I am managing accounts, I know what the private key is.
I am thinking the transaction needs to be signed with the private key?? Is this correct? Is this effectively the same thing as "unlocking the account"?
I tried doing this:
var contract = new web3.eth.Contract(contractJson, contractAddress);
var tx = {
from: "...{fromAddress -- address that has the private key below}",
to: "...",
value: ...
};
var signed = web3.eth.accounts.signTransaction(tx,
"...{privateKey}");
console.log(signed);
var promise = web3.eth.sendSignedTransaction(signed);
I get this error:
Returned error: The method net_version does not exist/is not available
What is the easiest way to authenticate and submit a transaction?
Ideally, I want to use the first approach in my code sample, as it is the cleanest.

This code allows me to sign a transaction server-side (node.js) using the privateKey from the account I created (using web3.eth.accounts.create()), and send the signed transaction to the network without having to unlock the account.
I am using Geth 1.7.1
var contract = new web3.eth.Contract(contractJson, contractAddress);
var transfer = contract.methods.transfer("0x...", 490);
var encodedABI = transfer.encodeABI();
var tx = {
from: "0x...",
to: contractAddress,
gas: 2000000,
data: encodedABI
};
web3.eth.accounts.signTransaction(tx, privateKey).then(signed => {
var tran = web3.eth.sendSignedTransaction(signed.rawTransaction);
tran.on('confirmation', (confirmationNumber, receipt) => {
console.log('confirmation: ' + confirmationNumber);
});
tran.on('transactionHash', hash => {
console.log('hash');
console.log(hash);
});
tran.on('receipt', receipt => {
console.log('reciept');
console.log(receipt);
});
tran.on('error', console.error);
});

A way to be able to call your contract methods without having to sign the transaction explicitly is this (web3js 1.0.0):
const privateKey = 'e0f3440344e4814d0dea8a65c1b9c488bab4295571c72fb879f5c29c8c861937';
const account = web3.eth.accounts.privateKeyToAccount('0x' + privateKey);
web3.eth.accounts.wallet.add(account);
web3.eth.defaultAccount = account.address;
// ...
contract = new web3.eth.Contract(JSON_INTERFACE, address);
contract.methods.myMethod(myParam1, myParam2)
.send({
from: this.web3.eth.defaultAccount,
gas: myConfig.gas,
gasPrice: myConfig.gasPrice
})

Here's a complete example of how to sign a transaction without a local wallet account. Especially useful if you are using infura for the transaction. This was written for
'use strict';
const Web3 = require('web3');
const wsAddress = 'wss://rinkeby.infura.io/ws';
const contractJson = '(taken from solc or remix online compiler)';
const privateKey = '0xOOOX';
const contractAddress = '0xOOOX';
const walletAddress = '0xOOOX';
const webSocketProvider = new Web3.providers.WebsocketProvider(wsAddress);
const web3 = new Web3(new Web3.providers.WebsocketProvider(webSocketProvider));
const contract = new web3.eth.Contract(
JSON.parse(contractJson),
contractAddress
);
// change this to whatever contract method you are trying to call, E.G. SimpleStore("Hello World")
const query = contract.methods.SimpleStore('Hello World');
const encodedABI = query.encodeABI();
const tx = {
from: walletAddress,
to: contractAddress,
gas: 2000000,
data: encodedABI,
};
const account = web3.eth.accounts.privateKeyToAccount(privateKey);
console.log(account);
web3.eth.getBalance(walletAddress).then(console.log);
web3.eth.accounts.signTransaction(tx, privateKey).then(signed => {
const tran = web3.eth
.sendSignedTransaction(signed.rawTransaction)
.on('confirmation', (confirmationNumber, receipt) => {
console.log('=> confirmation: ' + confirmationNumber);
})
.on('transactionHash', hash => {
console.log('=> hash');
console.log(hash);
})
.on('receipt', receipt => {
console.log('=> reciept');
console.log(receipt);
})
.on('error', console.error);
});
Using
"web3": "1.0.0-beta.30"

This is my implementation using "#truffle/hdwallet-provider": "^2.0.3", "web3": "^1.6.1",
function getWeb3Provider() {
return new HDWalletProvider({
privateKeys: [NFT_MINTER_ACCOUNT_PRIVATE_KEY],
providerOrUrl: BSC_RPC_ENDPOINT,
});
}
const web3 = new Web3(BSC_RPC_ENDPOINT);
const contract = new web3.eth.Contract(
jsonContractABI as unknown as AbiItem[],
NFT_CONTRACT_ADDRESS
);
contract.setProvider(getWeb3Provider());
then in send methods
contract.methods.safeMint(receiverAddress, itemUri).send({
from: NFT_MINTER_ACCOUNT,
});
in call methods
contract.methods.balanceOf(address).call();

Related

Sending a custom token with web3 + metamask

I'm trying to send USDT (custom token) with the metamask API using web3. I'm developing on ReactJS and I could detected correctly the user account from metamask.
My code:
const web3 = new Web3(window.web3.currentProvider);
const contractInstance = new web3.eth.Contract(abiUSDT, addressUSDT);
const amount = 200;
const tx = {
from: 'PERSON_SENDER',
to: contractInstance._address,
data: contractInstance.methods.transfer('PERSON_RECIPIENT', web3.utils.toWei( amount.toString() ) ).encodeABI(),
}
web3.eth.sendTransaction(tx).then(res => {
console.log("res",res)
}).catch(err => {
console.log("err",err)
});
obviously, I replace the fields called: PERSON_SENDER and PERSON_RECIPIENT with:
PERSON_SENDER: The user account
PERSON_RECIPIENT: My personal account (I want to deposit in this account)
Using this config, the transaction doesn't send to my account when I check in etherscan (and the amount was sent to the address of the contract), and a user communicate that if he uses real values for him real account, metamask doesn't touch his amount.
I made the same code and it works. But check the correct ABI, and adrresses format. The address "FROM" must be tha same address that your metamask is conected.
My code:
var contractABI = [{"YOUR TOKEN ABI HERE"}]
addressUSDT = "0xf80e1C5e28226cAfc7B0Ee6729E553ef54A7774F"; //IT'S OTHER CONTRACT
const web3 = new Web3(window.web3.currentProvider);
const contractInstance = new web3.eth.Contract(contractABI, addressUSDT);
const amount = 200;
const tx = {
from: '0x2acd4F50ee814d5092d9FC892d2BDEab91f5594d',
to: contractInstance._address,
data: contractInstance.methods.transfer('0xe54356F4e1aD4215973E7180BeBb4644a07DF508', web3.utils.toWei( amount.toString() ) ).encodeABI(),
}
web3.eth.sendTransaction(tx).then(res => {
console.log("res",res)
}).catch(err => {
console.log("err",err)
});

Using Uniswap SDK gives me this error: resolver or addr is not configured for ENS name

I am trying to swap ETH for DAI tokens using the UniSwap SDK and javascript, but am getting the following error on running the script.
(node:10096) UnhandledPromiseRejectionWarning: Error: resolver or addr is not configured for ENS name (argument="name", value="", code=INVALID_ARGUMENT, version=contracts/5.0.5)
I have narrowed the error down to the uniswap.swapExactETHForTokens function but I still don't know how to fix it.
Full code: (Private keys are hidden from the code for obvious reasons)
const { ChainId, Fetcher, WETH, Route, Trade, TokenAmount, TradeType, Percent } = require('#uniswap/sdk');
const ethers = require('ethers');
const chainId = ChainId.MAINNET;
const tokenAddress = '0x6B175474E89094C44Da98b954EedeAC495271d0F';
const init = async () => {
const dai = await Fetcher.fetchTokenData(chainId, tokenAddress);
const weth = WETH[chainId];
const pair = await Fetcher.fetchPairData(dai, weth);
const route = new Route([pair], weth);
const trade = new Trade(route, new TokenAmount(weth, '1000000000000'), TradeType.EXACT_INPUT);
const slippageTolerance = new Percent('50', '10000');
const amountOutMin = trade.minimumAmountOut(slippageTolerance).raw;
const path = [weth.address, dai.address];
const to = '';
const deadline = Math.floor(Date.now() / 1000) + 60 * 20;
const value = trade.inputAmount.raw;
const provider = ethers.getDefaultProvider('mainnet', {
infura: 'https://mainnet.infura.io/v3/ba14d1b3cfe5405088ee3c65ebd1d4'
});
const signer = new ethers.Wallet(PRIVATE_KEY);
const account = signer.connect(provider);
const uniswap = new ethers.Contract(
'0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D',
['function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline) external payable returns (uint[] memory amounts)'],
account
);
const tx = await uniswap.swapExactETHForTokens(
amountOutMin,
path,
to,
deadline,
{ value, gasPrice: 20e9 }
);
console.log(`Transaction hash: ${tx.hash}`);
const receipt = await tx.wait();
console.log(`Transaction was mined in block ${receipt.blockNumber}`);
}
init();
I guess you can replace
const to = ''
by:
const to = process.env.ACCOUNT
providing your account / wallet address to which the targetTokens shall be sent.
In my case, I had directly copy-pasted the router address from the uniswap documentation. So my variable looked something like this:
const routerAddress = "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D ";
Since there was a space at the end of the string, the ethers.js library confused it for ENS name instead of an address. So I corrected it to this:
const routerAddress = "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D";
Guess it was a silly mistake, but just watch out for it, in case nothing works!
For those who are facing an INVALID ARGUMENT error, here's what worked for my (using React):
Import router02 :
import UniswapV2Router02 from '#uniswap/v2-periphery/build/UniswapV2Router02.json';
hex function:
const toHex = (currencyAmount) => `0x${currencyAmount.raw.toString(16)}`;
const amountOutMin = toHex(trade.minimumAmountOut(slippageTolerance));
const value = toHex(trade.inputAmount);
connect to blockchain
const provider = ethers.getDefaultProvider('mainnet', {
infura: 'JUST_INFURA_NUMBER eg. xxxxxxxxxx'
});
Get contract and methods:
const abi = UniswapV2Router02['abi'];
const uniswapRouter = new ethers.Contract(
'0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D',
abi,
account); //if this doesnt work, try using provider/signer instead of account
console.log("uniswap contract: ", uniswapRouter);
const tx = await uniswapRouter.swapExactETHForTokens(
amountOutMin,
path,
to,
deadline,
{value, gasPrice: 20e9, gasLimit: 250000}
);
Tutorial code: https://www.youtube.com/watch?v=0Im5iaYoz1Y

How to build a readable stream in Node.js and TypeScript?

I connected the typescript function to Azure Blobstorage through Rest-API and this works fine for me. Now I want to get each blob contents and read the contents of each blobs.
I try this with this code here, but it returns an error:
const blobServiceClient = new BlobServiceClient(`https://${accountName}.blob.core.windows.net?${sasToken}`,
pipeline)
const containerClient = blobServiceClient.getContainerClient(containerName)
console.log(containerClient)
if (!containerClient.exists()) {
console.log("the container does not exit")
await containerClient.create()
}
const client = containerClient.getBlockBlobClient(this.currentFile.name)
//name of uploded blob
console.log(this.currentFile.name)
//metaata from the blob
console.log(client)
//List each blobs in the container
for await (const blob of containerClient.listBlobsFlat()) {
console.log('\t', blob.name);
const blockBlobClient = containerClient.getBlockBlobClient(blob.name);
const downloadBlockBlobResponse = await blockBlobClient.download(0);
console.log('\nDownloaded blob content...');
console.log('\t', await streamToString(downloadBlockBlobResponse.readableStreamBody));
//end of loop
}
async function streamToString(readableStream) {
return new Promise((resolve, reject) => {
const chunks = [];
readableStream.on("data", (data) => {
chunks.push(data.toString());
});
readableStream.on("end", () => {
resolve(chunks.join(""));
});
readableStream.on("error", reject);
});
}
The error is :
ERROR Error: Uncaught (in promise): TypeError: Cannot read property 'on' of undefined TypeError: Cannot read property 'on' of undefined
So how to solve the problem?
Thanks
Download the official sample code.
It runs normally on my side. Check if your local lack of dependencies, or the permissions in the storage need to be set.
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
/*
Setup: Enter your storage account name and shared key in main()
*/
import {
BlobServiceClient,
StorageSharedKeyCredential,
BlobDownloadResponseModel
} from "#azure/storage-blob";
// Load the .env file if it exists
import * as dotenv from "dotenv";
dotenv.config();
export async function main() {
// Enter your storage account name and shared key
const account = process.env.ACCOUNT_NAME || "pans*****age";
const accountKey = process.env.ACCOUNT_KEY || "IHa48xxo+0anyKQ2GzQ2K*******ZBxgJ0VotCpGs/PMftkebb9UFqyg==";
// Use StorageSharedKeyCredential with storage account and account key
// StorageSharedKeyCredential is only available in Node.js runtime, not in browsers
const sharedKeyCredential = new StorageSharedKeyCredential(account, accountKey);
// ONLY AVAILABLE IN NODE.JS RUNTIME
// DefaultAzureCredential will first look for Azure Active Directory (AAD)
// client secret credentials in the following environment variables:
//
// - AZURE_TENANT_ID: The ID of your AAD tenant
// - AZURE_CLIENT_ID: The ID of your AAD app registration (client)
// - AZURE_CLIENT_SECRET: The client secret for your AAD app registration
//
// If those environment variables aren't found and your application is deployed
// to an Azure VM or App Service instance, the managed service identity endpoint
// will be used as a fallback authentication source.
// const defaultAzureCredential = new DefaultAzureCredential();
// You can find more TokenCredential implementations in the [#azure/identity](https://www.npmjs.com/package/#azure/identity) library
// to use client secrets, certificates, or managed identities for authentication.
// Use AnonymousCredential when url already includes a SAS signature
// const anonymousCredential = new AnonymousCredential();
// List containers
const blobServiceClient = new BlobServiceClient(
// When using AnonymousCredential, following url should include a valid SAS or support public access
`https://${account}.blob.core.windows.net`,
sharedKeyCredential
);
let i = 1;
for await (const container of blobServiceClient.listContainers()) {
console.log(`Container ${i++}: ${container.name}`);
}
// Create a container
const containerName = `newcontainer${new Date().getTime()}`;
const containerClient = blobServiceClient.getContainerClient(containerName);
const createContainerResponse = await containerClient.create();
console.log(`Create container ${containerName} successfully`, createContainerResponse.requestId);
// Create a blob
const content = "hello, 你好";
const blobName = "newblob" + new Date().getTime();
const blockBlobClient = containerClient.getBlockBlobClient(blobName);
const uploadBlobResponse = await blockBlobClient.upload(content, Buffer.byteLength(content));
console.log(`Upload block blob ${blobName} successfully`, uploadBlobResponse.requestId);
// List blobs
i = 1;
for await (const blob of containerClient.listBlobsFlat()) {
console.log(`Blob ${i++}: ${blob.name}`);
}
// Get blob content from position 0 to the end
// In Node.js, get downloaded data by accessing downloadBlockBlobResponse.readableStreamBody
// In browsers, get downloaded data by accessing downloadBlockBlobResponse.blobBody
const downloadBlockBlobResponse: BlobDownloadResponseModel = await blockBlobClient.download(0);
console.log(
"Downloaded blob content",
await streamToString(downloadBlockBlobResponse.readableStreamBody!)
);
// Delete container
await containerClient.delete();
console.log("deleted container");
}
// A helper method used to read a Node.js readable stream into string
async function streamToString(readableStream: NodeJS.ReadableStream) {
return new Promise((resolve, reject) => {
const chunks: string[] = [];
readableStream.on("data", (data) => {
chunks.push(data.toString());
});
readableStream.on("end", () => {
resolve(chunks.join(""));
});
readableStream.on("error", reject);
});
}
main().catch((err) => {
console.error("Error running sample:", err.message);
});

By using ledger nano s, I wanna sign a transaction and send it

I'm trying to send ethereum transaction that sends ERC20 tokens to someone with Ledger Nano S through Node.JS but I'm not able to successfully sign and send this transaction.
First of all, I signed the transaction through the method, signTransaction, of ledgerhq API and then after signing it, I sended it to the main net by using sendSignedTransaction. When I execute below code, Ledger receives request and shows details of a transaction. However, after pressing Ledger's confirm button, the console returns error 'Returned error: Invalid signature: Crypto error (Invalid EC signature)'.
import AppEth from "#ledgerhq/hw-app-eth";
import TransportU2F from "#ledgerhq/hw-transport-u2f";
import TransportNodeHid from "#ledgerhq/hw-transport-node-hid";
import EthereumTx from "ethereumjs-tx"
const Web3 = require('web3');
import { addHexPrefix, bufferToHex, toBuffer } from 'ethereumjs-util';
const web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8545'));
var destAddresses = ['0xa6acFa18468786473269Dc1521fd4ff40F6481D9'];
var amount = 1000000000000;
var i=0;
var contract = new web3.eth.Contract([token contract ABI... ], '0x74a...');
const data1 = contract.methods.transfer(destAddresses[0], amount).encodeABI();
const exParams = {
gasLimit: 6e6,
gasPrice: 3e9,
from: '0x1A...',
data : data1,
to: '0x74a...',
value: '0x00',
nonce: "0x0",
chainId: 1,
v: "0x01",
r: "0x00",
s: "0x00"
}
async function makeSign(txParams) {
const tx = new EthereumTx(txParams);
const txHex = tx.serialize().toString("hex");
const signedTransaction = '0x' + txHex;
let transport;
try {
transport = await TransportNodeHid.create();
let eth2 = new AppEth(transport);
const result = await eth2.signTransaction("m/44'/60'/0'/0", txHex).then(result => {
web3.eth.sendSignedTransaction('0x' + txHex)
.then(res => {
console.log(res);
}).catch(err => {
console.log('sendSignedTransaction');
console.log(err);
});
}).catch(err => {
console.log('signTransaction');
console.log(err);
});
txParams.r = `0x${result.r, 'hex'}`;
txParams.s = `0x${result.s, 'hex'}`;
txParams.v = `0x${result.v, 'hex'}`;
return result;
} catch (e) {
console.log(e);
}
}
makeSign(exParams).then(function () {
console.log("Promise Resolved2");
}.catch(function () {
console.log("Promise Rejected2");
});
When I only use signTransaction function, I can confirm the transaction in the ledger device and return txhash on the console. However, ultimately I want to broadcast a transaction to the main net. Could you please give me any idea? I want any feedback. Also, if there are any examples of creating and broadcasting a raw transaction by using the ledger, notice me please.
Your code already sends the transaction to the network. However, just awaiting the "send" promise only gives you the transaction hash, not the receipt. You need to treat it as an event emitter and wait for the 'confirmation' event.
const serializedTx = tx.serialize();
web3.eth.sendSignedTransaction(serializedTx.toString('hex'))
.once('transactionHash', hash => console.log('Tx hash', hash))
.on('confirmation', (confNumber, receipt) => {
console.log(`Confirmation #${confNumber}`, receipt);
})
.on('error', console.error);
To send it to mainnet as you mention, you can either run a local geth node on port 8545 and use your code unchanged, or point web3 at infura or similar.

Calling a Firebase Cloud Function 'ForEach' child of a Snapshot

I'm trying to deploy a Firebase Cloud Function that sends a text message to its associated recipient for x number of text messages. The function is triggered in my iOS app when an update is made to the 'send' Realtime Database reference, indicating that the user has pressed the 'send' button.
My Firebase structure is
{
"user1uid": {
"send": false
"messagesToSend": {
"messageuid1": {
"messageText": "What's for dinner?",
"recipientNumber": "+18017378888",
}
"messageuid2:
"messageText": "Who won the Cowboys game?",
"recipientNumber": "+18017377787",
}
}
"user2uid": {
"send": false
"messagesToSend": {
"messageuid1": {
"messageText": "What's for dinner?",
"recipientNumber": "+18017378888",
}
"messageuid2:
"messageText": "Who won the Cowboys game?",
"recipientNumber": "+18017377787",
}
}
}
My code currently only sends one message, and I'm not sure how I can properly iterate through the messagesToSend node for each user and send all the messages in it.
I've been trying to follow the tutorial located here. I have looked at the following Stack Overflow responses but am unable to decipher or derive a solution from them:
Firebase cloud function promises
Am I using ForEach correctly?
My index.js code that sends one message is as follows:
const functions = require('firebase-functions');
// The Firebase Admin SDK to access the Firebase Realtime Database.
const admin = require('firebase-admin');
admin.initializeApp();
const twilio = require('twilio')
const accountSid = functions.config().twilio.sid;
const authToken = functions.config().twilio.token;
const client = new twilio(accountSid, authToken);
const twilioNumber = functions.config().twilio.number;
// Start cloud function
exports.sendSecrets = functions.database
.ref('/{uid}/send')
.onUpdate((change,context) => {
const uid = context.params.uid;
return admin.database().ref(uid+'/messagesToSend').once('value').then(snapshot => {
snapshot.forEach(function(childSnapshot) {
var key = childSnapshot.key;
var messageData = childSnapshot.val();
**if (messageData.sanitized) return true;**
var message = messageData.messageText;
var phoneNumber = messageData.recipientNumber;
const textMessage = {
body: `From My App - ${message}`,
from: twilioNumber, // From Twilio number
to: phoneNumber // Text to this number
}
return client.messages.create(textMessage)
})
**return snapshot.ref.toString();**
});
});
Please note that the lines marked with ** at either end indicate that I know I need to return something based on error messages I received indicating that 'Each then() should return a value or throw'.
I make the assumption that you are using the twilio-node library that use promises: https://www.npmjs.com/package/twilio.
Since you want to send several messages in parallel, you have to use Promise.all(), as follows:
const functions = require('firebase-functions');
// The Firebase Admin SDK to access the Firebase Realtime Database.
const admin = require('firebase-admin');
admin.initializeApp();
const twilio = require('twilio')
const accountSid = functions.config().twilio.sid;
const authToken = functions.config().twilio.token;
const client = new twilio(accountSid, authToken);
const twilioNumber = functions.config().twilio.number;
// Start cloud function
exports.sendSecrets = functions.database
.ref('/{uid}/send')
.onUpdate((change,context) => {
const uid = context.params.uid;
return admin.database().ref(uid+'/messagesToSend').once('value')
.then(snapshot => {
const promises = [];
snapshot.forEach(function(childSnapshot) {
var key = childSnapshot.key;
var messageData = childSnapshot.val();
//**if (messageData.sanitized) return true;**
var message = messageData.messageText;
var phoneNumber = messageData.recipientNumber;
const textMessage = {
body: `From My App - ${message}`,
from: twilioNumber, // From Twilio number
to: phoneNumber // Text to this number
}
promises.push(client.messages.create(textMessage));
})
return Promise.all(promises);
})
// Edits made below to parentheses/brackets
.then(results => {
//Do whatever you want !!
// e.g. print the results which will be an array of messages
// (see https://www.twilio.com/docs/libraries/node#testing-your-installation)
})
});
You can also simply return Promise.all() as follows:
....
return Promise.all(promises);
})
});

Categories

Resources