solidity ERC20 transferFrom gives no error but does not execute - solidity

I have a Solidity code that aims to Exchange BUSD with myToken (both ERC20) which calls a BUSD.transferFrom() and a myToken.transfer(), but despite the core executes with no errors only myToken is transferred.
The account is approved and has enough balance.
Can you please point the error?
bytes4 private constant SELECTOR_TRANSFER = bytes4(keccak256(bytes('transfer(address,uint256)')));
bytes4 private constant SELECTOR_TRANSFERFROM = bytes4(keccak256(bytes('transferFrom(address,address,uint256)')));
function _myTokenTransfer(uint256 amount) private {
_safeTransfer(myToken_addr, _msgSender(), amount);
}
function _busdTransfer(uint256 amount) private {
_safeTransferFrom(busd_addr, _msgSender(), address(this), amount);
}
function _safeTransferFrom(address token, address from, address to, uint value) private {
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(SELECTOR_TRANSFERFROM, from , to, value));
require(success && (data.length == 0 || abi.decode(data, (bool))), 'myTokenPrivateSale: TRANSFERFROM_FAILED');
}
function _safeTransfer(address token, address to, uint value) private {
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(SELECTOR_TRANSFER, to, value));
require(success && (data.length == 0 || abi.decode(data, (bool))), 'myTokenPrivateSale: TRANSFER_FAILED');
}
}

Your busd_addr is unset (i.e. 0x0).
When the _safeTransferFrom() function gets executed, it sends an internal transaction to the 0x0 address. The transaction doesn't revert (there's no contract that would throw an exception on the 0x0 address) and doesn't return any data (again, no contract that would return any data).
(bool success, bytes memory data)
// `success` is true because it didn't revert
// `data` is 0x00, the default value
The validation then passes as if it were sucessful.
require(success && (data.length == 0 || ...));
// `success` is true
// `data.length` is 0, so the rest of the OR condition is ignored
I see that you executed the setBUSD() function. But this function doesn't set the busd_addr value, that is used in the _safeTransferFrom() function. (It only sets the busd and busd_set.)
There's currently no way to set the busd_addr value in your code. So you'll need to make the proper changes in your code - set busd_addr value in the setBUSD() function, and then redeploy your contract.

Related

Implementing a Tax Feature in an ERC20 token

Good day Everyone, I am trying to implement a tax feature in an ERC20 token.
I successfully implemented something similar to a project that involved the creation of a TRC20 token using the code below as a reference:
pragma solidity ^0.6.0;
contract TransfertTokenAndPercentageToTargetAddress{
// pay 1% of all transactions to target address
address payable target = TUEtmARJ2m147RDJ5hy37mhZhEqx2Fnv8r; // I converted the tron address, I didn't use it as shown
// state variables for your token to track balances and to test
mapping (address => uint) public balanceOf;
uint public totalSupply;
// create a token and assign all the tokens to the creator to test
constructor(uint _totalSupply) public {
totalSupply = _totalSupply;
balanceOf[msg.sender] = totalSupply;
}
// the token transfer function with the addition of a 1% share that
// goes to the target address specified above
function transfer(address _to, uint amount) public {
// calculate the share of tokens for your target address
uint shareForX = amount/100;
// save the previous balance of the sender for later assertion
// verify that all works as intended
uint senderBalance = balanceOf[msg.sender];
// check the sender actually has enough tokens to transfer with function
// modifier
require(senderBalance >= amount, 'Not enough balance');
// reduce senders balance first to prevent the sender from sending more
// than he owns by submitting multiple transactions
balanceOf[msg.sender] -= amount;
// store the previous balance of the receiver for later assertion
// verify that all works as intended
uint receiverBalance = balanceOf[_to];
// add the amount of tokens to the receiver but deduct the share for the
// target address
balanceOf[_to] += amount-shareForX;
// add the share to the target address
balanceOf[target] += shareForX;
// check that everything works as intended, specifically checking that
// the sum of tokens in all accounts is the same before and after
// the transaction.
assert(balanceOf[msg.sender] + balanceOf[_to] + shareForX ==
senderBalance + receiverBalance);
}
}
Then I tried to follow the same approach for an ERC20 token using the template from openzeppelin: https://github.com/OpenZeppelin/openzeppelin-contracts/tree/master/contracts/token/ERC20
The following code shows the result of the modification:
function _transfer(
address from,
address to,
uint256 amount
) internal virtual {
require(from != address(0), "ERC20: transfer from the zero address");
require(to != address(0), "ERC20: transfer to the zero address");
_beforeTokenTransfer(from, to, amount);
uint tax = amount/10;
uint256 fromBalance = _balances[from];
require(fromBalance >= amount + tax, "ERC20: transfer amount and Tax exceeds balance");
unchecked {
_balances[from] = fromBalance - amount - tax;
}
uint receiverBalance = _balances[to];
_balances[to] += amount;
_balances[target] += tax;
emit Transfer(from, to, amount);
_afterTokenTransfer(from, to, amount);
// assert(_balances[from] + _balances[to] + tax == fromBalance + receiverBalance);
}
The token was successfully minted but the transfer function won't work. Any suggestions are highly appreciated.

'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!
}
}

Swap ETH to DAI using ISwapRouter from Uniswap

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 {}
}

Solidity variable definition: (bool sent, )

In Solidity, this sentence:
(bool sent, ) = msg.sender.call{value: _amount}("");
What is the sense of that "," after sent variable?
Any link to offcial documentation?
Many thanks.
It means you do not need to define both the variables to access the return values. You can access either of these variables as follows
(,uint256 alpha) = testFunction(); //access first returned variable
(uint256 beta,) = testFunction(); //access second returned variable
function testFunction() public pure returns(uint256,uint256){
return (1,2);
}
Solidity functions can return multiple variables of different types.
If you only want to keep one variable, then you can declare a variable and then use commas:
function multiValueFunction() public returns (bool, string memory, uint[] memory, uint {
//do something
return (true, "New String", [1,2], 21)
}
function differentFunction() public {
uint numberToKeep;
(,,,numberToKeep) = multiValueFunction();
}
Expected: numberToKeep = 21
Each comma represents the place of a returned variable (that is not kept)
It just means that the function will return two variables and you just want to store the first one.
This kind of syntax (variable type) is called Tuple In Python and other languages.
In Solidity,
they're not considered a proper type but can be used as a shorthand.
See the Official Docs: Destructuring Assignments and Returning Multiple Values
example:
uint256 amount = 10 ether;
require(
amount <= address(this).balance,
"Trying to withdraw more money than the contract has."
);
(bool success, ) = (msg.sender).call{value: amount}("");
require(success, "Failed to withdraw money from contract.");
above code, Roughly, Can be translated with basic if else statements like following:
uint256 amount = 10 ether;
bool success;
if(amount <= address(this).balance) {
success = true;
} else {
success = false;
return "Trying to withdraw more money than the contract has.";
}
if(success) {
(msg.sender).call{value: amount}("")
} else {
return "Failed to withdraw money from contract.";
}
here's another contract as example,
use this link https://remix.ethereum.org/#version=soljson-v0.6.9+commit.3e3065ac.js&optimize=false&gist=83bfbd85ef79387f760154999eb4f192&runs=200&evmVersion=null to play around in remix online
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.5.0 <0.8.0;
contract myContract {
uint index;
function myFunction() public {
( , , uint256 standardDepositAmount, , ) = returnTuple();
require(standardDepositAmount == 3);
// do something...
}
function returnTuple() public pure returns (uint, uint, uint, uint, uint) {
return (1, 2, 3, 4, 5);
}
}

Ethereum contract, running a function corrupts contract members

I made a simple contract that stores ether and then can send ether. The function that sends ether has a requirement that only the owner of the contract can send ether from the contract.
The contract mysteriously fails to send ether every subsequent call after the first.
I created a function to retrieve the owner address value in the contract and it turns out that after the first function call, it changes the data to 0x000000000000000000000000000000000000000a
Sending function:
function SendToAddress (uint8 amt, address adr) isOwner {
/* Have we transferred over the maximum amount in
the last 24 hours? */
if ((now - dayStartTime) >= secondsInADay) {
dayStartTime = now;
curDayTransfer = 0;
}
if ((curDayTransfer + amt) < dayMaxTransfer) {
adr.transfer (amt);
walletBalance -= amt;
curDayTransfer += amt;
MoneyTransfer newTransfer;
newTransfer.amount = amt;
newTransfer.target = adr;
newTransfer.timeStamp = now;
if (transferHistory.length == 100) {
// Shift all of the transactions in the history list forward
// to make space for the transaction.
for (uint8 i = 1; i < 100; i++) {
transferHistory[i] = transferHistory[i-1];
}
transferHistory[0] = newTransfer;
} else {
transferHistory.push (newTransfer);
}
}
}
isOwner modifier:
modifier isOwner() {
require(msg.sender == creatorAddress);
_;
}
constructor:
constructor () public {
creatorAddress = msg.sender;
}
I assume the compiler gives you a warning on the line MoneyTransfer newTransfer; about implicitly storing the data in storage. If you explicitly use MoneyTransfer storage newTransfer;, then you'll get a warning that you're using an uninitialized storage reference. That means whatever values you put in newTransfer will overwrite whatever's in the first few storage slots.
Use MoneyTransfer memory newTransfer; instead.