Skip to content

Instantly share code, notes, and snippets.

@irzhywau
Last active March 27, 2022 18:18
Show Gist options
  • Save irzhywau/b1257353a2dabbf3504043d2cb52038f to your computer and use it in GitHub Desktop.
Save irzhywau/b1257353a2dabbf3504043d2cb52038f to your computer and use it in GitHub Desktop.
wrap/unwrap token inside of a contract
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.12;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "./IERC20Wrappable.sol";
contract ERC20WrappableSupport {
modifier withWrap(address payable wToken) {
require(_bundleWrap(wToken), "failed to wrap");
_;
}
modifier withUnwrap(address payable wToken, uint256 amount) {
_;
_unwrap(wToken, amount);
}
modifier _underlyingTokenRequired(address payable wToken) {
require(wToken != address(0), "underlying token not set");
_;
}
/// @dev execute wrap transaction along the process and transfer wETH into original msg.sender account
function _bundleWrap(address payable wToken)
internal
_underlyingTokenRequired(wToken)
returns (bool)
{
// Note:
// There exists a special variant of a message `call`,
// named `delegatecall` which is identical to a message call
// apart from the fact that the code at the target address
// is executed in the context of the calling contract
// and msg.sender and msg.value do not change their values.
(bool deposited, ) = wToken.call{value: msg.value}(
abi.encodeWithSignature("deposit()")
);
require(deposited, "failed to deposit");
IERC20(wToken).transferFrom(address(this), msg.sender, msg.value);
// @todo: figure out how to bundle this in this method
// (bool approved, ) = wToken.delegatecall(
// abi.encodeWithSignature("approve(address,uint256)", this, amount)
// );
// require(approved, "failed to approve");
return true;
}
function _unwrap(address payable wToken, uint256 wad)
internal
_underlyingTokenRequired(wToken)
returns (bool)
{
// this statement needs an approval in prior
// transfer fund from user account to the current contract
require(
IERC20(wToken).transferFrom(msg.sender, address(this), wad),
"wERC20: failed to transfer to calling contract"
);
// here we will withdraw transfered wETH in the contract to ETH
// then transfer it again to the recipient (msg.sender)
// execution operated by this contract
IERC20Wrappable(wToken).withdraw(wad);
(bool withdrawn, ) = msg.sender.call{value: wad}("");
return withdrawn;
}
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.12;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
interface IERC20Wrappable is IERC20 {
function deposit() external payable;
function withdraw(uint256 wad) external;
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.12;
import "@openzeppelin/contracts/access/Ownable.sol";
import "../library/ERC20WrappableSupport.sol";
contract TestWrap is Ownable, ERC20WrappableSupport {
address payable public wToken;
function pay() external payable {
require(_bundleWrap(wToken), "failed to bundle actions");
}
function refundMe(uint256 amount) external {
require(_unwrap(wToken, amount), "failed to withdraw");
}
function setWToken(address _wToken) public onlyOwner {
wToken = payable(_wToken);
}
}
@codemaestro11
Copy link

codemaestro11 commented Mar 27, 2022

receive() external payable {
        _fallback();
    }

This function means that the contract can recieve ether from ourside contract or account.
If there is no such function, we can't recieve ether from outside.

@irzhywau
Copy link
Author

got it.

thank you @alexlu0917 your help just unlocked me from a week of struggle

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment