简体   繁体   中英

VM Exception while processing transaction: revert TransferHelper: TRANSFER_FROM_FAILED when running manipulatePrice.js test for bot.js

I am using the code from Dapp Unversity's trading bot masterclass to try to create a bot that will scan cryptocurrency decentralized exchanges for price differences in token pairs and then execute a smart contract to use a flash loan to profit off of this. When testing, I am able to see run a ganache-cli node and run my scanning bot to listen for swap opportunities on ganache. There is a script designed to create a swap opportunity by swapping a large amount of SHIB for WETH on the test.net to see if the smart contract will deploy and execute when a swap opportunity is detected by the bot. However, running this script yields the error

UnhandledPromiseRejectionWarning: Error: Returned error: VM Exception while processing transaction: revert TransferHelper: TRANSFER_FROM_FAILED

Also, in the ganache-cli terminal, I get:

Runtime Error: revert Revert reason: TransferHelper: TRANSFER_FROM_FAILED

Here are the commands I run to get to the points above: First, I successfully run ganache-cli -f wss://eth-mai.net.alchemyapi.io/v2/<Your-App-Key> -u 0x0e5069514a3dd613350bab01b58fd850058e5ca4 -p 7545 with my app key. Then, I successfully run node bot.js in another terminal to scan for swap opportunities on ganache. Finally, I run node scripts\manipulatePrice.JS which outputs "Beginnig Swap... Input token: SHIB Output token: WETH" before outputting the above error.

I have tried using node --trace-warnings to show where the warning was created, but was led nowhere helpful. I am wondering if it has something to do with the Runtime Error: revert message? Below is the code for the manipulatePrice.js script I am trying to run to test my bot. I can attach more code if need be, but don't want to post too much. If anyone has insight as to what or where the issue might be, I would greatly appreciate it!!

require("dotenv").config();

const Web3 = require('web3')
const {
    ChainId,
    Token,
    WETH
} = require("@uniswap/sdk")
const IUniswapV2Router02 = require('@uniswap/v2-periphery/build/IUniswapV2Router02.json')
const IUniswapV2Factory = require("@uniswap/v2-core/build/IUniswapV2Factory.json")
const IERC20 = require('@openzeppelin/contracts/build/contracts/ERC20.json')

// -- SETUP NETWORK & WEB3 -- //

const chainId = ChainId.MAINNET
const web3 = new Web3('http://127.0.0.1:7545')

// -- IMPORT HELPER FUNCTIONS -- //

const { getPairContract, calculatePrice } = require('../helpers/helpers')

// -- IMPORT & SETUP UNISWAP/SUSHISWAP CONTRACTS -- //

const config = require('../config.json')
const uFactory = new web3.eth.Contract(IUniswapV2Factory.abi, config.UNISWAP.FACTORY_ADDRESS) // UNISWAP FACTORY CONTRACT
const sFactory = new web3.eth.Contract(IUniswapV2Factory.abi, config.SUSHISWAP.FACTORY_ADDRESS) // SUSHISWAP FACTORY CONTRACT
const uRouter = new web3.eth.Contract(IUniswapV2Router02.abi, config.UNISWAP.V2_ROUTER_02_ADDRESS) // UNISWAP ROUTER CONTRACT
const sRouter = new web3.eth.Contract(IUniswapV2Router02.abi, config.SUSHISWAP.V2_ROUTER_02_ADDRESS) // UNISWAP ROUTER CONTRACT

// -- CONFIGURE VALUES HERE -- //

const V2_FACTORY_TO_USE = uFactory
const V2_ROUTER_TO_USE = uRouter

const UNLOCKED_ACCOUNT = '0x0e5069514a3Dd613350BAB01B58FD850058E5ca4' // SHIB Unlocked Account
const ERC20_ADDRESS = process.env.ARB_AGAINST
const AMOUNT = '40500000000000' // 40,500,000,000,000 SHIB -- Tokens will automatically be converted to wei
const GAS = 450000

// -- SETUP ERC20 CONTRACT & TOKEN -- //

const ERC20_CONTRACT = new web3.eth.Contract(IERC20.abi, ERC20_ADDRESS)
const WETH_CONTRACT = new web3.eth.Contract(IERC20.abi, WETH[chainId].address)

// -- MAIN SCRIPT -- //

const main = async () => {
    const accounts = await web3.eth.getAccounts()
    const account = accounts[1] // This will be the account to recieve WETH after we perform the swap to manipulate price

    const pairContract = await getPairContract(V2_FACTORY_TO_USE, ERC20_ADDRESS, WETH[chainId].address)
    const token = new Token(
        ChainId.MAINNET,
        ERC20_ADDRESS,
        18,
        await ERC20_CONTRACT.methods.symbol().call(),
        await ERC20_CONTRACT.methods.name().call()
    )

    // Fetch price of SHIB/WETH before we execute the swap
    const priceBefore = await calculatePrice(pairContract)

    await manipulatePrice(token, account)

    // Fetch price of SHIB/WETH after the swap
    const priceAfter = await calculatePrice(pairContract)

    const data = {
        'Price Before': `1 ${WETH[chainId].symbol} = ${Number(priceBefore).toFixed(0)} ${token.symbol}`,
        'Price After': `1 ${WETH[chainId].symbol} = ${Number(priceAfter).toFixed(0)} ${token.symbol}`,
    }

    console.table(data)

    let balance = await WETH_CONTRACT.methods.balanceOf(account).call()
    balance = web3.utils.fromWei(balance.toString(), 'ether')

    console.log(`\nBalance in reciever account: ${balance} WETH\n`)
}

