I've tried myself to implement token swap contract from https://solidity-by-example.org/app/erc20/. And I have some questions about it.
contract TokenSwap {
IERC20 public token1;
address public owner1;
uint public amount1;
IERC20 public token2;
address public owner2;
uint public amount2;
constructor(
address _token1,
address _owner1,
uint _amount1,
address _token2,
address _owner2,
uint _amount2
) {
token1 = IERC20(_token1);
owner1 = _owner1;
amount1 = _amount1;
token2 = IERC20(_token2);
owner2 = _owner2;
amount2 = _amount2;
}
_safeTransferFrom
instead of using just transferFrom
function inside the swap
function. What is the advantage of this? function swap() public {
require(msg.sender == owner1 || msg.sender == owner2, "Not authorized");
require(
token1.allowance(owner1, address(this)) >= amount1,
"Token 1 allowance too low"
);
require(
token2.allowance(owner2, address(this)) >= amount2,
"Token 2 allowance too low"
);
_safeTransferFrom(token1, owner1, owner2, amount1);
_safeTransferFrom(token2, owner2, owner1, amount2);
}
function _safeTransferFrom(
IERC20 token,
address sender,
address recipient,
uint amount
) private {
bool sent = token.transferFrom(sender, recipient, amount);
require(sent, "Token transfer failed");
}
1
You can declare and assign variables inside the constructor, they will just be local and only accessible inside the constructor after they have been declared. Please provide the exact attempt you made so I can see what was wrong.
2
A contract's interface provides all the information needed to interact with that contract. Specifically, what is needed is the function signatures of the contracts external/public functions. This is used to encode the calldata that is sent to the contract in order to call a specific function, and is completely obtainable from the contract's interface. See https://docs.soliditylang.org/en/v0.8.11/abi-spec.html for more details.
3
I am not too sure about the purpose of this new function. transferFrom
and its subsequent _transfer
call (see source code at https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol) contain require
statements that check the validity of the transfer, i.e. whether the spenders allowance is large enough and whether the actual token owner (the one's whose tokens are being spent) has the required tokens. Unless I have missed something, these checks determine whether a transaction is valid or not. As such, either the overall transaction will revert, due to one of these require
statements having a false condition, or it will suceed. This makes the additional require(sent, "Token transfer failed")
redundant, as the only time it will be called is when the transfer is a sucess.
Note: This method could be useful if you are using the address
type's call
or delegatecall
methods to call the function. This is because execeptions in this subcalls do not bubble up to the calling contract, so you could use the returned boolean to check if transfer was successful. See Error handling: Assert, Require, Revert and Exceptions section of https://docs.soliditylang.org/en/v0.8.11/control-structures.html for more detail on this.