Skip to content

Commit

Permalink
allow transfer and call to EOA
Browse files Browse the repository at this point in the history
  • Loading branch information
Amxx committed Jun 16, 2022
1 parent 1ec46cf commit c24145f
Show file tree
Hide file tree
Showing 3 changed files with 308 additions and 184 deletions.
6 changes: 4 additions & 2 deletions contracts/mocks/ERC1363Mock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ contract ERC1363ReceiverMock is IERC1363Receiver, IERC1363Spender {
if (data.length == 1) {
if (data[0] == 0x00) return bytes4(0);
if (data[0] == 0x01) revert("onTransferReceived revert");
if (data[0] == 0x02) assert(false);
if (data[0] == 0x02) revert();
if (data[0] == 0x03) assert(false);
}
emit TransferReceived(operator, from, value, data);
return this.onTransferReceived.selector;
Expand All @@ -50,7 +51,8 @@ contract ERC1363ReceiverMock is IERC1363Receiver, IERC1363Spender {
if (data.length == 1) {
if (data[0] == 0x00) return bytes4(0);
if (data[0] == 0x01) revert("onApprovalReceived revert");
if (data[0] == 0x02) assert(false);
if (data[0] == 0x02) revert();
if (data[0] == 0x03) assert(false);
}
emit ApprovalReceived(owner, value, data);
return this.onApprovalReceived.selector;
Expand Down
122 changes: 92 additions & 30 deletions contracts/token/ERC20/extensions/ERC1363.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,35 +6,44 @@ import "../../../interfaces/IERC1363.sol";
import "../../../interfaces/IERC1363Receiver.sol";
import "../../../interfaces/IERC1363Spender.sol";
import "../../../utils/introspection/ERC165.sol";
import "../../../utils/Address.sol";

abstract contract ERC1363 is IERC1363, ERC20, ERC165 {
using Address for address;

/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC165) returns (bool) {
return interfaceId == type(IERC1363).interfaceId || super.supportsInterface(interfaceId);
}

/**
* @dev See {IERC1363-transferAndCall}.
*/
function transferAndCall(address to, uint256 value) public override returns (bool) {
return transferAndCall(to, value, bytes(""));
}

/**
* @dev See {IERC1363-transferAndCall}.
*/
function transferAndCall(
address to,
uint256 value,
bytes memory data
) public override returns (bool) {
require(transfer(to, value));
try IERC1363Receiver(to).onTransferReceived(_msgSender(), _msgSender(), value, data) returns (bytes4 selector) {
require(
selector == IERC1363Receiver(to).onTransferReceived.selector,
"ERC1363: onTransferReceived invalid result"
);
} catch Error(string memory reason) {
revert(reason);
} catch {
revert("ERC1363: onTransferReceived reverted without reason");
}
require(
_checkOnTransferReceived(_msgSender(), to, value, data),
"ERC1363: transfer to non ERC1363Receiver implementer"
);
return true;
}

/**
* @dev See {IERC1363-transferFromAndCall}.
*/
function transferFromAndCall(
address from,
address to,
Expand All @@ -43,46 +52,99 @@ abstract contract ERC1363 is IERC1363, ERC20, ERC165 {
return transferFromAndCall(from, to, value, bytes(""));
}

/**
* @dev See {IERC1363-transferFromAndCall}.
*/
function transferFromAndCall(
address from,
address to,
uint256 value,
bytes memory data
) public override returns (bool) {
require(transferFrom(from, to, value));
try IERC1363Receiver(to).onTransferReceived(_msgSender(), from, value, data) returns (bytes4 selector) {
require(
selector == IERC1363Receiver(to).onTransferReceived.selector,
"ERC1363: onTransferReceived invalid result"
);
} catch Error(string memory reason) {
revert(reason);
} catch {
revert("ERC1363: onTransferReceived reverted without reason");
}
require(
_checkOnTransferReceived(from, to, value, data),
"ERC1363: transfer to non ERC1363Receiver implementer"
);
return true;
}

/**
* @dev See {IERC1363-approveAndCall}.
*/
function approveAndCall(address spender, uint256 value) public override returns (bool) {
return approveAndCall(spender, value, bytes(""));
}

/**
* @dev See {IERC1363-approveAndCall}.
*/
function approveAndCall(
address spender,
uint256 value,
bytes memory data
) public override returns (bool) {
require(approve(spender, value));
try IERC1363Spender(spender).onApprovalReceived(_msgSender(), value, data) returns (bytes4 selector) {
require(
selector == IERC1363Spender(spender).onApprovalReceived.selector,
"ERC1363: onApprovalReceived invalid result"
);
} catch Error(string memory reason) {
revert(reason);
} catch {
revert("ERC1363: onApprovalReceived reverted without reason");
}
require(
_checkOnApprovalReceived(_msgSender(), spender, value, data),
"ERC1363: transfer to non ERC1363Spender implementer"
);
return true;
}

/**
* @dev Internal function to invoke {IERC1363Receiver-onTransferReceived} on a target address.
* The call is not executed if the target address is not a contract.
*/
function _checkOnTransferReceived(
address from,
address to,
uint256 value,
bytes memory data
) private returns (bool) {
if (to.isContract()) {
try IERC1363Receiver(to).onTransferReceived(_msgSender(), from, value, data) returns (bytes4 retval) {
return retval == IERC1363Receiver.onTransferReceived.selector;
} catch (bytes memory reason) {
if (reason.length == 0) {
revert("ERC1363: transfer to non ERC1363Receiver implementer");
} else {
/// @solidity memory-safe-assembly
assembly {
revert(add(32, reason), mload(reason))
}
}
}
} else {
return true;
}
}

/**
* @dev Internal function to invoke {IERC1363Spender-onApprovalReceived} on a target address.
* The call is not executed if the target address is not a contract.
*/
function _checkOnApprovalReceived(
address owner,
address spender,
uint256 value,
bytes memory data
) private returns (bool) {
if (spender.isContract()) {
try IERC1363Spender(spender).onApprovalReceived(owner, value, data) returns (bytes4 retval) {
return retval == IERC1363Spender.onApprovalReceived.selector;
} catch (bytes memory reason) {
if (reason.length == 0) {
revert("ERC1363: transfer to non ERC1363Spender implementer");
} else {
/// @solidity memory-safe-assembly
assembly {
revert(add(32, reason), mload(reason))
}
}
}
} else {
return true;
}
}
}
Loading

1 comment on commit c24145f

@lukehutch
Copy link

@lukehutch lukehutch commented on c24145f Jun 16, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Amxx The author of the ERC1363 standard, @vittominacori, just said, at your invitation:

As ERC1363 was built on top of ERC20 I couldn't change the standard ERC20 transfer to add bytes data and I also didn't want to overload method signatures. So I added the *AndCall methods just to be used with contracts. If we want to transfer to EOA, or to a contract that does nothing, we can use the standard ERC20 transfer.

So the OpenZeppelin implementation should do the same thing, and refuse to send to EOA wallets using transferAndCall, and should refuse to allow approval of EOA wallets to spend using approveAndCall.

Please sign in to comment.