【发布时间】:2022-08-08 10:13:37
【问题描述】:
非常感谢您的时间和帮助
我想在 uniswap 和 sushiswap 之间运行 flashloan。但是,尽管尝试了类似问题的答案中提到的几种潜在解决方案,但我得到了这个错误并且它仍然存在。我是通过 vscode 用安全帽编写的。
这是错误:错误:处理事务时出现 VM 异常:使用原因字符串 \'TransferHelper: TRANSFER_FROM_FAILED\' 还原
这是坚固性代码:
// SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0;
import \"hardhat/console.sol\";
// Uniswap interface and library imports
import \"./libraries/UniswapV2Library.sol\";
import \"./libraries/SafeERC20.sol\";
import \"./interfaces/IUniswapV2Router02.sol\";
import \"./interfaces/IUniswapV2Pair.sol\";
import \"./interfaces/IUniswapV2Factory.sol\";
import \"./interfaces/IERC20.sol\";
contract FlashloanSwap {
using SafeERC20 for IERC20;
// Define the factory and router addresses of the DEXs
address private constant UNISWAP_FACTORY =
0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f;
address private constant UNISWAP_ROUTER =
0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D;
address private constant SUSHISWAP_FACTORY =
0xC0AEe478e3658e2610c5F7A4A2E1777cE9e4f2Ac;
address private constant SUSHISWAP_ROUTER =
0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F;
// Trade variables
uint256 private deadline = block.timestamp + 1 days;
// Get the contract balance of token
function getBalanceOfToken(address _address) public view returns (uint256) {
return IERC20(_address).balanceOf(address(this));
}
function checkProfitability(uint256 _input, uint256 _outPut)
private
pure
returns (bool)
{
return _outPut > _input;
}
// There must be a function to receive ETH
receive() external payable {}
fallback() external payable {}
// Eth balance needs to be checked sometimes
function getBalance() public view returns (uint256) {
return address(this).balance;
}
// Define a function to trade a given token in exchange for another token on a given DEX
function executeTrade(
address _fromToken,
address _toToken,
uint256 _amountIn,
address factory,
address router
) private returns (uint256) {
address pair = IUniswapV2Factory(factory).getPair(_fromToken, _toToken);
require(pair != address(0), \"Pool does not exist\");
// Calculate amount out
address[] memory path = new address[](2);
path[0] = _fromToken;
path[1] = _toToken;
uint256 amountRequired = IUniswapV2Router01(router).getAmountsOut(
_amountIn,
path
)[1];
console.log(\"Amount Required: \", amountRequired);
// Perform arbitrage - Swap for another token
IUniswapV2Router02(router)
.swapExactTokensForTokensSupportingFeeOnTransferTokens(
_amountIn,
amountRequired,
path,
address(this),
deadline
);
uint256 balanceA = getBalanceOfToken(_fromToken);
uint256 balanceB = getBalanceOfToken(_toToken);
uint256 amountReceived = balanceA == 0 ? balanceB : balanceA;
console.log(\"amountReceived: \", amountReceived);
require(amountReceived > 0, \"Aborted Transaction\");
return amountReceived;
}
/*
I) It will be run whenever an arbitrage opportunity is detected
*/
function runTheArbitrage(
address tokenA,
address tokenB,
uint256 amountA,
uint256 amountB
) external {
// Get the pair address on uniswap
address pairAddress = IUniswapV2Factory(UNISWAP_FACTORY).getPair(
tokenA,
tokenB
);
// Check whether the pair exists
require(
pairAddress != address(0),
\"The pair does not exist on uniswap\"
);
// Save the borrowed token\'s specifications in _data to be passed to uniswapV2Call
address borrowedTokenAddress = amountA == 0 ? tokenB : tokenA;
uint256 borrowedTokenAmount = amountA == 0 ? amountB : amountA;
bytes memory data = abi.encode(
borrowedTokenAddress,
borrowedTokenAmount
);
// Create the flashloan with the swap function
IUniswapV2Pair(pairAddress).swap(amountA, amountB, address(this), data);
}
/*
II) With executing the previous function, uniswap will call this function in order to complete the flashloan cycle
*/
function uniswapV2Call(
address _sender,
uint256 _amountA,
uint256 _amountB,
bytes calldata _data
) external {
// get the specifications of the borrowed token
address token0 = IUniswapV2Pair(msg.sender).token0();
address token1 = IUniswapV2Pair(msg.sender).token1();
(address borrowedTokenAddress, uint256 borrowedTokenAmount) = abi
.decode(_data, (address, uint256));
token0 = token0 == borrowedTokenAddress ? token0 : token1;
token1 = token0 == borrowedTokenAddress ? token1 : token0;
// Check whether this function is called only by the pair contracts of uniswap
require(
msg.sender ==
UniswapV2Library.pairFor(UNISWAP_FACTORY, token0, token1),
\"Only requests from uniswap pair contracts are accepted\"
);
// Check whether this contract is the sender
require(_sender == address(this), \"Sender should match this contract\");
// Check one of the amounts to be zero
require(
_amountA == 0 || _amountB == 0,
\"One of the amounts must be zero\"
);
// Execute the first swap on source DEX
IERC20(token0).safeIncreaseAllowance(
UNISWAP_ROUTER,
borrowedTokenAmount
);
uint256 firstAmountOut = executeTrade(
token0,
token1,
borrowedTokenAmount,
UNISWAP_FACTORY,
UNISWAP_ROUTER
);
// Aprove the second DEX to spend the swapped token, then execute the trade on it
IERC20(token1).safeIncreaseAllowance(SUSHISWAP_ROUTER, firstAmountOut);
uint256 secondAmountOut = executeTrade(
token1,
token0,
firstAmountOut,
SUSHISWAP_FACTORY,
SUSHISWAP_ROUTER
);
uint256 fee = ((borrowedTokenAmount * 3) / 997) + 1;
uint256 amountToBePaidBack = borrowedTokenAmount + fee;
// Check profitability
bool profCheck = checkProfitability(
amountToBePaidBack,
secondAmountOut
);
require(profCheck, \"Arbitrage not profitable\");
// Pay back the loan
bool success1 = IERC20(token0).transfer(msg.sender, amountToBePaidBack);
// Send the profit to the initiator of the transaction
bool success2 = IERC20(token0).transfer(
tx.origin,
secondAmountOut - amountToBePaidBack
);
console.log(secondAmountOut - amountToBePaidBack, success2);
}
}
这里还有用于安全帽测试的 hardhat.config.js 文件
const { version } = require(\"chai\");
require(\"@nomiclabs/hardhat-waffle\");
require(\'dotenv\').config();
module.exports = {
solidity: {
compilers: [
{version: \'0.5.5\'},
{version: \'0.6.6\'},
{version: \'0.8.8\'},
],
},
networks: {
hardhat: {
forking: {
url: process.env.alchemy_mainnet_key,
},
},
testnet: {
url: process.env.alchemy_renkiby_api,
chainId: 97,
accounts: [
process.enc.test_private_key
],
},
mainnet: {
url: process.env.alchemy_mainnet_key,
chainId: 56,
accounts: [
process.env.private_key
],
},
},
};
最后,这是我的测试代码
const { expect } = require(\"chai\");
const { ethers, waffle } = require(\"hardhat\");
const { deployContract } = require(\"ethereum-waffle\");
const provider = waffle.provider;
const { abi } = require(\'../artifacts/contracts/interfaces/IERC20.sol/IERC20.json\');
describe(\"Checking the whole arbitrage process\", function () {
// Get the factory and router addresses
const UNISWAP_FACTORY = \"0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f\";
const UNISWAP_ROUTER = \"0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D\";
const SUSHI_FACTORY = \"0xC0AEe478e3658e2610c5F7A4A2E1777cE9e4f2Ac\";
const SUSHI_ROUTER = \"0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F\";
// Token addresses
const tokenA = \"0x6B175474E89094C44Da98b954EedeAC495271d0F\";
const tokenB = \"0x0F5D2fB29fb7d3CFeE444a200298f468908cC942\";
// Get the decimals
const decimals = 18;
beforeEach(async function () {
// Get owner as signer
[owner] = await ethers.getSigners();
// Deploy smart contract
const FlashloanSwap = await ethers.getContractFactory(\"FlashloanSwap\");
flashloanSwap = await FlashloanSwap.deploy();
await flashloanSwap.deployed();
console.log(\'\\n\', \"Contract is deployed by: \", owner.address);
console.log(\"contract is deployed to: \", flashloanSwap.address, \'\\n\');
const transactionHash = await owner.sendTransaction({
to: flashloanSwap.address,
value: ethers.utils.parseEther(\"1.0\"),
});
console.log(\"transactionHash : \", transactionHash);
balanceOfEth = await provider.getBalance(flashloanSwap.address)
balanceOfEth = ethers.utils.formatUnits(balanceOfEth, 18);
console.log(\'\\n\', \"Balance of ETH before transaction : \", balanceOfEth.toString(), \'\\n\');
});
it(\"Check Whether Swap Occurs or Not\", async () => {
await flashloanSwap.runTheArbitrage(tokenA, tokenB, 0, 100);
balanceOfEth = await provider.getBalance(flashloanSwap.address)
balanceOfEth = ethers.utils.formatEther(balanceOfEth, 18);
console.log(\'\\n\', \"Balance of ETH after transaction : \", balanceOfEth, \'\\n\');
});