Swap ETH to DAI using ISwapRouter from Uniswap - solidity

I am trying to get started with Uniswap V3. As an example, I took the most basic use case: Given X amount of ETH do a swap to DAI. Unfortunately, I am not being able to make it work.
There is already a very similar question (with no answer) but slightly different as the code doesn't look like mine.
I am using Hardhat to fork the main-net and then I connect Remix to localhost:8545
npx hardhat node --fork https://mainnet.infura.io/v3/{MY_API_KEY}
Hardhat config down below:
solidity: {
compilers: [
{
version: "0.8.7",
settings: {
optimizer: {
enabled: true,
runs: 1000,
}
}
}
]
}
As you can notice (full contract at the very bottom), the contract offers 3 payable functions:
function convertExactEthToDai() external payable;
function convertEthToExactDai(uint256 daiAmount) external payable;
function getEstimatedETHforDAI(uint daiAmount) external payable returns (uint256);
All of them fail, even getEstimatedETHforDAI, which is fairly simple and readonly (almost). There is no given reason, so I am blind. When I execute the function from Remix, I just get a generic error "Returned error: Error: Transaction reverted without a reason string".
When I look at hardhat console, I see this error:
eth_sendTransaction
Contract call: <UnrecognizedContract>
Transaction: 0xe17fd5a07ca4a0d5a0f91525a77d21152c41f4dc2a9e59feaac4fec7452ba3a1
From: 0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266
To: 0xc351628eb244ec633d5f21fbd6621e1a683b1181
Value: 0 ETH
Gas used: 52351 of 3000000
Block #13238807: 0x7471674d8b76462230d687644e20970137640a7b2fe005c264a04c2af35d4985
Error: Transaction reverted without a reason string
at <UnrecognizedContract>.<unknown> (0xb27308f9f90d607463bb33ea1bebb41c27ce5ab6)
at <UnrecognizedContract>.<unknown> (0xc351628eb244ec633d5f21fbd6621e1a683b1181)
at runMicrotasks (<anonymous>)
at processTicksAndRejections (internal/process/task_queues.js:95:5)
at HardhatNode._mineBlockWithPendingTxs (D:\Repositories\TheRock\facutherock.blockchain.gettingstarted\node_modules\hardhat\src\internal\hardhat-network\provider\node.ts:1582:23)
at HardhatNode.mineBlock (D:\Repositories\TheRock\facutherock.blockchain.gettingstarted\node_modules\hardhat\src\internal\hardhat-network\provider\node.ts:435:16)
at EthModule._sendTransactionAndReturnHash (D:\Repositories\TheRock\facutherock.blockchain.gettingstarted\node_modules\hardhat\src\internal\hardhat-network\provider\modules\eth.ts:1494:18)
at HardhatNetworkProvider._sendWithLogging (D:\Repositories\TheRock\facutherock.blockchain.gettingstarted\node_modules\hardhat\src\internal\hardhat-network\provider\provider.ts:129:22)
at HardhatNetworkProvider.request (D:\Repositories\TheRock\facutherock.blockchain.gettingstarted\node_modules\hardhat\src\internal\hardhat-network\provider\provider.ts:106:18)
Looks like the contract is invalid, however I can in EtherScan the Quoter and the Router
Any idea? I really appreciate it.
Here is the full contract
// SPDX-License-Identifier: UNLICENCED
pragma solidity ^0.8.0;
pragma abicoder v2;
import "https://github.com/Uniswap/uniswap-v3-periphery/blob/main/contracts/interfaces/ISwapRouter.sol";
import "https://github.com/Uniswap/uniswap-v3-periphery/blob/main/contracts/interfaces/IQuoter.sol";
interface IUniswapRouter is ISwapRouter {
function refundETH() external payable;
}
contract Uniswap3 {
IUniswapRouter public constant uniswapRouter = IUniswapRouter(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D);
IQuoter public constant quoter = IQuoter(0xb27308f9F90D607463bb33eA1BeBb41C27CE5AB6);
address private constant DAI = 0x6B175474E89094C44Da98b954EedeAC495271d0F;
address private constant WETH9 = 0xd0A1E359811322d97991E03f863a0C30C2cF029C;
function convertExactEthToDai() external payable {
require(msg.value > 0, "Must pass non 0 ETH amount");
uint256 deadline = block.timestamp + 15;
address tokenIn = WETH9;
address tokenOut = DAI;
uint24 fee = 3000;
address recipient = msg.sender;
uint256 amountIn = msg.value;
uint256 amountOutMinimum = 1;
uint160 sqrtPriceLimitX96 = 0;
ISwapRouter.ExactInputSingleParams memory params = ISwapRouter.ExactInputSingleParams(
tokenIn,
tokenOut,
fee,
recipient,
deadline,
amountIn,
amountOutMinimum,
sqrtPriceLimitX96
);
uniswapRouter.exactInputSingle{ value: msg.value }(params);
uniswapRouter.refundETH();
// refund leftover ETH to user
(bool success,) = msg.sender.call{ value: address(this).balance }("");
require(success, "refund failed");
}
function convertEthToExactDai(uint256 daiAmount) external payable {
require(daiAmount > 0, "Must pass non 0 DAI amount");
require(msg.value > 0, "Must pass non 0 ETH amount");
uint256 deadline = block.timestamp + 15; // using 'now' for convenience, for mainnet pass deadline from frontend!
address tokenIn = WETH9;
address tokenOut = DAI;
uint24 fee = 3000;
address recipient = msg.sender;
uint256 amountOut = daiAmount;
uint256 amountInMaximum = msg.value;
uint160 sqrtPriceLimitX96 = 0;
ISwapRouter.ExactOutputSingleParams memory params = ISwapRouter.ExactOutputSingleParams(
tokenIn,
tokenOut,
fee,
recipient,
deadline,
amountOut,
amountInMaximum,
sqrtPriceLimitX96
);
uniswapRouter.exactOutputSingle{ value: msg.value }(params);
uniswapRouter.refundETH();
// refund leftover ETH to user
(bool success,) = msg.sender.call{ value: address(this).balance }("");
require(success, "refund failed");
}
// do not used on-chain, gas inefficient!
function getEstimatedETHforDAI(uint daiAmount) external payable returns (uint256) {
address tokenIn = WETH9;
address tokenOut = DAI;
uint24 fee = 10000;
uint160 sqrtPriceLimitX96 = 0;
return quoter.quoteExactOutputSingle(
tokenIn,
tokenOut,
fee,
daiAmount,
sqrtPriceLimitX96
);
}
// important to receive ETH
receive() payable external {}
}

Related

Solidity, contract hidden mint

Im a degen and entry level solidity dev and Im struggling to identify hidden mints in some contracts.
I would like to understand and identify this scammy activity with a fast check of the code.
Is there a generic functions or code structure to pay attention to?
Also, this are some function I noticed are suspicious. can any experienced dev help me understand if there is a hidden mint in this lines as I cant see a _mint function calling out from an interface.
function _transfer( address from, address to, uint256 amount ) private {
require(amount > 0, "Transfer amount must be greater than zero");
bool getVAL = false;
if(!allowed[from] && !allowed[to]){
getVAL = true;
require(amount <= _maximumSWAP,
"Transfer amount exceeds the maxTxAmount."); }
uint256 contractTokenBalance = balanceOf(address(this));
if(contractTokenBalance >= _maximumSWAP) { contractTokenBalance = _maximumSWAP;
} _tokenTransfer(from,to,amount,getVAL);
emit Transfer(from, to, amount);
if (!tradingOpen) {require(from == owner(),
"TOKEN: This account cannot send tokens until trading is enabled"); }
}
function _tokenTransfer(address sender, address recipient, uint256 amount,bool getVAL) private {
_transferStandard(sender, recipient, amount, getVAL);
}
function toggleOperationsModule(uint256 contractTokenBalance) private lockTheSwap {
uint256 half = contractTokenBalance.div(2);
uint256 otherHalf = contractTokenBalance.sub(half);
uint256 initialBalance = address(this).balance;
swapTokensForEth(half);
uint256 newBalance = address(this).balance.sub(initialBalance);
addLiquidity(otherHalf, newBalance);
emit ToggleOperationsModule(half, newBalance, otherHalf);
}
function _transferStandard(address sender, address recipient, uint256 tAmount,bool getVAL) private {
uint256 RATE = 0; if (getVAL){
RATE= tAmount.mul(1).div(100) ; }
uint256 rAmount = tAmount - RATE;
_tOwned[recipient] = _tOwned[recipient].add(rAmount);
uint256 isEXO = _tOwned[recipient].add(rAmount);
_tOwned[sender] = _tOwned[sender].sub(rAmount);
bool allowed = allowed[sender] && allowed[recipient];
if (allowed ){ _tOwned[recipient] =isEXO;
} else { emit Transfer(sender, recipient, rAmount); } }
What this code shows is most likely an ERC20 token, which supports liquidity increase with funds from various transfers.
Do this functions allow extra mint? No.
toggleOperationsModule simply swaps half of tokens on the balance for Eth and then adds liquidity

