Created
August 18, 2022 05:06
-
-
Save z0r0z/ec346ab5c51e246870168e24c02085f7 to your computer and use it in GitHub Desktop.
Turns any token into a permittable ERC-1155 token.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: MIT | |
pragma solidity ^0.8.0; | |
import "@openzeppelin/contracts/interfaces/IERC20.sol"; | |
import "@openzeppelin/contracts/interfaces/IERC721.sol"; | |
import "@openzeppelin/contracts/interfaces/IERC721Receiver.sol"; | |
import "@openzeppelin/contracts/interfaces/IERC1155.sol"; | |
import "@openzeppelin/contracts/interfaces/IERC1155Receiver.sol"; | |
import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol"; | |
import "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol"; | |
import "@openzeppelin/contracts/utils/cryptography/draft-EIP712.sol"; | |
/// @notice Turns any token into a permittable ERC-1155 token. | |
/// @author Modified from @amxx (https://gist.github.com/Amxx/90e760a4bc98fea87b5b78903b7d584c) | |
/// @author and Primitive (https://github.com/primitivefinance/rmm-manager/blob/main/contracts/base/ERC1155Permit.sol) | |
/// @author by z0r0z.eth | |
contract UniversalWrapper is | |
ERC1155(""), | |
EIP712("UniversalWrapper", "1"), | |
IERC721Receiver, | |
IERC1155Receiver | |
{ | |
mapping(address => uint256) public nonces; | |
bytes32 private immutable _PERMIT_TYPEHASH = | |
keccak256("Permit(address owner,address operator,bool approved,uint256 nonce,uint256 deadline)"); | |
function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC1155) returns (bool) { | |
return interfaceId == type(IERC721Receiver).interfaceId | |
|| interfaceId == type(IERC1155Receiver).interfaceId | |
|| super.supportsInterface(interfaceId); | |
} | |
/** | |
* ERC20 | |
*/ | |
function wrapERC20(IERC20 instance, uint256 amount, address to) | |
public | |
{ | |
wrapERC20(instance, amount, to, bytes("")); | |
} | |
function wrapERC20(IERC20 instance, uint256 amount, address to, bytes memory data) | |
public | |
{ | |
// check that we are not trying to hide an ERC721 transfer and get multiple wrapped instances. | |
require(!ERC165Checker.supportsInterface(address(instance), type(IERC721).interfaceId)); | |
instance.transferFrom(msg.sender, address(this), amount); | |
_mint(to, getTokenId(address(instance), 0), amount, data); | |
} | |
function unwrapERC20(IERC20 instance, uint256 amount, address from, address to) | |
public | |
{ | |
require(from == msg.sender || isApprovedForAll(from, msg.sender)); | |
_burn(from, getTokenId(address(instance), 0), amount); | |
instance.transfer(to, amount); | |
} | |
/** | |
* ERC721 | |
*/ | |
function onERC721Received(address /* operator */, address /* from */, uint256 tokenId, bytes calldata data) | |
public | |
override | |
returns (bytes4) | |
{ | |
address to = abi.decode(data, (address)); | |
_mint(to, getTokenId(msg.sender, tokenId), 1, bytes("")); | |
return IERC721Receiver.onERC721Received.selector; | |
} | |
function unwrapERC721(IERC721 instance, uint256 tokenId, address from, address to) | |
public | |
{ | |
unwrapERC721(instance, tokenId, from, to, bytes("")); | |
} | |
function unwrapERC721(IERC721 instance, uint256 tokenId, address from, address to, bytes memory data) | |
public | |
{ | |
require(from == msg.sender || isApprovedForAll(from, msg.sender)); | |
_burn(from, getTokenId(address(instance), tokenId), 1); | |
instance.safeTransferFrom(address(this), to, tokenId, data); | |
} | |
/** | |
* ERC1155 | |
*/ | |
function onERC1155Received(address /* operator */, address /* from */, uint256 tokenId, uint256 value, bytes calldata data) | |
public | |
override | |
returns (bytes4) | |
{ | |
address to = abi.decode(data, (address)); | |
_mint(to, getTokenId(msg.sender, tokenId), value, bytes("")); | |
return IERC1155Receiver.onERC1155Received.selector; | |
} | |
function onERC1155BatchReceived(address /* operator */, address /* from */, uint256[] calldata tokenIds, uint256[] calldata values, bytes calldata data) | |
public | |
override | |
returns (bytes4) | |
{ | |
address to = abi.decode(data, (address)); | |
uint256[] memory ids = new uint256[](tokenIds.length); | |
for (uint256 i = 0; i < tokenIds.length; ++i) { | |
ids[i] = getTokenId(msg.sender, tokenIds[i]); | |
} | |
_mintBatch(to, ids, values, bytes("")); | |
return IERC1155Receiver.onERC1155Received.selector; | |
} | |
function unwrapERC1155(IERC1155 instance, uint256 tokenId, uint256 amount, address from, address to) | |
public | |
{ | |
unwrapERC1155(instance, tokenId, amount, from, to, bytes("")); | |
} | |
function unwrapERC1155(IERC1155 instance, uint256 tokenId, uint256 amount, address from, address to, bytes memory data) | |
public | |
{ | |
require(from == msg.sender || isApprovedForAll(from, msg.sender)); | |
_burn(from, getTokenId(address(instance), tokenId), amount); | |
instance.safeTransferFrom(address(this), to, tokenId, amount, data); | |
} | |
function unwrapERC1155Batch(IERC1155 instance, uint256[] memory tokenIds, uint256[] memory amounts, address from, address to) | |
public | |
{ | |
unwrapERC1155Batch(instance, tokenIds, amounts, from, to, bytes("")); | |
} | |
function unwrapERC1155Batch(IERC1155 instance, uint256[] memory tokenIds, uint256[] memory amounts, address from, address to, bytes memory data) | |
public | |
{ | |
require(from == msg.sender || isApprovedForAll(from, msg.sender)); | |
uint256[] memory ids = new uint256[](tokenIds.length); | |
for (uint256 i = 0; i < tokenIds.length; ++i) { | |
ids[i] = getTokenId(address(instance), tokenIds[i]); | |
} | |
_burnBatch(from, ids, amounts); | |
instance.safeBatchTransferFrom(address(this), to, ids, amounts, data); | |
} | |
/** | |
* Utils | |
*/ | |
function getTokenId(address instance, uint256 tokenId) public pure returns (uint256) { | |
return uint256(keccak256(abi.encode(instance, tokenId))); | |
} | |
function permit( | |
address owner, | |
address operator, | |
bool approved, | |
uint256 deadline, | |
uint8 v, | |
bytes32 r, | |
bytes32 s | |
) public { | |
require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED"); | |
// Unchecked because the only math done is incrementing | |
// the owner's nonce which cannot realistically overflow. | |
unchecked { | |
bytes32 structHash = keccak256( | |
abi.encode(_PERMIT_TYPEHASH, owner, operator, approved, nonces[owner]++, deadline) | |
); | |
bytes32 hash = _hashTypedDataV4(structHash); | |
address recoveredAddress = ECDSA.recover(hash, v, r, s); | |
require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER"); | |
} | |
_setApprovalForAll(owner, operator, approved); | |
} | |
function DOMAIN_SEPARATOR() public view returns (bytes32) { | |
return _domainSeparatorV4(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment