I would like to make a function for receiving ERC20 in contract and after receiving ERC20 token it should transfer that ERC20 to another wallet.
the flow should be if a user uses that function first it should send that ERC20 to the contract and after that contract should forward that token to another wallet. I don't know where to start from
example transaction is this:
https://polygonscan.com/tx/0x88d85e4b746b65708a38b8f4c5d5bc0f73ff78e28868084eed565976b46df10e
The ERC-20 standard doesn't define how to notify a receiver contract about the incoming transfer. So you'll need to use either another standard (e.g. ERC-777) or build a custom notification hook.
Here's an example of such custom notification. It builds on top of the OpenZeppelin ERC-20 implementation, checks if the receiver is a contract - and if it is a contract, tries to call its onERC20Receive() function.
You can test it by deploying two separate contracts - MyToken and SomeReceiver - and then sending tokens from the deployer address to SomeReceiver. You can see that the ReceivedTokens event was emitted, as a result of invoking the function onERC20Receive when SomeReceiver received the tokens.
pragma solidity ^0.8.16;
import "#openzeppelin/contracts/token/ERC20/ERC20.sol";
contract MyToken is ERC20 {
constructor() ERC20("MyToken", "MyT") {
_mint(msg.sender, 1000 * 1e18);
}
function _afterTokenTransfer(address from, address to, uint256 amount) internal override {
if (to.code.length > 0) {
// token recipient is a contract, notify them
try IERC20Receiver(to).onERC20Receive(from, amount) returns (bool success) {
// the recipient returned a bool, TODO validate if they returned true
} catch {
// the notification failed (maybe they don't implement the `IERC20Receiver` interface?)
}
}
}
}
interface IERC20Receiver {
function onERC20Receive(address from, uint256 amount) external returns (bool);
}
contract SomeReceiver is IERC20Receiver {
event ReceivedTokens(address from, uint256 amount);
function onERC20Receive(address from, uint256 amount) external returns (bool) {
emit ReceivedTokens(from, amount);
return true;
}
}
Related
I want to create a payable token
which includes a function transferAndCall(TokenReceiver to, uint256 amount, bytes4 selector).
By calling this function, you can transfer tokens to the TokenReceiver smart contract address,
and then call onTransferReceived(address from,uint tokensPaid, bytes4 selector) on the receiver,
which in turn invokes a function specified in thebytes4 selector on the receiver.
Note that this is similar to/ inspired by ERC1363.
Here is a simplified version of my receivable token:
import "#openzeppelin/contracts/token/ERC20/ERC20.sol";
contract MeowToken is ERC20 {
constructor() ERC20("MeowToken", "MEO") {
ERC20._mint(msg.sender, 10_000_000);
}
function transferAndCall(
TokenReceiver to,
uint256 amount,
bytes4 selector
) external {
ERC20.transfer(address(to), amount);
to.onTransferReceived(msg.sender, amount, selector);
}
}
And this is a token receiver:
contract TokenReceiver {
address acceptedToken;
event PurchaseMade(address from, uint tokensPaid);
modifier acceptedTokenOnly () {
require(msg.sender == address(acceptedToken), "Should be called only via the accepted token");
_;
}
constructor(address _acceptedToken) {
acceptedToken = _acceptedToken;
}
function onTransferReceived(
address from,
uint tokensPaid,
bytes4 selector
) public acceptedTokenOnly {
(bool success,) = address(this).call(abi.encodeWithSelector(selector, from, tokensPaid));
require(success, "Function call failed");
}
function purchase(address from, uint tokensPaid) public acceptedTokenOnly {
emit PurchaseMade(from, tokensPaid);
}
}
I want to make sure that public functions on the receiver are only called via the payable token.
For this reason I added acceptedTokenOnly modifier to both of them.
However after adding the modifier my test began to fail:
it('Transfer Tokens and call Purchase', async () => {
const tokenAmount = 100;
const tx = meowToken.transferAndCall(
tokenReceiver.address,
tokenAmount,
tokenReceiver.interface.getSighash('purchase'),
);
await expect(tx)
.to.emit(tokenReceiver, 'PurchaseMade')
.withArgs(deployer.address, tokenAmount);
});
1) Transfer and call
Transfer Tokens and call Purchase:
Error: VM Exception while processing transaction: reverted with reason string 'Function call failed'
Why does this happen?
How to make sure the receiver's functions are invoked only by the accepted token?
For reference, I am developing and testing smart contracts in Hardhat and deploying on RSK.
When you're doing this:
(bool success,) = address(this).call(abi.encodeWithSelector(selector, from, tokensPaid));
you're making an external call, meaning that msg.sender will become address(this).
Now the modifier acceptedTokenOnly during function purchase will fail since msg.sender isn't the token anymore.
Suggested changing the function to this:
function purchase(address from, uint tokensPaid) public {
require(msg.sender == address(this), "wrong sender");
emit PurchaseMade(from, tokensPaid);
}
The problem is, you are using low level call method, here:
(bool success,) = address(this).call(abi.encodeWithSelector(selector, from, tokensPaid));
This changes the value of msg.sender inside onTransferReceived from the accepted token to the receiver itself.
Here is one way to achieve what you want:
Replace call with delegatecall.
This will solve your problem instantly.
Unlike call, the delegatecall will invoke your function on behalf of the caller smart contract:
function onTransferReceived(
address from,
uint tokensPaid,
bytes4 selector
) public acceptedTokenOnly {
(bool success,) = address(this).delegatecall(abi.encodeWithSelector(selector, from, tokensPaid));
require(success, "Function call failed");
}
Apart from switching from call to delegatecall, as mentioned in #Juan's answer, there is a more "manual" approach:
Do not use call altogether, and instead invoke the functions by name.
This can be accomplished using an if ... else control structure that compares the selector parameter with the intended function selector (purchase):
function onTransferReceived(
address from,
uint tokensPaid,
bytes4 selector
) public acceptedTokenOnly {
if (selector == this.purchase.selector) {
purchase(from, tokensPaid);
} else {
revert("Call of an unknown function");
}
}
While this is more tedious to do, it might be preferable from a security point of view.
For example, if you wish to white-list the functions that you allow to be called through
this mechanism.
Note that the approach using call/ delegatecall exposes a potential vulnerability
for arbitrary (and possibly unintended) function execution.
I create ERC20 tokens, and i want to transfer my tokens to another address.
I have two accounts in my metamask.(Account A/B)
My ERC20 code's here (I deployed and save tokens in account A)
pragma solidity ^0.8.0;
// SPDX-License-Identifier: MIT
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol";
contract MyToken is ERC20 {
constructor(string memory name, string memory symbol) ERC20(name,symbol) {
// mint 1000 token
_mint(msg.sender, 1000*10**uint(decimals()));
}
}
Question : how can I transfer my ERC20 tokens from the current address to another? (A->B)
I use this code in account A, but not work.
pragma solidity ^0.8.7;
// SPDX-License-Identifier: MIT
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol";
contract TokenTransfer {
IERC20 _token;
// token = MyToken's contract address
constructor(address token) public {
_token = IERC20(token);
}
// to = Account B's address
function stake(address to, uint amount) public {
_token.approve(address(this), amount);
require(_token.allowance(address(this), address(this)) >= amount);
_token.transferFrom(msg.sender, to, amount);
}
}
error message
transact to TokenTransfer.stake errored: Internal JSON-RPC error.
{
"code": 3,
"message": "execution reverted: ERC20: insufficient allowance",
"data": "0x08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001d45524332303a20696e73756666696369656e7420616c6c6f77616e6365000000"
}
how to fix it?
Your logic is wrong!
If you want to send a token from account A to account B without passing from smart contract, you can use 'Send' options available in Metamask and other wallet.
If you want use a smart contract, your logic change.
In your smart contract code, there are some errors:
_token.approve(address(this), amount): when you write this statement, you're approving smart contract itself to move your tokens but it doesn't have any token! Another thing about approve() function. This operation must be do from user, in details a person must to give to smart contract permission accessing about his wallet;
Another error is: smart contract cannot failt to have a function about deposit token to call from account A. In this case, when you write this statament, _token.transferFrom(msg.sender, to, amount);, (msg.sender is smart contract) you cannot transfer any amount to receiver address because smart contract doesn't have the amount of tokens.
Last problem, is when you transfer a token from smart contract to address you must use transfer() function instead transferFrom() because this last function require approve + transfer and smart contracts cannot approve itself. While transfer() is used only transfer funds.
To resolve this problems, you can see this smart contract code:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol";
contract TokenTransfer {
IERC20 _token;
// token = MyToken's contract address
constructor(address token) {
_token = IERC20(token);
}
// Modifier to check token allowance
modifier checkAllowance(uint amount) {
require(_token.allowance(msg.sender, address(this)) >= amount, "Error");
_;
}
// In your case, Account A must to call this function and then deposit an amount of tokens
function depositTokens(uint _amount) public checkAllowance(_amount) {
_token.transferFrom(msg.sender, address(this), _amount);
}
// to = Account B's address
function stake(address to, uint amount) public {
_token.transfer(to, amount);
}
// Allow you to show how many tokens owns this smart contract
function getSmartContractBalance() external view returns(uint) {
return _token.balanceOf(address(this));
}
}
For example I don't want to store ETH on Smart Contract but to the contract owner. Then how to implement withdrawal from the contract owner?
pragma solidity ^0.8.7;
contract WDfromContractOwner {
address public owner;
constructor() {
owner=msg.sender;
}
function deposit() external payable returns (bool) {
payable(owner).transfer(msg.value);
return true;
}
function withdrawal() external returns (bool) {
// Witdrawal from owner address....???
return true;
}
}
A smart contract is not able to pull ETH from an address (other than the address sending the ETH to the contract). The transfer needs to always be originated from and signed by the sender (in your case the owner).
However, it can pull tokens owned by an address. The sender (owner) needs to approve() the amount at first, interacting with the token contract from their address. Then your contract can invoke the token's transferFrom() function.
pragma solidity ^0.8;
interface IERC20 {
function transferFrom(address, address, uint256) external returns (bool);
}
contract WDfromContractOwner {
address public owner;
function withdrawToken() external {
// Only reachable from the mainnet.
// Transfers from other networks (such as Remix VM) will fail.
address mainnetUSDT = 0xdAC17F958D2ee523a2206206994597C13D831ec7;
address receiver = msg.sender; // address of the user executing the `withdrawToken()`
uint256 amount = 5 * 1e6; // 5 USDT, 6 decimals
require(
// the `owner` needs to execute `approve()` on the token contract directly from the `owner` address
// so that the `WDfromContractOwner` contract can spend their tokens
IERC20(mainnetUSDT).transferFrom(owner, receiver, amount)
);
}
}
You can get the approved amount using web3.js:
const USDTAddress = "0xdAC17F958D2ee523a2206206994597C13D831ec7";
const ownerAddress = "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF";
// just the `balanceOf()` is sufficient in this case
const ABI = [
{"constant":true,"inputs":[{"name":"who","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}
];
const USDTContract = new web3.eth.Contract(ABI, USDTAddress);
const approved = await USDTContract.methods.balanceOf(ownerAddress).call();
console.log(approved);
If you are already are transferring the funds to the owner each time an user deposit it should not be necessary, but if you want you could do it anyway, you have different options like passing the amount as a parameter, have a default or a minimum amount, etc, but for simplicity to withdraw all the funds just add this two lines in the function
(bool result,)= payable(owner).call{value: address(this).balance }("");
return result
So I'm trying to test a payable function on the following smart contract here using the truffle framework:
contract FundMe {
using SafeMathChainlink for uint256;
mapping(address => uint256) public addressToAmountFunded;
address[] public funders;
address public owner;
AggregatorV3Interface public priceFeed;
constructor(address _priceFeed) public {
priceFeed = AggregatorV3Interface(_priceFeed);
owner = msg.sender;
}
function fund() public payable {
uint256 mimimumUSD = 50 * 10**18;
require(
getConversionRate(msg.value) >= mimimumUSD,
"You need to spend more ETH!"
);
addressToAmountFunded[msg.sender] += msg.value;
funders.push(msg.sender);
}
I specifically want to test the payable function, and I've seen a few things on the internet where people create other contracts with initial balances and then send their testing contract some eth. But I would just like to grab a local ganache wallet and send some eth to the contract and then test that, if someone could show me some test javascript code to wrap my head around this that would be much appreciated!
For a contract to be able to receive ETH (or any native token - BNB on Binance Smart Chain, TRX on Tron network, ...) without invoking any function, you need to define at least one of these functions receive() (docs) or fallback() (docs).
contract FundMe {
// intentionally missing the `function` keyword
receive() external payable {
// can be empty
}
// ... rest of your code
}
Then you can send a regular transaction to the contract address in truffle (docs):
const instance = await MyContract.at(contractAddress);
await instance.send(web3.toWei(1, "ether"));
Note that because receive() and fallback() are not regular functions, you cannot invoke them using the truffle autogenerated methods: myContract.functionName()
If you want to execute a payable function sending it ETH, you can use the transaction params (docs). It's always the last argument, after all of the regular function arguments.
const instance = await MyContract.at(contractAddress);
await instance.fund({
value: web3.toWei(1, "ether")
});
Note: If the fund() function had 1 argument (let's say a bool), the transaction params would be the 2nd:
await instance.fund(true, {
value: web3.toWei(1, "ether")
});
I wrote the following code. I know it sends the ether from the contract balance but It doesn't allow me to send ether to the contract`s address.
How can I change the method so it will allow sending ether to the contract?
pragma solidity ^0.5.11;
contract MyFirstContract
{
function() external payable { }
function send(address payable _to, uint256 _value) public returns (bool)
{
require(_value <= address(this).balance);
_to.transfer(_value);
return true;
}
}
The reason it doesn't allow you to send ether to a contract's address could be there is no fallback function in that contract.
If you want to send ether to a contract without calling any of its functions, you need to have a fallback function in that contract to receive ether.
Add this function in the contract which you want to send ether to:
function () external payable {}
Here is a more detailed version: fallback