2023-01-29 18:29:54 +00:00
|
|
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
|
|
pragma solidity ^0.8.17;
|
2022-02-11 04:17:00 +00:00
|
|
|
|
|
|
|
import "./ERC1155.sol";
|
2023-01-29 19:46:02 +00:00
|
|
|
import "./IMastersFedi.sol";
|
2022-02-11 04:17:00 +00:00
|
|
|
import "./Address.sol";
|
|
|
|
import "./IERC1155Metadata.sol";
|
2023-01-29 22:22:15 +00:00
|
|
|
import "./ERC1155Metadata.sol";
|
2023-02-05 21:35:43 +00:00
|
|
|
import "./IERC223ReceivingContract.sol";
|
2023-01-29 19:46:02 +00:00
|
|
|
import "@openzeppelin/contracts/utils/Strings.sol";
|
|
|
|
import "@openzeppelin/contracts/utils/Base64.sol";
|
2023-01-29 21:15:20 +00:00
|
|
|
import "@openzeppelin/contracts/access/Ownable.sol";
|
2023-01-29 22:50:26 +00:00
|
|
|
import "@openzeppelin/contracts/security/Pausable.sol";
|
2023-01-30 06:09:51 +00:00
|
|
|
import "@openzeppelin/contracts/token/common/ERC2981.sol";
|
2022-02-11 04:17:00 +00:00
|
|
|
|
2023-02-05 21:35:43 +00:00
|
|
|
contract CurioERC1155Wrapper is ERC1155, ERC1155Metadata_URI, ERC1155Metadata, ERC2981, IERC223ReceivingContract, Ownable, Pausable {
|
2022-02-11 04:17:00 +00:00
|
|
|
using Address for address;
|
|
|
|
|
|
|
|
mapping (uint256 => address) public contracts;
|
2023-01-29 21:15:20 +00:00
|
|
|
mapping (uint256 => string) public urls;
|
2022-02-11 04:17:00 +00:00
|
|
|
|
2023-02-05 21:58:31 +00:00
|
|
|
string public name;
|
|
|
|
string public symbol;
|
2023-01-30 17:47:02 +00:00
|
|
|
|
2023-02-05 21:58:31 +00:00
|
|
|
uint96 internal defaultRoyalty;
|
|
|
|
|
|
|
|
struct InitPair {
|
|
|
|
address token;
|
|
|
|
string url;
|
|
|
|
}
|
2023-01-30 06:09:51 +00:00
|
|
|
|
2022-02-11 04:17:00 +00:00
|
|
|
/**
|
2023-01-29 21:44:25 +00:00
|
|
|
@notice Initialize an nft id's data
|
2022-02-11 04:17:00 +00:00
|
|
|
*/
|
2023-01-29 21:15:20 +00:00
|
|
|
function create(uint256 _id, address _contract, string memory _url) internal {
|
2022-02-11 04:17:00 +00:00
|
|
|
|
|
|
|
require(contracts[_id] == address(0), "id already exists");
|
|
|
|
contracts[_id] = _contract;
|
|
|
|
|
2023-01-29 21:15:20 +00:00
|
|
|
urls[_id] = _url;
|
|
|
|
|
2022-02-11 04:17:00 +00:00
|
|
|
// mint 0 just to let explorers know it exists
|
|
|
|
emit TransferSingle(msg.sender, address(0), msg.sender, _id, 0);
|
|
|
|
}
|
|
|
|
|
2023-02-05 21:58:31 +00:00
|
|
|
constructor(string memory _name, string memory _symbol, InitPair[] memory _tokens, uint96 _defaultRoyalty) {
|
|
|
|
name = _name;
|
|
|
|
symbol = _symbol;
|
|
|
|
|
|
|
|
for(uint256 i = 1; i <= _tokens.length; i++) {
|
|
|
|
create(i, _tokens[i].token, _tokens[i].url);
|
|
|
|
}
|
|
|
|
|
|
|
|
_setDefaultRoyalty(msg.sender, _defaultRoyalty);
|
2023-01-30 06:09:51 +00:00
|
|
|
}
|
|
|
|
|
2023-02-05 22:06:29 +00:00
|
|
|
function setDefaultRoyalty(address receiver, uint96 feeNumerator) external onlyOwner {
|
2023-01-30 17:47:02 +00:00
|
|
|
_setDefaultRoyalty(receiver, feeNumerator);
|
|
|
|
}
|
|
|
|
|
2023-02-05 22:06:29 +00:00
|
|
|
function deleteDefaultRoyalty() external onlyOwner {
|
2023-01-30 17:47:02 +00:00
|
|
|
_deleteDefaultRoyalty();
|
|
|
|
}
|
|
|
|
|
2023-02-05 22:06:29 +00:00
|
|
|
function setTokenRoyalty(uint256 _id, address receiver, uint96 feeNumerator) external onlyOwner {
|
2023-01-30 17:47:02 +00:00
|
|
|
require(contracts[_id] != address(0), "token does not exist");
|
|
|
|
_setTokenRoyalty(_id, receiver, feeNumerator);
|
|
|
|
}
|
|
|
|
|
2023-02-05 22:06:29 +00:00
|
|
|
function resetTokenRoyalty(uint256 _id) external onlyOwner {
|
2023-01-30 17:47:02 +00:00
|
|
|
require(contracts[_id] != address(0), "token does not exist");
|
|
|
|
_resetTokenRoyalty(_id);
|
2022-02-11 04:17:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2023-01-29 21:44:25 +00:00
|
|
|
@notice Returns URI of token metadata
|
2022-02-11 04:17:00 +00:00
|
|
|
@param _id NFT ID
|
2023-01-29 21:44:25 +00:00
|
|
|
@return URI data URI of metadata JSON
|
2022-02-11 04:17:00 +00:00
|
|
|
*/
|
2023-01-29 21:44:25 +00:00
|
|
|
function uri(uint256 _id) external override view returns (string memory) {
|
2023-01-29 21:15:20 +00:00
|
|
|
IMastersFedi curio = IMastersFedi(contracts[_id]);
|
|
|
|
|
|
|
|
return string(abi.encodePacked("data:application/json;base64,", Base64.encode(abi.encodePacked(
|
|
|
|
'{',
|
2023-01-30 03:21:09 +00:00
|
|
|
'"name":"', curio.name(), '",',
|
|
|
|
'"description":"', curio.description(), '",',
|
|
|
|
'"image":"ipfs://', curio.ipfs_hash(), '",',
|
2023-01-29 21:15:20 +00:00
|
|
|
'"external_url":"', urls[_id], '",',
|
|
|
|
'"properties":{',
|
2023-01-30 05:25:36 +00:00
|
|
|
'"symbol":"', curio.symbol(), '",',
|
|
|
|
'"erc20_contract":"', Strings.toHexString(contracts[_id]), '"',
|
2023-01-29 21:15:20 +00:00
|
|
|
'}',
|
|
|
|
'}'
|
|
|
|
))));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2023-01-29 21:44:25 +00:00
|
|
|
@notice Change the external URL for a token
|
2023-01-29 21:15:20 +00:00
|
|
|
@param _id NFT ID
|
2023-01-29 21:44:25 +00:00
|
|
|
@param _url URL pointing to user or site
|
2023-01-29 21:15:20 +00:00
|
|
|
*/
|
|
|
|
function setExternalUrl(uint256 _id, string memory _url) external onlyOwner {
|
|
|
|
require(contracts[_id] != address(0), "id must exist");
|
|
|
|
|
|
|
|
urls[_id] = _url;
|
2022-02-11 04:17:00 +00:00
|
|
|
}
|
|
|
|
|
2023-01-29 22:22:15 +00:00
|
|
|
/**
|
|
|
|
@notice Query if a contract implements an interface
|
|
|
|
@param _interfaceId The interface identifier, as specified in ERC-165
|
|
|
|
@return `true` if the contract implements `_interfaceId`
|
|
|
|
*/
|
2023-01-30 06:09:51 +00:00
|
|
|
function supportsInterface(bytes4 _interfaceId) public view override(ERC1155, ERC1155Metadata, ERC2981) returns (bool) {
|
2023-01-29 22:22:15 +00:00
|
|
|
return ERC1155.supportsInterface(_interfaceId)
|
2023-01-30 06:09:51 +00:00
|
|
|
|| ERC1155Metadata.supportsInterface(_interfaceId)
|
|
|
|
|| ERC2981.supportsInterface(_interfaceId)
|
|
|
|
;
|
2023-01-29 22:22:15 +00:00
|
|
|
}
|
|
|
|
|
2022-02-11 04:17:00 +00:00
|
|
|
/**
|
2023-01-29 21:44:25 +00:00
|
|
|
@notice If NFT ID exists
|
|
|
|
@dev Makes OpenSea happy
|
2022-02-11 04:17:00 +00:00
|
|
|
@param _id NFT ID
|
2023-01-29 21:44:25 +00:00
|
|
|
@return exists if NFT ID exists.
|
2022-02-11 04:17:00 +00:00
|
|
|
*/
|
|
|
|
function exists(uint256 _id) external view returns(bool) {
|
|
|
|
return contracts[_id] != address(0);
|
|
|
|
}
|
|
|
|
|
2023-01-29 22:50:26 +00:00
|
|
|
bool public destroyable = true;
|
|
|
|
|
|
|
|
/**
|
|
|
|
@notice Destroy the contract
|
|
|
|
@dev Only if it hasn't been disabled
|
|
|
|
*/
|
2023-02-05 22:06:29 +00:00
|
|
|
function destroy() external onlyOwner whenPaused {
|
2023-01-29 22:50:26 +00:00
|
|
|
require(destroyable, "destroy() has been disabled");
|
|
|
|
selfdestruct(payable(msg.sender));
|
|
|
|
}
|
|
|
|
|
2023-02-05 22:06:29 +00:00
|
|
|
function disableDestroy() external onlyOwner {
|
2023-01-29 22:50:26 +00:00
|
|
|
destroyable = false;
|
|
|
|
}
|
|
|
|
|
2023-02-05 21:35:43 +00:00
|
|
|
/**
|
|
|
|
@dev called by token 1 contract after transfer, this is the only method to wrap 1.
|
|
|
|
*/
|
|
|
|
function tokenFallback(address _from, uint256 _value, bytes memory) override external {
|
|
|
|
require(_from == owner(), "Only owner can wrap token 1");
|
|
|
|
require(msg.sender == contracts[1] , "Can only transfer from token 1 owner");
|
|
|
|
IMastersFedi curio = IMastersFedi(contracts[1]);
|
|
|
|
require (curio.balanceOf(address(this)) <= 450, "Sanity transfer exceeded");
|
|
|
|
// no balance check is done because an overflow would fail the sanity check anyway
|
|
|
|
|
|
|
|
balances[1][_from] += _value;
|
|
|
|
|
|
|
|
// mint
|
|
|
|
emit TransferSingle(msg.sender, address(0), _from, 1, _value);
|
|
|
|
|
|
|
|
// safe transfer acceptance check not necessary because locked to a known contract
|
|
|
|
}
|
|
|
|
|
2022-02-11 04:17:00 +00:00
|
|
|
/**
|
2023-01-29 21:44:25 +00:00
|
|
|
@notice Converts old-style NFT to new-style
|
|
|
|
@dev For an NFT ID, queries and transfers tokens from the appropriate
|
|
|
|
curio contract to itself, and mints and transfers corresponding new
|
|
|
|
ERC-1155 tokens to caller
|
|
|
|
@param _id NFT ID
|
|
|
|
@param _quantity how many to wrap
|
2022-02-11 04:17:00 +00:00
|
|
|
*/
|
2023-01-29 22:50:26 +00:00
|
|
|
function wrap(uint256 _id, uint256 _quantity) external whenNotPaused {
|
2022-02-11 04:17:00 +00:00
|
|
|
address tokenContract = contracts[_id];
|
|
|
|
require(tokenContract != address(0), "invalid id");
|
2023-02-05 21:35:43 +00:00
|
|
|
require(_id != 1, "cannot wrap token 1 this way");
|
2023-01-29 19:46:02 +00:00
|
|
|
IMastersFedi curio = IMastersFedi(tokenContract);
|
2022-02-11 04:17:00 +00:00
|
|
|
|
|
|
|
// these are here for convenience because curio contract doesn't throw meaningful exceptions
|
|
|
|
require(curio.balanceOf(msg.sender) >= _quantity, "insufficient curio balance");
|
|
|
|
require(curio.allowance(msg.sender, address(this)) >= _quantity, "insufficient curio allowance");
|
|
|
|
|
|
|
|
curio.transferFrom(msg.sender, address(this), _quantity);
|
|
|
|
|
2023-01-29 21:55:26 +00:00
|
|
|
balances[_id][msg.sender] += _quantity;
|
2022-02-11 04:17:00 +00:00
|
|
|
|
|
|
|
// mint
|
|
|
|
emit TransferSingle(msg.sender, address(0), msg.sender, _id, _quantity);
|
|
|
|
|
2023-02-05 22:06:29 +00:00
|
|
|
if (msg.sender.isContract()) {
|
2022-02-11 04:17:00 +00:00
|
|
|
_doSafeTransferAcceptanceCheck(msg.sender, msg.sender, msg.sender, _id, _quantity, '');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2023-01-29 21:44:25 +00:00
|
|
|
@notice Batch version of wrap function
|
|
|
|
@param _ids array of NFT IDs
|
|
|
|
@param _quantities how many to wrap of each
|
2022-02-11 04:17:00 +00:00
|
|
|
*/
|
2023-01-29 22:50:26 +00:00
|
|
|
function wrapBatch(uint256[] calldata _ids, uint256[] calldata _quantities) external whenNotPaused {
|
2022-02-11 04:17:00 +00:00
|
|
|
require(_ids.length == _quantities.length, "ids and quantities must match");
|
|
|
|
|
|
|
|
for (uint256 i=0; i < _ids.length; ++i) {
|
|
|
|
uint256 _id = _ids[i];
|
|
|
|
uint256 _quantity = _quantities[i];
|
|
|
|
|
|
|
|
address tokenContract = contracts[_id];
|
|
|
|
require(tokenContract != address(0), "invalid id");
|
2023-02-05 21:35:43 +00:00
|
|
|
require(_id != 1, "cannot wrap token 1 this way");
|
2023-01-29 19:46:02 +00:00
|
|
|
IMastersFedi curio = IMastersFedi(tokenContract);
|
2022-02-11 04:17:00 +00:00
|
|
|
|
|
|
|
require(curio.balanceOf(msg.sender) >= _quantity, "insufficient curio balance");
|
|
|
|
require(curio.allowance(msg.sender, address(this)) >= _quantity, "insufficient curio allowance");
|
|
|
|
|
|
|
|
curio.transferFrom(msg.sender, address(this), _quantity);
|
|
|
|
|
2023-01-29 21:55:26 +00:00
|
|
|
balances[_id][msg.sender] += _quantity;
|
2022-02-11 04:17:00 +00:00
|
|
|
|
2023-02-05 22:06:29 +00:00
|
|
|
if (msg.sender.isContract()) {
|
2022-02-11 04:17:00 +00:00
|
|
|
_doSafeTransferAcceptanceCheck(msg.sender, msg.sender, msg.sender, _id, _quantity, '');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// mint
|
2023-02-05 22:06:29 +00:00
|
|
|
emit TransferBatch(msg.sender, address(0), msg.sender, _ids, _quantities);
|
2022-02-11 04:17:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2023-01-29 21:44:25 +00:00
|
|
|
@notice Unwrap new-style NFTs back to old-style
|
|
|
|
@dev For an NFT ID, burns ERC-1155 quantity and transfers curio ERC-20
|
|
|
|
tokens to caller
|
|
|
|
@param _id NFT ID
|
|
|
|
@param _quantity how many to unwrap
|
2022-02-11 04:17:00 +00:00
|
|
|
*/
|
2023-01-29 22:50:26 +00:00
|
|
|
function unwrap(uint256 _id, uint256 _quantity) external whenNotPaused {
|
2022-02-11 04:17:00 +00:00
|
|
|
address tokenContract = contracts[_id];
|
|
|
|
require(tokenContract != address(0), "invalid id");
|
2023-02-05 21:35:43 +00:00
|
|
|
require(_id != 1, "token 1 cannot be unwrapped");
|
2023-01-29 19:46:02 +00:00
|
|
|
IMastersFedi curio = IMastersFedi(tokenContract);
|
2022-02-11 04:17:00 +00:00
|
|
|
|
|
|
|
require(balances[_id][msg.sender] >= _quantity, "insufficient balance");
|
2023-01-29 22:22:15 +00:00
|
|
|
balances[_id][msg.sender] -= _quantity;
|
2022-02-11 04:17:00 +00:00
|
|
|
|
2023-01-30 14:09:02 +00:00
|
|
|
// do it this way to make sure there's a transfer event
|
|
|
|
curio.approve(address(this), _quantity);
|
|
|
|
curio.transferFrom(address(this), msg.sender, _quantity);
|
2022-02-11 04:17:00 +00:00
|
|
|
|
|
|
|
// burn
|
2023-01-29 18:00:39 +00:00
|
|
|
emit TransferSingle(msg.sender, msg.sender, address(0), _id, _quantity);
|
2022-02-11 04:17:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2023-01-29 21:44:25 +00:00
|
|
|
@notice Batch version of unwrap
|
|
|
|
@param _ids array of NFT IDs
|
|
|
|
@param _quantities how many to unwrap of each
|
2022-02-11 04:17:00 +00:00
|
|
|
*/
|
2023-01-29 22:50:26 +00:00
|
|
|
function unwrapBatch(uint256[] calldata _ids, uint256[] calldata _quantities) external whenNotPaused {
|
2022-02-11 04:17:00 +00:00
|
|
|
require(_ids.length == _quantities.length, "ids and quantities must match");
|
|
|
|
|
|
|
|
for (uint256 i=0; i < _ids.length; ++i) {
|
|
|
|
uint256 _id = _ids[i];
|
|
|
|
uint256 _quantity = _quantities[i];
|
|
|
|
|
|
|
|
address tokenContract = contracts[_id];
|
|
|
|
require(tokenContract != address(0), "invalid id");
|
2023-02-05 21:35:43 +00:00
|
|
|
require(_id != 1, "token 1 cannot be unwrapped");
|
2023-01-29 19:46:02 +00:00
|
|
|
IMastersFedi curio = IMastersFedi(tokenContract);
|
2022-02-11 04:17:00 +00:00
|
|
|
|
|
|
|
require(balances[_id][msg.sender] >= _quantity, "insufficient balance");
|
2023-01-29 21:55:26 +00:00
|
|
|
balances[_id][msg.sender] -= _quantity;
|
2022-02-11 04:17:00 +00:00
|
|
|
|
2023-01-30 14:09:02 +00:00
|
|
|
// do it this way to make sure there's a transfer event
|
|
|
|
curio.approve(address(this), _quantity);
|
|
|
|
curio.transferFrom(address(this), msg.sender, _quantity);
|
2022-02-11 04:17:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// burn
|
2023-01-29 18:00:39 +00:00
|
|
|
emit TransferBatch(msg.sender, msg.sender, address(0), _ids, _quantities);
|
2022-02-11 04:17:00 +00:00
|
|
|
}
|
|
|
|
}
|