How to add account to web3 object? - solidity

i have private key and this is how i access account (Binance Smart Chain network):
const web3 = new Web3('https://bsc-dataseed1.binance.org:443')
const account = await web3.eth.accounts.privateKeyToAccount(pk)
So, i have account object,
{ address: '0x...', privateKey: '0x...', signTransaction: [Function: signTransaction], sign: [Function: sign], encrypt: [Function: encrypt] }
I want to send() method on BEP-20 token address:
const contract = new web3.eth.Contract(ABI, address)
const tx = await contract.methods.transfer(address, amount).send({
from: account.address
})
But i am getting error Error: Returned error: unknown account
Do i have to sign each transaction and then send it?
Maybe there is a way when provider signs transaction for me?
How to do it? How to add account object to web3.eth.accounts ?

In here:
const tx = await contract.methods.transfer(address, amount).send({
from: account.address
})
You are essentially asking the Ethereum node that you're communicating with, to sign the transaction for you.
In order for this to work, you first need to unlock the specified account on that node, by sending an appropriate request.
Alternatively (and probably more securely), you can sign the transaction yourself:
async function signAndSend(web3, account, gasPrice, transaction, value = 0) {
while (true) {
try {
const options = {
to : transaction._parent._address,
data : transaction.encodeABI(),
gas : await transaction.estimateGas({from: account.address, value: value}),
gasPrice: gasPrice,
value : value,
};
const signed = await web3.eth.accounts.signTransaction(options, account.privateKey);
const receipt = await web3.eth.sendSignedTransaction(signed.rawTransaction);
return receipt;
}
catch (error) {
console.log(error.message);
}
}
}
async function yourFunc(web3, account, gasPrice, address, amount) {
const contract = new web3.eth.Contract(ABI, address)
const receipt = await signAndSend(web3, account, gasPrice, contract.methods.transfer(address, amount));
}
BTW, I find it quite odd that you are trying to transfer tokens from your account into the Token contract.

Related

Re-send a transaction using Alchemy to speed up erc20 transaction

I am building a crypto wallet app on the ethereum chain and have set it up so that I can send transactions. Next I would like to be able to speed up transactions and understand how this is done. As far as I know you send another transaction with the same nonce but more gas. To sped up a transaction where I am sending ETH this is straightforward.
Here is the original function to send the transaction:
export const sendTransaction = async (currentWallet, toAddress, value) => {
// // Create Provider
const url = "https://eth-mainnet.g.alchemy.com/v2/" + ALCHEMY_API_KEY;
const customHttpProvider = await new ethers.providers.JsonRpcProvider(url);
// // Create wallet and connect to provider
const wallet = await new Wallet(currentWallet.pk, customHttpProvider);
const currentChainId = await getChainId();
const feeData = await customHttpProvider.getFeeData();
let gasPrice = feeData.maxFeePerGas;
const transaction = {
to: toAddress,
value: Utils.parseEther(value),
gasLimit: "21000",
maxPriorityFeePerGas: feeData.maxPriorityFeePerGas,
maxFeePerGas: feeData.maxFeePerGas,
nonce: await alchemy.core.getTransactionCount(currentWallet.walletInstance.address),
type: 2,
chainId: currentChainId
// chainId: 5
}
const rawTransaction = await wallet.signTransaction(transaction);
try {
const sentTransaction = await alchemy.core.sendTransaction(rawTransaction);
return sentTransaction;
} catch (err) {
return null;
}
}
and then to speed this up I would run the same function but replace the gas and set the same nonce.
However, with erc20 tokens my function to transfer the amount specified like so:
export const sendErc20Transaction = async (token, currentWallet, toAddress, value) => {
try {
// Create Provider
const url = "https://eth-mainnet.g.alchemy.com/v2/" + ALCHEMY_API_KEY;
const customHttpProvider = await new ethers.providers.JsonRpcProvider(url);
// Create wallet and connect to provider
const wallet = await new Wallet(currentWallet.pk, customHttpProvider);
// Dynamically get ABI
const abi = await getTokenABI(token, 'erc20');
// Create Smart Contract
const currentContract = new Contract(token.contractAddress, abi, wallet);
// // Send Transaction
const transaction = await currentContract.transfer(toAddress, Utils.parseUnits(value));
return transaction;
} catch (err) {
return null;
}
}
I don't see anywhere within this function where I can specify the gas or the nonce.
How would I resend this same transaction, still sending the same token, with higher gas and the same nonce?

Firebase updatePassword removes sign_in_second_factor phone property from token

I do a reautenticate with a user whom is already logged in as a Multifactor user
async reauthenticate(oldPassword: string): Promise<SignInEmailPassword> {
const user = firebase.auth().currentUser;
try {
if (user?.email) {
const cred = firebase.auth.EmailAuthProvider.credential(user.email, oldPassword);
await user.reauthenticateWithCredential(cred);
}
return { exception: '', token: '' };
} catch (reason) {
let phoneNumber = '****';
if ((reason as any).code === 'auth/multi-factor-auth-required') {
// The user is enrolled in MFA, must be verified
this.mfaResolver = (reason as any).resolver;
phoneNumber = (reason as any).resolver.hints[0].phoneNumber;
}
return { exception: 'auth/multi-factor-auth-required', phoneNumber };
}
}
I do the phone verification like
const phoneAuthProvider = new firebase.auth.PhoneAuthProvider();
const phoneOpts = {
multiFactorHint: this.mfaResolver.hints[0],
session: this.mfaResolver.session,
};
try {
this.verificationId = await phoneAuthProvider.verifyPhoneNumber(phoneOpts, recaptcha);
All good so far ( the recaptcha works with some other code, not mentioned here )
Then the actual SMS is verified, like:
const cred = firebase.auth.PhoneAuthProvider.credential(this.verificationId, phoneCode);
const multiFactorAssertion = firebase.auth.PhoneMultiFactorGenerator.assertion(cred);
const user = firebase.auth().currentUser;
try {
if (this.mfaResolver) {
await this.mfaResolver.resolveSignIn(multiFactorAssertion);
}
all good, and then finally
I can update the password with
const user = firebase.app().auth().currentUser;
if (user) {
await user.updatePassword(password);
}
If I console.log the token JUST before the updatePassword, I get my old token, with the
"sign_in_second_factor": "phone" property, but the token AFTER the updatePassword suddenly is without the sign_in_second_factor property, so basically it broke the token.
My solution is now to log out, and force the user to log back in ( again with MFA ), but an unnecessary step.
Is this avoidable,
to me it looks like a firebase bug, as it generates a valid token, WITHOUT a sign_in_second_factor being present, while it is a MFA firebase user.

Ethers.js, send money to a smart contract (receive function)

I have a smart contract with a receive function :
receive() external payable {
Wallets[msg.sender] += msg.value;
}
I have a front end and I want to send Ethers to this smart contract using the receive() function.
async function transfer() {
if(typeof window.ethereum !== 'undefined') {
const accounts = await window.ethereum.request({ method: 'eth_requestAccounts' });
const provider = new ethers.providers.Web3Provider(window.ethereum);
const signer = provider.getSigner();
const contract = new ethers.Contract(WalletAddress, Wallet.abi, signer);
const transaction = await contract.send({
from: accounts[0],
value: amount
})
await transaction.wait();
alert('ok');
setAmount('');
getBalance();
}
}
Saldy, there is no "send" function, what is the function I need to use there ?
Thx a lot !
When you want to invoke the receive() function, you need to send a transaction to the contract address with empty data field. The same way as if you were sending ETH to a non-contract address.
// not defining `data` field will use the default value - empty data
const transaction = signer.sendTransaction({
from: accounts[0],
to: WalletAddress,
value: amount
});

Transfer trc20 usdt from one address to another

I want to build a gateway for usdt payments and for this i'm using Tronweb tronWeb.utils.accounts.generateAccount() method
I have generated an address, transfered 1 usdt to it and now i want to transfer the funds to another address.
I get an error when sending the transaction:
error: 'CONTRACT_VALIDATE_ERROR',
message: 'contract validate error : account does not exist'
This is my code:
const TronWeb = require('tronweb');
const HttpProvider = TronWeb.providers.HttpProvider;
const fullNode = new HttpProvider("https://api.trongrid.io");
const solidityNode = new HttpProvider("https://api.trongrid.io");
const eventServer = new HttpProvider("https://api.trongrid.io");
const privateKey = "c83f36ae2e8661170e798ca73181693b76d75af016666e6f6baad92f69cfa1e2";
const tronWeb = new TronWeb(fullNode, solidityNode, eventServer, privateKey);
const trc20ContractAddress = "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t";//contract address
const addressTo = "TYcDSZor5ZgTsVMCZe1czfPEu8kzn6qe7L";
async function transfer() {
try {
const ownerAddress = tronWeb.address.fromPrivateKey(privateKey);
const contractAddressHex = tronWeb.address.toHex(trc20ContractAddress);
const contractInstance = await tronWeb.contract().at(contractAddressHex);
const decimals = await contractInstance.decimals().call();
const amount = 7 * Math.pow(10, decimals);
const response = await contractInstance.transfer(addressTo, amount).send();
console.log(response);
} catch (e) {
console.error(e);
return null;`enter code here`
}
}
transfer();
Is it something else i should do for getting this to work?
After generating new account, you need to activate it by transferring TRX/TRC-10 token to the address.
Transferring TRC20 will not activate an account. However, the balance can be inquired from Tronscan by the address.
TRON Account Documentation

How to persist CognitoUser during signIn with CUSTOM_AUTH authentication flow

My React Native app uses Amplify for a CUSTOM_AUTH authentication flow. The user receives a link via email to satisfy a challengeAnswer request. The process is like this:
User initiatiates sign in:
const cognitoUser = await Auth.signIn(username);
Email is sent to user via lambda.
User leaves app to retrieve email.
User clicks a link in the email which routes user back to the app via the RN Linking api.
The code from the link is processed with:
await Auth.sendCustomChallengeAnswer(
cognitoUser,
authChallengeAnswer
);
Usually this works well, but there is no guarantee that the cognitoUser object will exist after the app has been backgrounded while the user retrieves the email. There is a non-zero chance that iOS could dump the app during this time, and the cognitoUser var would be gone forcing the user to restart the sign in process. I'm looking for a way to persist the cognitoUser object somehow so if iOS decides the app needs to die this var can be retrieved from cache.
I'm able to cache the object into the Amplify cache (AsyncStorage) with
await Cache.setItem("cognitoUser", cognitoUser);
then fetch with
await Cache.getItem("cognitoUser");
which fails with
TypeError: user.sendCustomChallengeAnswer is not a function
because the process of caching it lost all its __proto__ functions. Its just retrieved as a basic object.
I suspect the cause is that I'm not using TypeScript, and the object loses some type information somehow.
Is there a better way of persisting this CognitoUser object so I can guarantee it exists after the user leaves/returns to the app as is needed in a CUSTOM_AUTH flow.
I use the following code to persist CognitoUser during sign in with CUSTOM_AUTH authentication flow:
import Auth from '#aws-amplify/auth'
import { CognitoUser } from 'amazon-cognito-identity-js'
const CUSTOM_AUTH_TTL = 5 * 60 * 1000 // Milliseconds
interface CustomAuthSession {
username: string
session: string
// Milliseconds after epoch
expiresAt: number
}
function clearCustomAuthSession() {
window.localStorage.removeItem('CustomAuthSession')
}
function loadCustomAuthSession(): CognitoUser {
const raw = window.localStorage.getItem('CustomAuthSession')
if (!raw) {
throw new Error('No custom auth session')
}
const storedSession: CustomAuthSession = window.JSON.parse(raw)
if (storedSession.expiresAt < window.Date.now()) {
clearCustomAuthSession()
throw new Error('Stored custom auth session has expired')
}
const username = storedSession.username
// Accessing private method of Auth here which is BAD, but it's still the
// safest way to restore the custom auth session from local storage, as there
// is no interface that lets us do it.
// (If we created a new user pool object here instead to pass to a
// CognitoUser constructor that would likely result in hard to catch bugs,
// as Auth can assume that all CognitoUsers passed to it come from its pool
// object.)
const user: CognitoUser = (Auth as any).createCognitoUser(username)
// Session is not exposed to TypeScript, but it's a public member in the
// JS code.
;(user as any).Session = storedSession.session
return user
}
function storeCustomAuthSession(cognitoUser: CognitoUser) {
// Session isn't exposed to TypeScript, but it's a public member in JS
const session = (cognitoUser as any).Session
const expiresAt = window.Date.now() + CUSTOM_AUTH_TTL
const otpSession: CustomAuthSession = {
session,
expiresAt,
username: cognitoUser.getUsername(),
}
const json = window.JSON.stringify(otpSession)
window.localStorage.setItem('CustomAuthSession', json)
}
You can reconstruct the CognitoUser object manually from your serialized object in localStorage or cache:
import { CognitoUser, CognitoUserPool } from 'amazon-cognito-identity-js';
const pool = new CognitoUserPool({
UserPoolId: cognitoObject.pool.userPoolId,
ClientId: cognitoObject.pool.clientId,
endpoint: cognitoObject.client.endpoint,
Storage: window.localStorage,
AdvancedSecurityDataCollectionFlag: cognitoObject.advancedSecurityDataCollectionFlag,
})
const cognitoUser = new CognitoUser({
Username: cognitoObject.username,
Pool: pool,
Storage: window.localStorage,
})
cognitoUser.Session = cognitoObject.Session
await Auth.completeNewPassword(cognitoUser, newPassword, cognitoObject.challengeParams)
Had to add the import statement myself, but got the general idea here: https://github.com/aws-amplify/amplify-js/issues/1715#issuecomment-800999983
I had this same issue and the simplest solution was to store it as a global variable within the slice.
authSlice.ts:
// we use this to temporarily store CognitoUser for MFA login.
// CognitoUser is not serializable so we cannot store it on Redux.
let cognitoUser = {};
export const doLogin = createAsyncThunk(
"auth/login",
async ({ email, password }: UserCredentials): Promise<Login | MFA> => {
const res = await Auth.signIn(email, password);
if (res.challengeName === "SOFTWARE_TOKEN_MFA") {
// we use this to temporarily store CognitoUser for MFA login.
// CognitoUser is not serializable so we cannot store it on Redux.
cognitoUser = res;
return {
status: "MFA",
user: null,
};
} else {
const user = await getUser();
return { user, status: "OK" };
}
}
);
export const confirmMFA = createAsyncThunk("auth/confirmMFA", async ({ mfa }: UserMFA) => {
if (!cognitoUser) {
throw new Error("Invalid flow?!");
}
await Auth.confirmSignIn(cognitoUser, mfa, "SOFTWARE_TOKEN_MFA");
const user = await getUser();
return { user, status: "OK" };
});
const getUser = async (): Promise<User> => {
const session = await Auth.currentSession();
// #ts-ignore https://github.com/aws-amplify/amplify-js/issues/4927
const { accessToken } = session;
if (!accessToken) {
throw new Error("Missing access token");
}
setCredentials(accessToken.jwtToken);
const user = await Auth.currentAuthenticatedUser();
return user.attributes;
};
Our requirement was also the same and we managed to get the customAuth flow working by creating cognitoUserPool and cognitoUser instance from localStorage/sessionStorage before calling sendCustomChallengeAnswer.
Example:
const userPoolData = {
Attributes: values(from localStorage);
}
const cognitoUserPool = new CognitoUserPool(userPoolData);
const userData = {
Attributes: values(from localStorage);
}
const cognitoUser = new CognitoUser(userData);
Auth.sendCustomChallengeAnswer(cognitoUser, validationCode);