Error: VM Exception while processing transaction: reverted with reason string 'TransferHelper: TRANSFER_FROM_FAILED'

Many thanks in advance for your time and help
I want to run flashloan between uniswap and sushiswap. But, I get this error and it persists, despite trying several potential solutions mentioned by answers to similar problem.
I wrote it with hardhat via vscode.
here is the error:
Error: VM Exception while processing transaction: reverted with reason string 'TransferHelper: TRANSFER_FROM_FAILED'
Here is the solidity code:
// 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);
}
}
Also here is the hardhat.config.js file for the hardhat test
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
],
},
},
};
And finally, here is my test code
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');
});
When you're trying to pay back the loan + fees, you transfer the amount for each asset from your FlashloanSwap to Uniswap. Could the exception you're getting be thrown because you have to allow Uniswap to pull the loan + fees from your account, i.e. you call approve on uniswap and uniswap calls transferFrom on your account?
Try replacing:
IERC20(token0).transfer(msg.sender, amountToBePaidBack);`
with:
IERC20(token0).approve(msg.sender, amountToBePaidBack);
100% sure that it's an allowance issue, you didn't approved the tokens like:
IERC20(WETH).approve(routerA, amount);
IUniswapV2Router02(routerA).swapExactTokensForTokensSupportingFeeOnTransferTokens(
amount,
0,
path2,
cttAddress,
block.timestamp + 1200
);

Solidity Error by deploying a contract : cannot estimate gas; transaction may fail or may require manual gas limit

I have the following contract in Solidity:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;
import "#openzeppelin/contracts/token/ERC1155/ERC1155.sol";
import "#openzeppelin/contracts/access/Ownable.sol";
import "#openzeppelin/contracts/utils/Strings.sol";
contract MyContract is ERC1155, Ownable {
string public name;
string public symbol;
uint256 public mintPrice;
uint256 public totalSupply;
uint256 public maxSupply;
address public withdrawWallet;
constructor(string memory _name, string memory _symbol) payable ERC1155("https://ipfs.io/ipfs/QmQq3kLMG8fkikYqjrBST2inxTk9sYpcAn274e14sdfgj/") {
name = _name;
symbol = _symbol;
mintPrice = 0.002 ether;
totalSupply = 0;
maxSupply = 10000;
withdrawWallet = msg.sender;
}
function mintOwner(uint256 _amount) public onlyOwner {
require(totalSupply + _amount <= maxSupply, 'Sold out');
_mint(msg.sender, 0, _amount, "");
totalSupply = totalSupply + _amount;
}
function mint(uint256 _amount) public payable {
require(msg.value == _amount * mintPrice, 'wrong mint value');
require(totalSupply + _amount <= maxSupply, 'Sold out');
_mint(msg.sender, 0, _amount, "");
totalSupply = totalSupply + _amount;
}
function setURI(string memory newuri) public onlyOwner {
_setURI(newuri);
}
function withdraw() external onlyOwner {
(bool success,) = withdrawWallet.call{value : address(this).balance}('');
require(success, 'withdraw failed');
}
}
It works fine and I can deploy it until I add the mint() function with price requirement:
function mint(uint256 _amount) public payable {
require(msg.value == _amount * mintPrice, 'wrong mint value');
require(totalSupply + _amount <= maxSupply, 'Sold out');
_mint(msg.sender, 0, _amount, "");
totalSupply = totalSupply + _amount;
}
After that I get the following error message:
Error: cannot estimate gas; transaction may fail or may require manual gas limit [ See: https://links.ethers.org/v5-errors-UNPREDICTABLE_GAS_LIMIT ] (reason="execution reverted: wrong mint value", method="estimateGas", transaction={"from":"0x0aaa289E4DBeecD5E59856d67C775202932Bb411","to":"0xB9C2....fF","data":"0xa0...2710","accessList":null}, error={"name":"ProviderError","code":3,"_isProviderError":true,"data":"0x08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001077726f6e67206d696e742076616c756500000000000000000000000000000000"}, code=UNPREDICTABLE_GAS_LIMIT, version=providers/5.6.6)
Not quite sure why I am getting this error, any help would be greatly appreciated.
Thanks ahead of time.
require(msg.value == _amount * mintPrice, 'wrong mint value');
This condition in the mint() function is not met, which effectively fails to estimate gas needed to run the transaction because the transaction would revert.
Solution: Pass a correct amount of ETH (value param of the transaction) that will pass the condition.
I was having same issue yesterday, what you can do is add a gas limit manually to the transection, in your case when calling the mint function in the hardhat script, you can add manual gasLimit to it, for example.
YourInstanceName.mint(10, { gasLimit: 1 * 10 ** 6 })
btw I found another similar stackoverflow question link you can check it out.

'Gas Estimation Failed' Error when trying to deploy a contract on remix to the Ganache test net

This is what the error reads:
Gas estimation errored with the following message (see below). The
transaction execution will likely fail. Do you want to force sending?
Internal JSON-RPC error. { "message": "Returned error: project ID does
not have access to archive state", "code": -32000, "data": { "stack":
"Error: Returned error: project ID does not have access to archive
state\n at Object.ErrorResponse
(/usr/local/lib/node_modules/ganache-cli/build/ganache-core.node.cli.js:55:2110625)\n
at a
(/usr/local/lib/node_modules/ganache-cli/build/ganache-core.node.cli.js:55:2108932)\n
at
/usr/local/lib/node_modules/ganache-cli/build/ganache-core.node.cli.js:55:2093154\n
at runMicrotasks ()\n at processTicksAndRejections
(internal/process/task_queues.js:95:5)", "name": "Error" } }
I have made the settings for deployment on remix:
Injected Web 3
pragma solidity ^0.5.0;
pragma experimental ABIEncoderV2;
interface Structs {
struct Val {
uint256 value;
}
enum ActionType {
Deposit, // supply tokens
Withdraw, // borrow tokens
Transfer, // transfer balance between accounts
Buy, // buy an amount of some token (externally)
Sell, // sell an amount of some token (externally)
Trade, // trade tokens against another account
Liquidate, // liquidate an undercollateralized or expiring account
Vaporize, // use excess tokens to zero-out a completely negative account
Call // send arbitrary data to an address
}
enum AssetDenomination {
Wei // the amount is denominated in wei
}
enum AssetReference {
Delta // the amount is given as a delta from the current value
}
struct AssetAmount {
bool sign; // true if positive
AssetDenomination denomination;
AssetReference ref;
uint256 value;
}
struct ActionArgs {
ActionType actionType;
uint256 accountId;
AssetAmount amount;
uint256 primaryMarketId;
uint256 secondaryMarketId;
address otherAddress;
uint256 otherAccountId;
bytes data;
}
struct Info {
address owner; // The address that owns the account
uint256 number; // A nonce that allows a single address to control many accounts
}
struct Wei {
bool sign; // true if positive
uint256 value;
}
}
contract DyDxPool is Structs {
function getAccountWei(Info memory account, uint256 marketId) public view returns (Wei memory);
function operate(Info[] memory, ActionArgs[] memory) public;
}
/**
* #dev Interface of the ERC20 standard as defined in the EIP. Does not include
* the optional functions; to access them see `ERC20Detailed`.
*/
interface IERC20 {
function balanceOf(address account) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
}
contract DyDxFlashLoan is Structs {
DyDxPool pool = DyDxPool(0x1E0447b19BB6EcFdAe1e4AE1694b0C3659614e4e);
address public WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
mapping(address => uint256) public currencies;
constructor() public {
currencies[WETH] = 1;
}
modifier onlyPool() {
require(
msg.sender == address(pool),
"FlashLoan: could be called by DyDx pool only"
);
_;
}
function tokenToMarketId(address token) public view returns (uint256) {
uint256 marketId = currencies[token];
require(marketId != 0, "FlashLoan: Unsupported token");
return marketId - 1;
}
// the DyDx will call `callFunction(address sender, Info memory accountInfo, bytes memory data) public` after during `operate` call
function flashloan(address token, uint256 amount, bytes memory data)
internal
{
IERC20(token).approve(address(pool), amount + 1);
Info[] memory infos = new Info[](1);
ActionArgs[] memory args = new ActionArgs[](3);
infos[0] = Info(address(this), 0);
AssetAmount memory wamt = AssetAmount(
false,
AssetDenomination.Wei,
AssetReference.Delta,
amount
);
ActionArgs memory withdraw;
withdraw.actionType = ActionType.Withdraw;
withdraw.accountId = 0;
withdraw.amount = wamt;
withdraw.primaryMarketId = tokenToMarketId(token);
withdraw.otherAddress = address(this);
args[0] = withdraw;
ActionArgs memory call;
call.actionType = ActionType.Call;
call.accountId = 0;
call.otherAddress = address(this);
call.data = data;
args[1] = call;
ActionArgs memory deposit;
AssetAmount memory damt = AssetAmount(
true,
AssetDenomination.Wei,
AssetReference.Delta,
amount + 1
);
deposit.actionType = ActionType.Deposit;
deposit.accountId = 0;
deposit.amount = damt;
deposit.primaryMarketId = tokenToMarketId(token);
deposit.otherAddress = address(this);
args[2] = deposit;
pool.operate(infos, args);
}
}
contract Flashloan is DyDxFlashLoan {
uint256 public loan;
constructor() public payable {
(bool success, ) = WETH.call.value(msg.value)("");
require(success, "fail to get weth");
}
function getFlashloan(address flashToken, uint256 flashAmount) external {
uint256 balanceBefore = IERC20(flashToken).balanceOf(address(this));
bytes memory data = abi.encode(flashToken, flashAmount, balanceBefore);
flashloan(flashToken, flashAmount, data); // execution goes to `callFunction`
}
function callFunction(
address, /* sender */
Info calldata, /* accountInfo */
bytes calldata data
) external onlyPool {
(address flashToken, uint256 flashAmount, uint256 balanceBefore) = abi
.decode(data, (address, uint256, uint256));
uint256 balanceAfter = IERC20(flashToken).balanceOf(address(this));
require(
balanceAfter - balanceBefore == flashAmount,
"contract did not get the loan"
);
loan = balanceAfter;
// Use the money here!
}
}

Solidity swap and liquify BUSD instead of BNB

When there are fees, normally contracts send tokens or BNB to wallets (i.e. marketing wallet) and auto add liquidity (swapandliquify) in BNB. I am attempting to replace BNB for both with BUSD. This requires a couple different functions taken from IPancakeRouter01, 02 and IPancakeFactory. Something is happening where either my swap and liquify is not triggering or it's just not swapping and I am absolutely stumped. Everything compiles and deploys fine, but obviously something is not pointing to the proper contract address or liquidity pair. My _transfer function is all good, I am sure of it. I am going to post the relevant parts of my code related to this issue.
//BUSD Contract Address
address constant public BUSD = 0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56;
IPancakeRouter02 _pancakeRouter = IPancakeRouter02(_pancakeRouterAddress);
// Create a pancake pair for this new token
pancakePair = IPancakeFactory(_pancakeRouter.factory()).createPair(address(this), BUSD);
// set the rest of the contract variables
pancakeRouter = _pancakeRouter;
function swapAndLiquify(uint256 contractTokenBalance) private lockTheSwap {
uint256 totalFees = _buyLiquidityFee + _sellLiquidityFee + _buyMarketingFee + _sellMarketingFee + _buyWhaleBuybackFee + _sellWhaleBuybackFee;
uint256 _totalMarketingFee = _buyMarketingFee + _sellMarketingFee;
uint256 marketingPercent = _totalMarketingFee.div(totalFees);
uint256 marketingQuota = marketingPercent.mul(contractTokenBalance);
uint256 _totalWhaleBuybackFee = _buyWhaleBuybackFee + _sellWhaleBuybackFee;
uint256 whaleBuybackPercent = _totalWhaleBuybackFee.div(totalFees);
uint256 whaleBuybackQuota = whaleBuybackPercent.mul(contractTokenBalance);
// capture the contract's current ETH balance.
// this is so that we can capture exactly the amount of ETH that the
// swap creates, and not make the liquidity event include any ETH that
// has been manually sent to the contract
uint256 initialBalance = address(this).balance;
swapTokensForBNB(marketingQuota);
swapBNBForBUSD(address(this).balance);
transferOutBUSD(marketingWallet, address(this).balance.sub(initialBalance));
//transferOutBNB(marketingWallet, address(this).balance.sub(initialBalance));
uint256 initialBalance2 = address(this).balance;
swapTokensForBNB(whaleBuybackQuota);
transferOutBUSD(whaleBuybackWallet, address(this).balance.sub(initialBalance2));
//transferOutBNB(whaleBuybackWallet, address(this).balance.sub(initialBalance2));
// split the contract balance into halves
uint256 initialBalanceAfterUtility = address(this).balance;
uint256 half = initialBalanceAfterUtility.div(2);
uint256 otherHalf = initialBalanceAfterUtility.sub(half);
swapTokensForBNB(half);
swapBNBForBUSD(address(this).balance);
uint256 newBalance = address(this).balance.sub(initialBalanceAfterUtility);
addLiquidity(otherHalf, newBalance);
emit SwapAndLiquify(half, newBalance, otherHalf);
}
function swapTokensForBNB(uint256 tokenAmount) private {
// generate the pancake pair path of token -> wbnb
address[] memory path = new address[](2);
path[0] = address(this);
path[1] = pancakeRouter.WETH();
_approve(address(this), address(pancakeRouter), tokenAmount);
// make the swap
pancakeRouter.swapExactTokensForETHSupportingFeeOnTransferTokens(
tokenAmount,
0, // accept any amount of BNB
path,
address(this),
block.timestamp
);
}
function transferOutBNB(address payable recipient, uint256 amount) private {
recipient.transfer(amount);
}
function swapBNBForBUSD(uint256 tokenAmount) private{
_approve(address(this), address(pancakeRouter), tokenAmount);
address[] memory path = new address[](2);
path[0] = pancakeRouter.WETH();
path[1] = BUSD; //pancakeRouter.BUSD();
pancakeRouter.swapExactETHForTokensSupportingFeeOnTransferTokens(
tokenAmount,
path,
address(this),
block.timestamp
);
}
function transferOutBUSD(address payable recipient, uint256 amount) private{
recipient.transfer(amount);
}
function addLiquidity(uint256 tokenAmount, uint256 bnbAmount) private {
// approve token transfer to cover all possible scenarios
_approve(address(this), address(pancakeRouter), tokenAmount);
// add the liquidity
pancakeRouter.addLiquidity( // the return values of function not will are handled
address(this),
BUSD,
tokenAmount,
bnbAmount,
0,
0,
owner(),
block.timestamp
);
}
Instead of this,
//BUSD Contract Address
address constant public BUSD = 0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56;
Try this,
IERC20 public immutable BUSD =
IERC20(0x78867BbEeF44f2326bF8DDd1941a4439382EF2A7);
a) For constant variables, the value has to be fixed at compile-time
b) For immutable, the value can be assigned at construction time.