main()

// 

async function manipulatePrice(token, account) {
    console.log(`\nBeginning Swap...\n`)

    console.log(`Input Token: ${token.symbol}`)
    console.log(`Output Token: ${WETH[chainId].symbol}\n`)

    const amountIn = new web3.utils.BN(
        web3.utils.toWei(AMOUNT, 'ether')
    )

    const path = [token.address, WETH[chainId].address]
    const deadline = Math.floor(Date.now() / 1000) + 60 * 20 // 20 minutes

    await ERC20_CONTRACT.methods.approve(V2_ROUTER_TO_USE._address, amountIn).send({ from: UNLOCKED_ACCOUNT })
    const receipt = await V2_ROUTER_TO_USE.methods.swapExactTokensForTokens(amountIn, 0, path, account, deadline).send({ from: UNLOCKED_ACCOUNT, gas: GAS });

    console.log(`Swap Complete!\n`)

    return receipt
}

I ran into this issue for the same bot myself and I found the solution. Instead of using .send you should use .sendSignedTransaction and sign it yourself with the correct parameters. BUT if you are sending from an unlocked account, then use ethers.js to get a signer without knowing the private key!

So, you should replace

await ERC20_CONTRACT.methods.approve(V2_ROUTER_TO_USE._address, amountIn).send({ from: UNLOCKED_ACCOUNT })
const receipt = await V2_ROUTER_TO_USE.methods.swapExactTokensForTokens(amountIn, 0, path, account, deadline).send({ from: UNLOCKED_ACCOUNT, gas: GAS });

With this ethers.js alternative

const { ethers } = require("ethers")
const provider = new ethers.providers.JsonRpcProvider("http://127.0.0.1:8545")
const signer = provider.getSigner(UNLOCKED_ACCOUNT)
console.log(provider.chainId)

/*
    Define the above code at the top,
    Then put the rest of your code here, 
    Then use this for the ethers transaction
*/

const approvalReceiptEthers = await signer.sendTransaction({
    from: UNLOCKED_ACCOUNT,
    to: ERC20_CONTRACT._address,
    data: ERC20_CONTRACT.methods.approve(V2_ROUTER_TO_USE._address, web3.utils.fromWei(amountIn).toString()).encodeABI(),
    gasLimit: GAS
})

/*
 * Verify that your unlocked account is allowed to use the funds
*/
var allowance = await ERC20_CONTRACT.methods.allowance(UNLOCKED_ACCOUNT, V2_ROUTER_TO_USE._address).call()
console.log("ALLOWANCE:\t\t", web3.utils.fromWei(allowance).toString(), 'ether')
console.log("ATTEMPTED AMOUNT:\t", web3.utils.fromWei(amountIn).toString(), 'ether')


const swapReceiptEthers = await signer.sendTransaction({
    from: UNLOCKED_ACCOUNT,
    to: V2_ROUTER_TO_USE._address,
    data: V2_ROUTER_TO_USE.methods.swapExactTokensForTokens(web3.utils.fromWei(amountIn).toString(), 0, path, account, deadline).encodeABI(),
    gasLimit: GAS
}).then(console.log)

Note: the following code, will only work provided that you signed the transaction with THE SENDER'S PRIVATE KEY. So, if you are sending a transaction on behalf of your own account, use this code (presumably for bot.js)

const approvalTransaction = {
    'from' : 'your account address here',
    'to' : ERC20_CONTRACT._address,
    'data' : ERC20_CONTRACT.methods.approve(V2_ROUTER_TO_USE._address, web3.utils.fromWei(amountIn).toString()).encodeABI(),
    'gas' : GAS
}
const transaction = {
    'from' : 'your account address here',
    'to' : V2_ROUTER_TO_USE._address,
    'data' : V2_ROUTER_TO_USE.methods.swapExactTokensForTokens(web3.utils.fromWei(amountIn).toString(), 0, path, account, deadline).encodeABI(),
    'gas' : GAS
    
}
const signedApprovalTx = await web3.eth.accounts.signTransaction(approvalTransaction, process.env.DEPLOYMENT_ACCOUNT_KEY, )
const signedTx = await web3.eth.accounts.signTransaction(transaction, process.env.DEPLOYMENT_ACCOUNT_KEY)

await web3.eth.sendSignedTransaction(signedApprovalTx.rawTransaction)
const receipt = await web3.eth.sendSignedTransaction(signedTx.rawTransaction)

NOTE : For those of you following the same Trading Bot masterclass, this same logic applies to the bot.js code as well when you execute your trade..

SUMMARY: If you are sending a signed transaction using an unlocked account with ganache-cli, you will need to use ethers.js to sign the message without knowing the private key. Otherwise if you are sending a signed transaction on behalf of yourself, you can use Web3.js to sign the message with your own private

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM