Skip to content

Instantly share code, notes, and snippets.

@kennethhutw
Created May 7, 2020 08:40
Show Gist options
  • Save kennethhutw/441d969edfe84c4108353be14ad339e0 to your computer and use it in GitHub Desktop.
Save kennethhutw/441d969edfe84c4108353be14ad339e0 to your computer and use it in GitHub Desktop.
pragma solidity ^0.5.0;
// METADATA 是每一個token 都有不同的metadata
// import "openzeppelin-solidity/contracts/token/ERC721/ERC721Full.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.5.0/contracts/drafts/Counters.sol";
// ERC721Full
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.5.0/contracts/token/ERC721/ERC721.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.5.0/contracts/token/ERC721/ERC721Enumerable.sol";
//import "./ERC721Metadata.sol";
// ERC721Metadata
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.5.0/contracts/GSN/Context.sol";
// import "./ERC721.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.5.0/contracts/token/ERC721/IERC721Metadata.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.5.0/contracts/introspection/ERC165.sol";
// import "./ownership.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.5.0/contracts/ownership/Ownable.sol";
contract ERC721Metadata is Context, ERC165, ERC721, IERC721Metadata {
// Token name
string private _name;
// Token symbol
string private _symbol;
// Base URI
string private _baseURI;
struct metadata {
uint256 openingTime;
uint256 closingTime;
string description;
string note;
string legal;
}
// Optional mapping for token URIs
mapping(uint256 => string) private _tokenURIs;
mapping(uint256 => metadata) private _metadata;
/*
* bytes4(keccak256('name()')) == 0x06fdde03
* bytes4(keccak256('symbol()')) == 0x95d89b41
* bytes4(keccak256('tokenURI(uint256)')) == 0xc87b56dd
*
* => 0x06fdde03 ^ 0x95d89b41 ^ 0xc87b56dd == 0x5b5e139f
*/
bytes4 private constant _INTERFACE_ID_ERC721_METADATA = 0x5b5e139f;
/**
* @dev Constructor function
*/
constructor (string memory name, string memory symbol) public {
_name = name;
_symbol = symbol;
// register the supported interfaces to conform to ERC721 via ERC165
_registerInterface(_INTERFACE_ID_ERC721_METADATA);
}
/**
* @dev Gets the token name.
* @return string representing the token name
*/
function name() external view returns (string memory) {
return _name;
}
/**
* @dev Gets the token symbol.
* @return string representing the token symbol
*/
function symbol() external view returns (string memory) {
return _symbol;
}
/**
* @dev Returns the URI for a given token ID. May return an empty string.
*
* If the token's URI is non-empty and a base URI was set (via
* {_setBaseURI}), it will be added to the token ID's URI as a prefix.
*
* Reverts if the token ID does not exist.
*/
function tokenURI(uint256 tokenId) external view returns (string memory) {
require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");
string memory _tokenURI = _tokenURIs[tokenId];
// Even if there is a base URI, it is only appended to non-empty token-specific URIs
if (bytes(_tokenURI).length == 0) {
return "";
} else {
// abi.encodePacked is being used to concatenate strings
return string(abi.encodePacked(_baseURI, _tokenURI));
}
}
/**
* @dev Internal function to set the token URI for a given token.
*
* Reverts if the token ID does not exist.
*
* TIP: if all token IDs share a prefix (e.g. if your URIs look like
* `http://api.myproject.com/token/<id>`), use {_setBaseURI} to store
* it and save gas.
*/
function _setTokenURI(uint256 tokenId, string memory _tokenURI) internal {
require(_exists(tokenId), "ERC721Metadata: URI set of nonexistent token");
_tokenURIs[tokenId] = _tokenURI;
}
/**
* @dev Internal function to set the token URI for a given token.
*
* Reverts if the token ID does not exist.
*
* TIP: if all token IDs share a prefix (e.g. if your URIs look like
* `http://api.myproject.com/token/<id>`), use {_setBaseURI} to store
* it and save gas.
*/
function _setMetaData(uint256 tokenId, uint256 openingTime, uint256 closingTime,
string memory description, string memory note, string memory legal) internal {
require(_exists(tokenId), "ERC721Metadata: URI set of nonexistent token");
metadata storage __metadata = _metadata[tokenId];
__metadata.openingTime = openingTime;
__metadata.closingTime = closingTime;
__metadata.description = description;
__metadata.note = note;
__metadata.legal = legal;
}
/**
* @dev Internal function to set the token URI for a given token.
*
* Reverts if the token ID does not exist.
*
* TIP: if all token IDs share a prefix (e.g. if your URIs look like
* `http://api.myproject.com/token/<id>`), use {_setBaseURI} to store
* it and save gas.
*/
function MetaData(uint256 tokenId) public view returns (uint256, uint256, string memory, string memory, string memory) {
require(_exists(tokenId), "ERC721Metadata: Metadata set of nonexistent token");
return (
_metadata[tokenId].openingTime,
_metadata[tokenId].closingTime,
_metadata[tokenId].description,
_metadata[tokenId].note,
_metadata[tokenId].legal);
}
/**
* @return the crowdsale opening time.
*/
function openingTime(uint256 tokenId) public view returns (uint256) {
require(_exists(tokenId), "ERC721Metadata: Metadata set of nonexistent token");
return _metadata[tokenId].openingTime;
}
/**
* @return the crowdsale closing time.
*/
function closingTime(uint256 tokenId) public view returns (uint256) {
require(_exists(tokenId), "ERC721Metadata: Metadata set of nonexistent token");
return _metadata[tokenId].closingTime;
}
/**
* @return true if the crowdsale is open, false otherwise.
*/
function isOpen(uint256 tokenId) public view returns (bool) {
require(_exists(tokenId), "ERC721Metadata: Metadata set of nonexistent token");
return block.timestamp >= _metadata[tokenId].openingTime && block.timestamp <= _metadata[tokenId].closingTime;
}
/**
* @dev Checks whether the period in which the crowdsale is open has already elapsed.
* @return Whether crowdsale period has elapsed
*/
function hasClosed(uint256 tokenId) public view returns (bool) {
require(_exists(tokenId), "ERC721Metadata: Metadata set of nonexistent token");
return block.timestamp > _metadata[tokenId].closingTime;
}
/**
* @dev Internal function to set the base URI for all token IDs. It is
* automatically added as a prefix to the value returned in {tokenURI}.
*
* _Available since v2.5.0._
*/
function _setBaseURI(string memory baseURI) internal {
_baseURI = baseURI;
}
/**
* @dev Returns the base URI set via {_setBaseURI}. This will be
* automatically added as a preffix in {tokenURI} to each token's URI, when
* they are non-empty.
*
* _Available since v2.5.0._
*/
function baseURI() external view returns (string memory) {
return _baseURI;
}
/**
* @dev Internal function to burn a specific token.
* Reverts if the token does not exist.
* Deprecated, use _burn(uint256) instead.
* @param owner owner of the token to burn
* @param tokenId uint256 ID of the token being burned by the msg.sender
*/
function _burn(address owner, uint256 tokenId) internal {
super._burn(owner, tokenId);
// Clear metadata (if any)
if (bytes(_tokenURIs[tokenId]).length != 0) {
delete _tokenURIs[tokenId];
}
}
}
/**
* @title Full ERC721 Token
* @dev This implementation includes all the required and some optional functionality of the ERC721 standard
* Moreover, it includes approve all functionality using operator terminology.
*
* See https://eips.ethereum.org/EIPS/eip-721
*/
contract ERC721Full is ERC721Enumerable, ERC721Metadata {
constructor (string memory name, string memory symbol) public ERC721Metadata(name, symbol) {
// solhint-disable-previous-line no-empty-blocks
}
}
contract BaypayItem is ERC721Full, Ownable {
using Counters for Counters.Counter;
Counters.Counter private _tokenIds;
mapping(address => bool) internal mIsOfficialOperator;
mapping(address => bool) internal mIsUserNotAcceptingAllOfficialOperators;
event OfficialOperatorAdded(address operator);
event OfficialOperatorRemoved(address operator);
event OfficialOperatorsAcceptedByUser(address indexed user);
event OfficialOperatorsRejectedByUser(address indexed user);
event Sent(
address indexed operator,
address indexed from,
address indexed to,
uint256 tokenId,
bytes holderData,
bytes operatorData
);
// name, symbol
constructor(string memory tokenName, string memory tokenSymbol) ERC721Full(tokenName, tokenSymbol) public {
}
function delivery(address player, string memory tokenURI, uint256 openingTime, uint256 closingTime,
string memory description, string memory note, string memory legal) public returns (uint256) {
_tokenIds.increment();
uint256 newItemId = _tokenIds.current();
_mint(player, newItemId);
_setTokenURI(newItemId, tokenURI);
_setMetaData(newItemId, openingTime, closingTime, description, note, legal);
return newItemId;
}
function mint(address player, string memory tokenURI) public returns (uint256) {
_tokenIds.increment();
uint256 newItemId = _tokenIds.current();
_mint(player, newItemId);
_setTokenURI(newItemId, tokenURI);
return newItemId;
}
function setMetaData(uint256 tokenId, uint256 openingTime, uint256 closingTime,
string memory description, string memory note, string memory legal) public {
require(_exists(tokenId), "ERC721Metadata: Metadata set of nonexistent token");
_setMetaData(tokenId, openingTime, closingTime, description, note, legal);
}
/// @notice Add an address into the list of official operators.
/// @param _operator The address of a new official operator.
/// An official operator must be a contract.
function addOfficialOperator(address _operator) external onlyOwner {
// require(_operator.isContract(), "An official operator must be a contract.");
require(!mIsOfficialOperator[_operator], "_operator is already an official operator.");
mIsOfficialOperator[_operator] = true;
emit OfficialOperatorAdded(_operator);
}
/// @notice Delete an address from the list of official operators.
/// @param _operator The address of an official operator.
function removeOfficialOperator(address _operator) external onlyOwner {
require(mIsOfficialOperator[_operator], "_operator is not an official operator.");
mIsOfficialOperator[_operator] = false;
emit OfficialOperatorRemoved(_operator);
}
/// @notice Unauthorize all official operators to manage `msg.sender`'s tokens.
function rejectAllOfficialOperators() external {
require(!mIsUserNotAcceptingAllOfficialOperators[msg.sender], "Official operators are already rejected by msg.sender.");
mIsUserNotAcceptingAllOfficialOperators[msg.sender] = true;
emit OfficialOperatorsRejectedByUser(msg.sender);
}
/// @notice Authorize all official operators to manage `msg.sender`'s tokens.
function acceptAllOfficialOperators() external {
require(mIsUserNotAcceptingAllOfficialOperators[msg.sender], "Official operators are already accepted by msg.sender.");
mIsUserNotAcceptingAllOfficialOperators[msg.sender] = false;
emit OfficialOperatorsAcceptedByUser(msg.sender);
}
/// @return true if the address is an official operator, false if not.
function isOfficialOperator(address _operator) external view returns(bool) {
return mIsOfficialOperator[_operator];
}
/// @return true if a user is accepting all official operators, false if not.
function isUserAcceptingAllOfficialOperators(address _user) external view returns(bool) {
return !mIsUserNotAcceptingAllOfficialOperators[_user];
}
/// @notice Check whether the `_operator` address is allowed to manage the tokens held by `_tokenHolder` address.
/// @param _operator address to check if it has the right to manage the tokens
/// @param _tokenHolder address which holds the tokens to be managed
/// @return `true` if `_operator` is authorized for `_tokenHolder`
function isOperatorFor(address _operator, address _tokenHolder) public view returns (bool) {
return (
_operator == _tokenHolder
|| (!mIsUserNotAcceptingAllOfficialOperators[_tokenHolder] && mIsOfficialOperator[_operator]));
}
function operatorSend(address _from, address _to, uint256[] calldata __tokenIds, bytes calldata _userData, bytes calldata _operatorData) external {
require(isOperatorFor(msg.sender, _from),"sender is not operator");
doSend(msg.sender, _from, _to, __tokenIds, _userData, _operatorData);
}
/// @notice Helper function actually performing the sending of tokens.
/// @param _operator The address performing the send
/// @param _from The address holding the tokens being sent
/// @param _to The address of the recipient
/// @param __tokenIds The number of tokens to be sent
/// implementing `ERC777TokensRecipient`.
/// ERC777 native Send functions MUST set this parameter to `true`, and backwards compatible ERC20 transfer
/// functions SHOULD set this parameter to `false`.
function doSend(
address _operator,
address _from,
address _to,
uint256[] memory __tokenIds,
bytes memory _userData,
bytes memory _operatorData
)
internal
{
for(uint i = 0; i < __tokenIds.length; i++) {
_transferFrom(_from, _to, __tokenIds[i]);
emit Sent(_operator, _from, _to, __tokenIds[i], _userData, _operatorData);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment