I want to automate my staking on The Sandbox. For that I need in the first step to interact with the mSand-Matic Pool Contract. It is this one: https://polygonscan.com/address/0x4ab071c42c28c4858c4bac171f06b13586b20f30#code
I have written a little program in a GitHub repository to show what I have done: https://github.com/ChristianErdtmann/mSandMaticStakingAutomation
Or here is the code example from the contract-interact.js
Web3 = require('web3')
const fs = require('fs');
const web3 = new Web3("https://polygon-rpc.com")
const contractAddress = "0x4AB071C42C28c4858C4BAc171F06b13586b20F30"
const contractJson = fs.readFileSync('./abi.json')
const abi = JSON.parse(contractJson)
const mSandMaticContract = new web3.eth.Contract(abi, contractAddress)
mSandMaticContract.balanceOf('0x7e5475290Df8E66234A541483453B5503551C780')
The ABI I have taken from the contract link on the top. But it seems there is a problem.
I tried for testing to read something from the contract. For that I used the function balanceOf(address), how you can see in the code.
But I always get this error:
TypeError: mSandMaticContract.balanceOf is not a function
I found the solution
web3 needs .methots to get acces to the balanceOf() function
if we want only to read so we need to add .call()
we need to add await before the function is called this needs to be in a asynchonus function
So the final working code is:
Web3 = require('web3')
const fs = require('fs');
const web3 = new Web3("https://polygon-rpc.com")
const contractAddress = "0x4AB071C42C28c4858C4BAc171F06b13586b20F30"
const contractJson = fs.readFileSync('./abi.json')
const abi = JSON.parse(contractJson)
const mSandMaticContract = new web3.eth.Contract(abi, contractAddress)
asyncCall()
async function asyncCall() {
console.log(await mSandMaticContract.methods.balanceOf('0x7e5475290Df8E66234A541483453B5503551C780').call())
}
If you dont want only to read you need addtional to sign the transaction with:
The solution is, to sign the transaction before sending we can doing this with any method by this code:
encoded = mSandMaticContract.methods.getReward().encodeABI()
var block = await web3.eth.getBlock("latest");
var gasLimit = Math.round(block.gasLimit / block.transactions.length);
var tx = {
gas: gasLimit,
to: publicKey,
data: encoded
}
web3.eth.accounts.signTransaction(tx, privateKey).then(signed => {
web3.eth.sendSignedTransaction(signed.rawTransaction).on('receipt', console.log)
})
I am new at ethereum development and I am working on a simple script to execute swaps using Pancakeswap. Everything seems to be working well up to the point where I am building my transaction and calculate the tx-cost. I am using the UniswapRouterV02 abi create my Pancakeswap contract. The code:
const init = async () => {
const [WBNB, BUSD] = await Promise.all(
[addresses.WBNB, addresses.BUSD].map(tokenAddress => (
new Token(
ChainId.MAINNET,
tokenAddress,
18
)
)));
const pair = await Fetcher.fetchPairData(WBNB, BUSD, provider)
const route = await new Route([pair], WBNB)
const trade = await new Trade(route, new TokenAmount(WBNB, tradeAmount), TradeType.EXACT_INPUT)
const executionPrice = trade.executionPrice.toSignificant(12)
// Correct prices; everything seems correct up until here
const slippageTolerance = new Percent('50', '10000')
const amountOutMin = trade.minimumAmountOut(slippageTolerance).raw
const path = [WBNB.address, BUSD.address]
const to = MY_ADDRESS
const deadline = Math.floor(Date.now() / 1000) + 60 * 20
const value = trade.inputAmount.raw
// Correct prices everything seems correct up until here
const pancakeSwap = new web3.eth.Contract(
abis.uniswapRouter.abi,
addresses.PANCAKE_ROUTER //'0x05fF2B0DB69458A0750badebc4f9e13aDd608C7F'
);
let tx = pancakeSwap.methods.swapExactTokensForTokens(
tradeAmount,
web3.utils.toBN(amountOutMin.toString()),
path,
to,
deadline
)
const [gasPrice, gasCost] = await Promise.all([
web3.eth.getGasPrice(),
tx.estimateGas({from: admin}),
]);
console.log(`gasPrice: ${gasPrice}`)
console.log(`gasCost: ${gasCost}`)
}
init()
The price calculation for the swap returns correct prices. However when I try to calculate the transaction costs the following error is thrown:
Error: Returned error: gas required exceeds allowance (44038122) or always failing transaction
Any help would be greatly appreciated, please let me know if more of my code should be clarified!
In turns out that with web3 it is not possible to interact with the Pancakeswap contract. I found a solution using ethers.js. Following code worked for me to execute a swap on Pancake on mainnet.
Before executing the first transaction Pancakeswap needs to be allowed, this code is commented out.
I had to play around with the gasprice and gasLimit a bit to make it work.
require("dotenv").config()
const ethers = require('ethers')
const {ChainId, Token, TokenAmount, Fetcher, Pair, Route, Trade, TradeType, Percent} =
require('#pancakeswap-libs/sdk');
const Web3 = require('web3');
const web3 = new Web3('wss://apis.ankr.com/wss/c40792ffe3514537be9fb4109b32d257/946dd909d324e5a6caa2b72ba75c5799/binance/full/main');
const {JsonRpcProvider} = require("#ethersproject/providers");
const provider = new JsonRpcProvider('https://bsc-dataseed1.binance.org/');
const { address: admin } = web3.eth.accounts.wallet.add(process.env.PRIVATE_KEY)
const addresses = {
WBNB: '0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c',
BUSD: '0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56',
PANCAKE_ROUTER: '0x05fF2B0DB69458A0750badebc4f9e13aDd608C7F'
}
const ONE_ETH_IN_WEI = web3.utils.toBN(web3.utils.toWei('1'))
const tradeAmount = ONE_ETH_IN_WEI.div(web3.utils.toBN('1000'))
const init = async () => {
const [WBNB, BUSD] = await Promise.all(
[addresses.WBNB, addresses.BUSD].map(tokenAddress => (
new Token(
ChainId.MAINNET,
tokenAddress,
18
)
)));
const pair = await Fetcher.fetchPairData(WBNB, BUSD, provider)
const route = await new Route([pair], WBNB)
const trade = await new Trade(route, new TokenAmount(WBNB, tradeAmount), TradeType.EXACT_INPUT)
const slippageTolerance = new Percent('50', '10000')
// create transaction parameters
const amountOutMin = trade.minimumAmountOut(slippageTolerance).raw
const path = [WBNB.address, BUSD.address]
const to = admin
const deadline = Math.floor(Date.now() / 1000) + 60 * 20
// Create signer
const wallet = new ethers.Wallet(
Buffer.from(
process.env.PRIVATE_KEY, // paste your private key from metamask here
"hex"
)
)
const signer = wallet.connect(provider)
// Create Pancakeswap ethers Contract
const pancakeswap = new ethers.Contract(
addresses.PANCAKE_ROUTER,
['function swapExactTokensForTokens(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline) external returns (uint[] memory amounts)'],
signer
)
// Allow Pancakeswap
// let abi = ["function approve(address _spender, uint256 _value) public returns (bool success)"]
// let contract = new ethers.Contract(WBNB.address, abi, signer)
// await contract.approve(addresses.PANCAKE_ROUTER, ethers.utils.parseUnits('1000.0', 18), {gasLimit: 100000, gasPrice: 5e9})
// Execute transaction
const tx = await pancakeswap.swapExactTokensForTokens(
ethers.utils.parseUnits('0.001', 18),
ethers.utils.parseUnits(web3.utils.fromWei(amountOutMin.toString()), 18),
path,
to,
deadline,
{ gasLimit: ethers.utils.hexlify(200000), gasPrice: ethers.utils.parseUnits("10", "gwei") }
)
console.log(`Tx-hash: ${tx.hash}`)
const receipt = await tx.wait();
console.log(`Tx was mined in block: ${receipt.blockNumber}`)
}
init()
You can check this working example that buys a token from pancakeswap.finance:
https://github.com/religion-counter/onlyone/blob/main/helper-scripts/buy-onlyone-pancakeswap.js
// Helper script that buys ONLYONE token from a specified address specified on text file SPECIFY_ACCOUNTS_YOU_WANT_TO_BUY_FOR_HERE.json
// The amount is specified with 'originalAmountToBuyWith' variable in the source
// The JSON file should have an array with objects with 'address' field and 'privateKey' field.
// Buys ONLYONE for ${bnbAmount} BNB from pancakeswap for address ${targetAccounts[targetIndex].address}
// targetIndex is passed as an argument: process.argv.splice(2)[0]
var fs = require('fs')
var Tx = require('ethereumjs-tx').Transaction;
var Web3 = require('web3')
var Common = require('ethereumjs-common').default;
var web3 = new Web3(new Web3.providers.HttpProvider('https://bsc-dataseed.binance.org/'))
var BSC_FORK = Common.forCustomChain(
'mainnet',
{
name: 'Binance Smart Chain Mainnet',
networkId: 56,
chainId: 56,
url: 'https://bsc-dataseed.binance.org/'
},
'istanbul',
);
// SPECIFY_THE_AMOUNT_OF_BNB_YOU_WANT_TO_BUY_FOR_HERE
var originalAmountToBuyWith = '0.007' + Math.random().toString().slice(2,7);
var bnbAmount = web3.utils.toWei(originalAmountToBuyWith, 'ether');
var targetAccounts = JSON.parse(fs.readFileSync('SPECIFY_ACCOUNTS_YOU_WANT_TO_BUY_FOR_HERE.json', 'utf-8'));
var targetIndex = Number(process.argv.splice(2)[0]);
var targetAccount = targetAccounts[targetIndex];
console.log(`Buying ONLYONE for ${originalAmountToBuyWith} BNB from pancakeswap for address ${targetAccount.address}`);
var res = buyOnlyone(targetAccounts[targetIndex], bnbAmount);
console.log(res);
async function buyOnlyone(targetAccount, amount) {
var amountToBuyWith = web3.utils.toHex(amount);
var privateKey = Buffer.from(targetAccount.privateKey.slice(2), 'hex') ;
var abiArray = JSON.parse(JSON.parse(fs.readFileSync('onlyone-abi.json','utf-8')));
var tokenAddress = '0xb899db682e6d6164d885ff67c1e676141deaaa40'; // ONLYONE contract address
var WBNBAddress = '0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c'; // WBNB token address
// var onlyOneWbnbCakePairAddress = '0xd22fa770dad9520924217b51bf7433c4a26067c2';
// var pairAbi = JSON.parse(fs.readFileSync('cake-pair-onlyone-bnb-abi.json', 'utf-8'));
// var pairContract = new web3.eth.Contract(pairAbi, onlyOneWbnbCakePairAddress/*, {from: targetAccount.address}*/);
var amountOutMin = '100' + Math.random().toString().slice(2,6);
var pancakeSwapRouterAddress = '0x10ed43c718714eb63d5aa57b78b54704e256024e';
var routerAbi = JSON.parse(fs.readFileSync('pancake-router-abi.json', 'utf-8'));
var contract = new web3.eth.Contract(routerAbi, pancakeSwapRouterAddress, {from: targetAccount.address});
var data = contract.methods.swapExactETHForTokens(
web3.utils.toHex(amountOutMin),
[WBNBAddress,
tokenAddress],
targetAccount.address,
web3.utils.toHex(Math.round(Date.now()/1000)+60*20),
);
var count = await web3.eth.getTransactionCount(targetAccount.address);
var rawTransaction = {
"from":targetAccount.address,
"gasPrice":web3.utils.toHex(5000000000),
"gasLimit":web3.utils.toHex(290000),
"to":pancakeSwapRouterAddress,
"value":web3.utils.toHex(amountToBuyWith),
"data":data.encodeABI(),
"nonce":web3.utils.toHex(count)
};
var transaction = new Tx(rawTransaction, { 'common': BSC_FORK });
transaction.sign(privateKey);
var result = await web3.eth.sendSignedTransaction('0x' + transaction.serialize().toString('hex'));
console.log(result)
return result;
}
You can also contribute to the repository if you are interested.
I am a total beginner but have the same goal as you. I'm trying to get your code to work.
I used the following Code to create the signer. I used my 'mnemonic' instead of the private key:
const wallet = ethers.Wallet.fromMnemonic(mnemonic);
I noticed that pancake uses the swapExactETHForTokens() function instead of swapExactTokensForTokens() for the transactions. Does that make any difference?
I am not quite sure whether the Output and Input Amount is calculated correctly... I don't quite understand it yet, but will try to go through it as soon as possible.
jklepatch has written a code which contains trading via pancakeswap V2. However, the trade does not work if i execute the code because the AmountOut generats an error (codeline 78)...
In the meantime, have you been able to modify your code or make it work? If it is helpful I can also post my version of your code here.
I am attempting to send a json file created from fields in a webpage to a node function in AWS Lambda to add it to a DynamoDB table. I have the JSON made but I don't know how to pass it from the js used for the page to the lambda function. As this is for a class project, my group and I have decided to forego amazon's gateway API, and are just raw calling lambda functions using amazon's js sdk. I've checked Amazon's documentation and other various examples, but I haven't been able to find a complete solution.
Node function in lambda
const AWS = require('aws-sdk');
const db = new AWS.DynamoDB.DocumentClient({region: 'us-east-1'});
exports.handler = async (event) => {
const params = {
TableName : 'characterTable',
Item: {
name : 'defaultName'
}
};
const userID = 'placeholder';
params.Item.userID = userID;
return await db.put(params).promise();
};
//}
Webpage js:
var lambda = new AWS.Lambda();
function makeJSON(){
var userID = "";
var name = document.forms["characterForm"]["characterName"].value;
var race = document.forms["characterForm"]["race"].value;
var playerClass = document.forms["characterForm"]["class"].value;
var strength = document.forms["characterForm"]["strength"].value;
var dexterity = document.forms["characterForm"]["dexterity"].value;
var constitution = document.forms["characterForm"]["constitution"].value;
var intelligence = document.forms["characterForm"]["intelligence"].value;
var wisdom = document.forms["characterForm"]["wisdom"].value;
var charisma = document.forms["characterForm"]["charisma"].value;
characterSheetObj = {userID: userID, name: name, race: race, class: playerClass, strength: strength, dexterity: dexterity, constitution: constitution, intelligence: intelligence, wisdom: wisdom, charisma: charisma}
characterSheetJSON = JSON.stringify(characterSheetObj);
alert(characterSheetJSON);
var myParams = {
FunctionName : 'addCharacterSheet',
InvocationType : 'RequestResponse',
LogType : 'None',
Payload : characterSheetJSON
}
lambda.invoke(myParams, function(err, data){
//if it errors, prompts an error message
if (err) {
prompt(err);
}
//otherwise puts up a message that it didnt error. the lambda function presently doesnt do anything
//in the future the lambda function should produce a json file for the JavaScript here to do something with
else {
alert("Did not error");
}
});
}
The html page for the raw javascript includes the proper setup for importing the sdk and configuring the region/user pool
I just don't know how to get the payload from the invocation in my node function, as this is my first time working with lambda and amazon's sdk, or doing any web development work at all, to be honest.
i would do it with async await. It's better to read.
lambda.invoke = util.promisify(lambda.invoke);
const result = await lambda.invoke(yourParams);
const payload = JSON.parse(result.Payload);
I use the following code which works as expected however I wonder if there is a way to improve is as Im doing New Buffer twice.
I need to encode two values now but more latter... , user and password.
I mean reduce boilerplate code
let secret = await getSecret("mt", "fe");
let adminBuff = new Buffer(secret.admin, 'base64');
let admin = adminBuff.toString('ascii');
let adminPass = new Buffer(secret.pass, 'base64');
let pass = adminPass.toString('ascii');
Use Buffer.from() since new Buffer is deprecated.
As for the reusability part, creating a function would be helpful. Something like the following:
function toAsciiString (value) {
const valueBuff = Buffer.from(value, 'base64');
return valueBuff.toString('ascii');
}
let secret = await getSecret("mt", "fe");
let admin = toAsciiString(secret.admin);
let pass = toAsciiString(secret.pass);
Hope this helps :)
You can iterate over the keys of the object "secret", process their values and create a new object containing the processing results.
For example:
let secret = await getSecret("mt", "fe");
const resultObj = {};
Object.keys(secret).forEach((key) => {
const newBuffer = new Buffer(secret[key], 'base64');
resultObj[key] = newBuffer.toString('ascii');
});
I have created a smart contract that facilitates a token sale. I want a user to be able to press a button that automatically generates an ERC20 wallet that tokens can be sent to.
I am using the truffle HDWallet Provider and Infura. I don't want the user to have to use MetaMask or anything else. I will sign the transactions on the backend using the private key of the user's generated wallet.
How would I go about implementing this so a new wallet is created for every new user that wants to perform a transaction?
This is the code that allowed me to create a wallet on a button press. I now need to find a way to store the credentials of these wallets so I can sign transactions and transfer tokens to them.
var bip39 = require('bip39');
const EthereumUtil = require('ethereumjs-util');
const hdkey = require('hdkey');
const mnemonic = bip39.generateMnemonic(); //generates string
const seed = bip39.mnemonicToSeed(mnemonic); //creates seed buffer
const root = hdkey.fromMasterSeed(seed);
const masterPrivateKey = root.privateKey.toString('hex');
const addrNode = root.derive("m/44'/60'/0'/0/0");
const pubKey = EthereumUtil.privateToPublic(addrNode._privateKey);
const addr = EthereumUtil.publicToAddress(pubKey).toString('hex');
const address = EthereumUtil.toChecksumAddress(addr);
Its pretty simple, just create a HDWalletProvider and the provider.addresses has all the accounts you can use:
Here is the code:
const createWallet = function(_providerUrl) {
const mnemonicPhrase = bip39.generateMnemonic()
let provider = new HDWalletProvider({
mnemonic: {
phrase: mnemonicPhrase
},
providerOrUrl: "http://localhost:8545"
});
return provider.addresses
}
Addresses has the accounts
The solution has already been commented above, but I have a better suggestion for you. I think it's better to generate only one mnemonic in your backend and you create infinite accounts from that mnemonic. From each account you extract the private key and store it, store the address of the account and the index of the account.
Management code:
const HDWalletProvider = require("#truffle/hdwallet-provider");
const Web3 = require("web3");
const mnemonicPhrase = "mnemonicPhrase here";
const urlRPC = "https://bsc-dataseed.binance.org";
//Creating another instance to query balances
const Web3Instance = require('web3');
const web3instance = new Web3Instance(new Web3Instance.providers.HttpProvider(urlRPC));
//HD Wallet Provider loads by default only 10 accounts
//We change the limit to as many accounts as we want
//You cannot run the loop below for i greater than 10, because for
const limit = 50;
async function getAccount() {
provider = new HDWalletProvider({
mnemonic: mnemonicPhrase,
numberOfAddresses: limit,
providerOrUrl: urlRPC,
addressIndex: 0,
});
//HDWalletProvider is compatible with Web3
//Use it at Web3 constructor, just like any other Web3 Provider
const web3 = new Web3(provider);
//I realized that iterating a loop to limit always gives an error when it reaches limit
//This happens in this HD Wallet Provider package
//limit - 1 seems to work best
for (let i = 0; i < limit - 1; i ++) {
var get = await web3.eth.getAccounts()
var address = get[i];
var balanceETH = await web3instance.eth.getBalance(address)
//Checking the adress
console.log(address);
console.log(balanceETH);
}
}
getAccount();
Here is more information:
https://ethereum.stackexchange.com/questions/52370/web3-create-account-from-mnemonic-passphrase/144340#144340