token-gallery-contracts/contracts/ERC1155Facet.sol

450 lines
16 KiB
Solidity

// SPDX-License-Identifier: Proprietary
pragma solidity ^0.7.2;
pragma experimental ABIEncoderV2;
import "./Address.sol";
import "./SafeMath.sol";
import "./Strings.sol";
import "./LibControl.sol";
import "./LibPause.sol";
import "./LibERC1155.sol";
import "./IERC1155.sol";
import "./IERC1155Receiver.sol";
import "./IBurnable.sol";
import "./IMintable.sol";
import "./IPriceChange.sol";
import "./IERC1155MetadataURI.sol";
import "./IERC1155Extra.sol";
contract ERC1155Facet is IERC1155, IERC1155MetadataURI, IERC1155Extra, IBurnable, IMintable, IPriceChange {
using Address for address;
using Strings for string;
using SafeMath for uint256;
using SafeMath for uint16;
modifier onlyCreator {
LibControl.enforceIsCreator();
_;
}
modifier onlyManager {
LibControl.enforceIsManager();
_;
}
modifier whenNotPaused {
require(!LibPause.paused());
_;
}
/**
Generates URI in form of {uri prefix}{ipfshash}
IDs between stores should never clash because of embedded store id
*/
function uri(uint256 _id) external virtual override view returns (string memory) {
LibERC1155.ERC1155Store storage store = LibERC1155.getERC1155Store();
return Strings.strConcat(
store._uri,
store.nftData[_id].metadataIpfsHash
);
}
/**
Should really only ever be "ipfs://"
*/
function setURI(string calldata _newURI) external virtual override onlyManager {
LibERC1155.setURI(_newURI);
}
function name() external override view returns(string memory) {
LibERC1155.ERC1155Store storage store = LibERC1155.getERC1155Store();
return store.name;
}
function symbol() external override view returns(string memory) {
LibERC1155.ERC1155Store storage store = LibERC1155.getERC1155Store();
return store.symbol;
}
function proxyRegistryAddress() external override view returns(address) {
LibERC1155.ERC1155Store storage store = LibERC1155.getERC1155Store();
return store.proxyRegistryAddress;
}
function setProxyRegistryAddress(address _newAddress) external override onlyManager {
LibERC1155.setProxyRegistryAddress(_newAddress);
}
function nftData(uint256 _id) external override view returns(LibERC1155.Nft memory) {
LibERC1155.ERC1155Store storage store = LibERC1155.getERC1155Store();
return store.nftData[_id];
}
function storeId(uint256 _id) external override pure returns(uint24) {
return LibERC1155.getStoreId(_id);
}
function nonce() external override view returns(uint64) {
LibERC1155.ERC1155Store storage store = LibERC1155.getERC1155Store();
return store.nonce;
}
function metaNonce() external override view returns(uint64) {
LibERC1155.ERC1155Store storage store = LibERC1155.getERC1155Store();
return store.metaNonce;
}
function metaId(uint256 _id) external override pure returns(uint64) {
return LibERC1155.getMetaId(_id);
}
function subId(uint256 _id) external override pure returns(uint64) {
return LibERC1155.getSubId(_id);
}
function quantityForId(uint256 _id) external override pure returns (uint8) {
return LibERC1155.getQuantity(_id);
}
function balanceOf(address account, uint256 id) external view override returns (uint256) {
require(account != address(0), "0 disallowed");
LibERC1155.ERC1155Store storage store = LibERC1155.getERC1155Store();
if (LibERC1155.isFungible(id)) {
return store._balances[id][account];
}
else {
return store.nftData[id].owner == account ? 1 : 0;
}
}
function balanceOfBatch(
address[] calldata accounts,
uint256[] calldata ids
)
external
view
override
returns (uint256[] memory)
{
require(accounts.length == ids.length, "parm len mismatch");
LibERC1155.ERC1155Store storage store = LibERC1155.getERC1155Store();
uint256[] memory batchBalances = new uint256[](accounts.length);
for (uint256 i = 0; i < accounts.length; ++i) {
require(accounts[i] != address(0), "0 disallowed");
if (LibERC1155.isFungible(ids[i])) {
batchBalances[i] = store._balances[ids[i]][accounts[i]];
}
else {
batchBalances[i] = store.nftData[ids[i]].owner == accounts[i] ? 1 : 0;
}
}
return batchBalances;
}
function isDataLocked(uint256 _id) override external view returns (bool) {
LibERC1155.ERC1155Store storage store = LibERC1155.getERC1155Store();
return store.nftData[_id].dataLocked;
}
function lockData(uint256 _id) override external {
LibERC1155.ERC1155Store storage store = LibERC1155.getERC1155Store();
require(!store.nftData[_id].dataLocked, "Already locked");
bool isCreatorControlled = msg.sender == LibControl.creator() && store.nftData[_id].owner == msg.sender;
require(isCreatorControlled || msg.sender == LibControl.manager() || msg.sender == LibControl.bridge(), "Not permitted to lock");
store.nftData[_id].dataLocked = true;
}
/**
Caution, remember there is a prefix
*/
function changeMetadataUri(uint256 _id, string memory _uri) override external {
LibERC1155.ERC1155Store storage store = LibERC1155.getERC1155Store();
require(!store.nftData[_id].dataLocked, "Data is locked");
require(msg.sender == LibControl.bridge(), "Only bridge can change URI");
store.nftData[_id].metadataIpfsHash = _uri;
emit URI(_uri, _id);
}
/**
Override isApprovedForAll to whitelist user's OpenSea proxy accounts to
enable gas-free listings.
*/
function isApprovedForAll(address _account, address _operator) public view override returns (bool) {
return LibERC1155.isApprovedForAll(_account, _operator);
}
function setApprovalForAll(address operator, bool approved) external virtual override whenNotPaused {
LibERC1155.setApprovalForAll(operator, approved);
}
function safeTransferFrom(
address from,
address to,
uint256 id,
uint256 amount,
bytes memory data
)
public
virtual
override
whenNotPaused
{
require(to != address(0), "To 0 disallowed");
LibERC1155.safeTransferFrom(from, to, id, amount, data);
}
function safeBatchTransferFrom(
address from,
address to,
uint256[] memory ids,
uint256[] memory amounts,
bytes memory data
)
public
virtual
override
whenNotPaused
{
require(to != address(0), "To 0 disallowed");
LibERC1155.safeBatchTransferFrom(from, to, ids, amounts, data);
}
/**
* @dev Creates `amount` tokens of token type `id`, and assigns them to `account`.
*
* Emits a {TransferSingle} event.
*
* Requirements:
*
* - `account` cannot be the zero address.
* - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
* acceptance magic value.
*/
function _mint(address account, uint256 id, uint256 amount, bytes memory data) internal virtual {
require(account != address(0), "Mint to 0");
LibERC1155.ERC1155Store storage store = LibERC1155.getERC1155Store();
address operator = msg.sender;
LibERC1155._beforeTokenTransfer(operator, address(0), account, LibERC1155._asSingletonArray(id), LibERC1155._asSingletonArray(amount), data);
store._balances[id][account] = store._balances[id][account].add(amount);
emit TransferSingle(operator, address(0), account, id, amount);
LibERC1155._doSafeTransferAcceptanceCheck(operator, address(0), account, id, amount, data);
}
/**
You can never mint more of the same meta id.
*/
function mintNonFungible(
bool sameMetaId,
LibERC1155.Nft[] memory nfts
) external virtual override whenNotPaused onlyCreator returns(uint256[] memory) {
require (nfts.length > 0, "0 quantity");
uint8 quantity = uint8(nfts.length);
LibERC1155.ERC1155Store storage store = LibERC1155.getERC1155Store();
uint256[] memory ids = new uint256[](quantity);
uint256[] memory values = new uint256[](quantity); // just an array of 1's for the event
address to = nfts[0].owner; // only used if every nft has same owner
bool sameOwner = true;
uint24 _storeId = LibERC1155.getERC1155Store().storeId;
uint64 _metaId = LibERC1155.getERC1155Store().metaNonce;
uint8 _subId = 0; // reset for every meta id
for (uint8 i = 0; i < quantity; ++i) {
LibERC1155.Nft memory nft = nfts[i];
require(nft.price == 0 || nft.price >= 100, "price must be 0 or above 100 wei");
require(nft.tokenPrice == 0 || nft.tokenPrice >= 100, "token price must be 0 or above 100 wei");
require(nft.owner != address(0), "Cannot mint to 0 address");
values[i] = 1;
sameOwner = sameOwner && ( nft.owner == to );
ids[i] =
(uint256(_storeId) << LibERC1155.STORE_ID_OFFSET) |
LibERC1155.NFT_FLAG |
(uint256(_metaId) << LibERC1155.META_ID_OFFSET) |
(uint256(quantity) << LibERC1155.QUANTITY_OFFSET) |
uint256(_subId)
;
store.nftData[ids[i]] = nft;
if (nft.forSale == LibERC1155.NFTLucreState.ForSale || nft.tokenForSale == LibERC1155.NFTLucreState.ForSale) {
emit PriceChange(
ids[i],
nft.owner,
nft.price,
nft.tokenPrice,
nft.forSale == LibERC1155.NFTLucreState.ForSale,
nft.tokenForSale == LibERC1155.NFTLucreState.ForSale
);
}
if (sameMetaId) {
_subId++;
}
else {
_metaId++;
}
}
LibERC1155.incrementMetaNonce( sameMetaId ? 1 : quantity );
if (sameOwner) {
emit TransferBatch(msg.sender, address(0), to, ids, values);
}
else {
for (uint8 i = 0; i < quantity; ++i) {
emit TransferSingle(msg.sender, address(0), nfts[i].owner, ids[i], 1);
}
}
return ids;
}
function mintFungible(address _to, uint256 _id, uint256 _quantity) external virtual override whenNotPaused onlyCreator {
uint24 _storeId = LibERC1155.getERC1155Store().storeId;
uint256 _nonce = LibERC1155.getERC1155Store().nonce;
uint256 _nextFungibleId = (uint256(_storeId) << LibERC1155.STORE_ID_OFFSET) | _nonce;
// can only mint more of an existing, or next id
if (_id == _nextFungibleId) {
LibERC1155.incrementNonce(1);
_nextFungibleId += 1;
}
require(_id < _nextFungibleId, "Invalid fungible ID");
_mint(_to, _id, _quantity, "");
}
function mintFungible(address _to, uint256 _quantity) external virtual override whenNotPaused onlyCreator returns(uint256) {
uint24 _storeId = LibERC1155.getERC1155Store().storeId;
uint256 _nonce = LibERC1155.getERC1155Store().nonce;
uint256 _id = (uint256(_storeId) << LibERC1155.STORE_ID_OFFSET) | _nonce;
require(_id < (uint256(_storeId) << LibERC1155.STORE_ID_OFFSET) | LibERC1155.NFT_FLAG , "Out of fungible IDs");
LibERC1155.incrementNonce(1);
_mint(_to, _id, _quantity, "");
return _id;
}
function isNonFungibleItem(uint256 _id) external pure virtual override returns(bool) {
return LibERC1155.isNonFungible(_id);
}
function getSetForId(uint256 _id) external pure virtual override returns (uint256[] memory) {
return LibERC1155.getSetForId(_id);
}
/**
* @dev Destroys `amount` tokens of token type `id` from `account`
*
* Requirements:
*
* - `account` cannot be the zero address.
* - `account` must have at least `amount` tokens of token type `id`.
*/
function _burn(address account, uint256 id, uint256 amount) internal virtual {
require(account != address(0), "Burn from 0");
LibERC1155.ERC1155Store storage store = LibERC1155.getERC1155Store();
address operator = msg.sender;
LibERC1155._beforeTokenTransfer(operator, account, address(0), LibERC1155._asSingletonArray(id), LibERC1155._asSingletonArray(amount), "");
store._balances[id][account] = store._balances[id][account].sub(
amount,
"Burn > bal"
);
emit TransferSingle(operator, account, address(0), id, amount);
}
/**
Anyone can burn their fungible tokens but only someone both owner and creator can burn an NFT.
*/
function burn(address account, uint256 id, uint256 amount) external whenNotPaused override virtual {
LibERC1155.ERC1155Store storage store = LibERC1155.getERC1155Store();
if (LibERC1155.isNonFungible(id)) {
require(amount == 1, "Burn 1 only");
LibERC1155.Nft storage nft = store.nftData[id];
require(account == nft.owner, "Account doesn't own NFT");
require(
msg.sender == nft.owner ||
msg.sender == LibControl.controlStorage().mothership.bridge() ||
isApprovedForAll(account, msg.sender
), "Disallowed");
address operator = msg.sender;
LibERC1155._beforeTokenTransfer(operator, account, address(0), LibERC1155._asSingletonArray(id), LibERC1155._asSingletonArray(amount), "");
nft.owner = address(0);
LibERC1155.resetNftPrice(nft); // not bothering to emit a price change
emit TransferSingle(operator, account, address(0), id, 1);
}
else {
require(account == msg.sender || isApprovedForAll(account, msg.sender), "Disallowed");
_burn(account, id, amount);
}
}
function burnBatch(address account, uint256[] calldata ids, uint256[] calldata amounts) external whenNotPaused override virtual {
require(account != address(0), "Burn 0 disallowed");
require(ids.length == amounts.length, "Len mismatch");
LibERC1155.ERC1155Store storage store = LibERC1155.getERC1155Store();
address operator = msg.sender;
LibERC1155._beforeTokenTransfer(operator, account, address(0), ids, amounts, "");
for (uint i = 0; i < ids.length; i++) {
uint256 id = ids[i];
uint256 amount = amounts[i];
if (LibERC1155.isFungible(id)) {
require(
account == msg.sender || isApprovedForAll(account, msg.sender),
"Disallowed"
);
store._balances[id][account] = store._balances[id][account].sub(
amount,
"Burn exceeds bal"
);
emit TransferSingle(operator, account, address(0), id, 1);
}
else {
require(amount == 1, "Burn 1 only");
LibERC1155.Nft storage nft = store.nftData[id];
require(account == nft.owner, "Account doesn't own NFT");
require(
( msg.sender == nft.owner ) || isApprovedForAll(account, msg.sender) || msg.sender == LibControl.controlStorage().mothership.bridge(),
"Disallowed"
);
nft.owner = address(0);
LibERC1155.resetNftPrice(nft); // not bothering to emit a price change
emit TransferSingle(operator, account, address(0), id, 1);
}
}
}
}