Skip to content

Instantly share code, notes, and snippets.

@max-clinch
Created February 5, 2024 19:32
Show Gist options
  • Select an option

  • Save max-clinch/512d41379eee8f2dede3ec306bd0e04a to your computer and use it in GitHub Desktop.

Select an option

Save max-clinch/512d41379eee8f2dede3ec306bd0e04a to your computer and use it in GitHub Desktop.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
contract Yoshka is Ownable(msg.sender) {
enum MusicType {
ASSET,
SONG
}
struct MusicItem {
MusicType musicType;
string title;
string description;
string fileType;
string duration;
string thumbnailURI;
string fileURI;
uint256 dateOfCreation;
uint256 initialPrice;
uint8 royaltyPercentage;
uint256[] ingredients;
}
struct Post {
uint256 postId;
address author;
string content;
uint256 creationDate;
uint256[] comments;
uint256 likes;
uint256 dislikes;
}
struct Comment {
uint256 commentId;
address author;
string content;
uint256 creationDate;
uint256 likes;
uint256 dislikes;
}
mapping(uint256 => MusicItem) public musicItems;
mapping(uint256 => address) private itemCreators;
mapping(uint256 => Post) public posts;
mapping(uint256 => Comment) public comments;
uint256 public nextPostId;
uint256 public nextCommentId;
event NFTMinted(
uint256 indexed tokenId,
address indexed creator,
string title
);
event OwnershipTransferred(
uint256 indexed tokenId,
address indexed previousOwner,
address indexed newOwner
);
event PostCreated(
uint256 indexed postId,
address indexed author,
string content
);
event CommentCreated(
uint256 indexed commentId,
address indexed author,
string content
);
event PostLiked(uint256 indexed postId, address indexed liker);
event PostDisliked(uint256 indexed postId, address indexed disliker);
event CommentLiked(uint256 indexed commentId, address indexed liker);
event CommentDisliked(uint256 indexed commentId, address indexed disliker);
event PostEdited(uint256 indexed postId, string newContent);
event CommentEdited(uint256 indexed commentId, string newContent);
event PostDeleted(uint256 indexed postId);
event CommentDeleted(uint256 indexed commentId);
mapping(address => bool) private admins;
uint256 private nextTokenId;
bool private reentrancyLock = false;
modifier onlyAdmin() {
require(
admins[msg.sender] || msg.sender == owner(),
"Only admins can call this function"
);
_;
}
modifier noReentrancy() {
require(!reentrancyLock, "Reentrant call");
reentrancyLock = true;
_;
reentrancyLock = false;
}
constructor() {
admins[msg.sender] = true;
nextTokenId = 1;
}
function addAdmin(address newAdmin) external onlyOwner {
require(newAdmin != address(0), "Invalid address");
admins[newAdmin] = true;
}
function removeAdmin(address admin) external onlyOwner {
require(admins[admin], "Not an admin");
admins[admin] = false;
}
function mintNFT(
MusicType _musicType,
string memory _title,
string memory _description,
string memory _fileType,
string memory _duration,
string memory _thumbnailURI,
string memory _fileURI,
uint256 _initialPrice,
uint8 _royaltyPercentage,
uint256[] memory _ingredients
) external {
uint256 tokenId = nextTokenId;
nextTokenId++;
MusicItem memory newItem = MusicItem({
musicType: _musicType,
title: _title,
description: _description,
fileType: _fileType,
duration: _duration,
thumbnailURI: _thumbnailURI,
fileURI: _fileURI,
dateOfCreation: block.timestamp,
initialPrice: _initialPrice,
royaltyPercentage: _royaltyPercentage,
ingredients: _ingredients
});
musicItems[tokenId] = newItem;
itemCreators[tokenId] = msg.sender;
emit NFTMinted(tokenId, msg.sender, _title);
}
function batchMintNFTs(
MusicType[] memory _musicTypes,
string[] memory _titles,
string[] memory _descriptions,
string[] memory _fileTypes,
string[] memory _durations,
string[] memory _thumbnailURIs,
string[] memory _fileURIs,
uint256[] memory _initialPrices,
uint8[] memory _royaltyPercentages,
uint256[][] memory _ingredients
) external {
require(_musicTypes.length == _titles.length, "Arrays length mismatch");
for (uint256 i = 0; i < _musicTypes.length; i++) {
uint256 tokenId = nextTokenId;
nextTokenId++;
MusicItem memory newItem = MusicItem({
musicType: _musicTypes[i],
title: _titles[i],
description: _descriptions[i],
fileType: _fileTypes[i],
duration: _durations[i],
thumbnailURI: _thumbnailURIs[i],
fileURI: _fileURIs[i],
dateOfCreation: block.timestamp,
initialPrice: _initialPrices[i],
royaltyPercentage: _royaltyPercentages[i],
ingredients: _ingredients[i]
});
musicItems[tokenId] = newItem;
itemCreators[tokenId] = msg.sender;
emit NFTMinted(tokenId, msg.sender, _titles[i]);
}
}
function batchTransferNFTs(uint256[] memory _tokenIds, address _to)
external
{
for (uint256 i = 0; i < _tokenIds.length; i++) {
require(
itemCreators[_tokenIds[i]] == msg.sender,
"Only the creator can transfer"
);
itemCreators[_tokenIds[i]] = _to;
emit OwnershipTransferred(_tokenIds[i], msg.sender, _to);
}
}
function ownedNFTCount(address user) public view returns (uint256) {
uint256 ownedCount = 0;
for (uint256 tokenId = 1; tokenId < nextTokenId; tokenId++) {
if (itemCreators[tokenId] == user) {
ownedCount++;
}
}
return ownedCount;
}
function ownedNFTs(address user) public view returns (uint256[] memory) {
uint256 ownedCount = ownedNFTCount(user);
uint256[] memory ownedNFTIds = new uint256[](ownedCount);
uint256 index = 0;
for (uint256 tokenId = 1; tokenId < nextTokenId; tokenId++) {
if (itemCreators[tokenId] == user) {
ownedNFTIds[index] = tokenId;
index++;
}
}
return ownedNFTIds;
}
function mintNFTWithPayment(
MusicType _musicType,
string memory _title,
string memory _description,
string memory _fileType,
string memory _duration,
string memory _thumbnailURI,
string memory _fileURI,
uint256 _initialPrice,
uint8 _royaltyPercentage,
uint256[] memory _ingredients
) external payable onlyAdmin noReentrancy {
uint256 tokenId = nextTokenId;
nextTokenId++;
require(msg.value >= _initialPrice, "Insufficient payment");
MusicItem memory newItem = MusicItem({
musicType: _musicType,
title: _title,
description: _description,
fileType: _fileType,
duration: _duration,
thumbnailURI: _thumbnailURI,
fileURI: _fileURI,
dateOfCreation: block.timestamp,
initialPrice: _initialPrice,
royaltyPercentage: _royaltyPercentage,
ingredients: _ingredients
});
musicItems[tokenId] = newItem;
itemCreators[tokenId] = msg.sender;
emit NFTMinted(tokenId, msg.sender, _title);
if (msg.value > _initialPrice) {
payable(msg.sender).transfer(msg.value - _initialPrice);
}
}
function getMusicItemMetadata(uint256 tokenId)
external
view
returns (
MusicType musicType,
string memory title,
string memory description,
string memory fileType,
string memory duration,
string memory thumbnailURI,
string memory fileURI,
uint256 dateOfCreation,
uint256 initialPrice,
uint8 royaltyPercentage,
uint256[] memory ingredients
)
{
require(tokenId > 0 && tokenId < nextTokenId, "Invalid token ID");
MusicItem memory musicItem = musicItems[tokenId];
return (
musicItem.musicType,
musicItem.title,
musicItem.description,
musicItem.fileType,
musicItem.duration,
musicItem.thumbnailURI,
musicItem.fileURI,
musicItem.dateOfCreation,
musicItem.initialPrice,
musicItem.royaltyPercentage,
musicItem.ingredients
);
}
function distributeRoyalties(uint256 tokenId, uint256 salePrice) internal {
require(tokenId > 0 && tokenId < nextTokenId, "Invalid token ID");
address creator = itemCreators[tokenId];
require(creator != address(0), "Invalid creator address");
uint256 royaltyAmount = (salePrice *
musicItems[tokenId].royaltyPercentage) / 100;
payable(creator).transfer(royaltyAmount);
}
function updateNFTMetadata(
uint256 tokenId,
string memory newTitle,
string memory newDescription,
string memory newThumbnailURI,
string memory newFileURI
) external onlyAdmin {
require(tokenId > 0 && tokenId < nextTokenId, "Invalid token ID");
MusicItem storage musicItem = musicItems[tokenId];
musicItem.title = newTitle;
musicItem.description = newDescription;
musicItem.thumbnailURI = newThumbnailURI;
musicItem.fileURI = newFileURI;
}
function burnNFT(uint256 tokenId) external {
require(tokenId > 0 && tokenId < nextTokenId, "Invalid token ID");
require(itemCreators[tokenId] == msg.sender, "Not the owner");
delete musicItems[tokenId];
delete itemCreators[tokenId];
}
function upgradeContract(address newContractAddress) external onlyOwner {
payable(newContractAddress).transfer(address(this).balance);
transferOwnership(newContractAddress);
}
bool private paused = false;
modifier whenNotPaused() {
require(!paused, "Contract is paused");
_;
}
function pauseContract() external onlyAdmin {
paused = true;
}
function unpauseContract() external onlyAdmin {
paused = false;
}
function batchTransferNFTsToAddress(uint256[] memory tokenIds, address to)
external
onlyAdmin
{
for (uint256 i = 0; i < tokenIds.length; i++) {
uint256 tokenId = tokenIds[i];
require(itemCreators[tokenId] != address(0), "Invalid token ID");
itemCreators[tokenId] = to;
emit OwnershipTransferred(tokenId, itemCreators[tokenId], to);
}
}
function setRoyaltyPercentage(uint256 tokenId, uint8 newRoyaltyPercentage)
external
onlyAdmin
{
require(tokenId > 0 && tokenId < nextTokenId, "Invalid token ID");
require(newRoyaltyPercentage <= 100, "Invalid royalty percentage");
musicItems[tokenId].royaltyPercentage = newRoyaltyPercentage;
}
function withdrawBalance(address payable recipient) external onlyOwner {
uint256 balance = address(this).balance;
require(balance > 0, "No balance to withdraw");
recipient.transfer(balance);
}
function transferNFTOwnership(uint256 tokenId, address newOwner) external {
require(tokenId > 0 && tokenId < nextTokenId, "Invalid token ID");
require(itemCreators[tokenId] == msg.sender, "Not the owner");
itemCreators[tokenId] = newOwner;
emit OwnershipTransferred(tokenId, msg.sender, newOwner);
}
function getTotalSupply() external view returns (uint256) {
return nextTokenId - 1;
}
function createPost(string memory content) external {
uint256 postId = nextPostId;
nextPostId++;
Post memory newPost = Post({
postId: postId,
author: msg.sender,
content: content,
creationDate: block.timestamp,
comments: new uint256[](0),
likes: 0,
dislikes: 0
});
posts[postId] = newPost;
emit PostCreated(postId, msg.sender, content);
}
function likePost(uint256 postId) external {
require(postId < nextPostId, "Invalid post ID");
Post storage post = posts[postId];
require(post.author != address(0), "Post does not exist");
post.likes++;
emit PostLiked(postId, msg.sender);
}
function dislikePost(uint256 postId) external {
require(postId < nextPostId, "Invalid post ID");
Post storage post = posts[postId];
require(post.author != address(0), "Post does not exist");
post.dislikes++;
emit PostDisliked(postId, msg.sender);
}
function editPost(uint256 postId, string memory newContent) external {
require(postId < nextPostId, "Invalid post ID");
Post storage post = posts[postId];
require(post.author == msg.sender, "Only the author can edit the post");
post.content = newContent;
emit PostEdited(postId, newContent);
}
function deletePost(uint256 postId) external {
require(postId < nextPostId, "Invalid post ID");
Post storage post = posts[postId];
require(
post.author == msg.sender,
"Only the author can delete the post"
);
delete posts[postId];
emit PostDeleted(postId);
}
function createComment(uint256 postId, string memory content) external {
require(postId < nextPostId, "Invalid post ID");
Comment memory newComment = Comment({
commentId: nextCommentId,
author: msg.sender,
content: content,
creationDate: block.timestamp,
likes: 0,
dislikes: 0
});
comments[nextCommentId] = newComment;
Post storage post = posts[postId];
post.comments.push(nextCommentId);
nextCommentId++;
emit CommentCreated(newComment.commentId, msg.sender, content);
}
function likeComment(uint256 commentId) external {
require(commentId < nextCommentId, "Invalid comment ID");
Comment storage comment = comments[commentId];
require(comment.author != address(0), "Comment does not exist");
comment.likes++;
emit CommentLiked(commentId, msg.sender);
}
function dislikeComment(uint256 commentId) external {
require(commentId < nextCommentId, "Invalid comment ID");
Comment storage comment = comments[commentId];
require(comment.author != address(0), "Comment does not exist");
comment.dislikes++;
emit CommentDisliked(commentId, msg.sender);
}
function editComment(uint256 commentId, string memory newContent) external {
require(commentId < nextCommentId, "Invalid comment ID");
Comment storage comment = comments[commentId];
require(
comment.author == msg.sender,
"Only the author can edit the comment"
);
comment.content = newContent;
emit CommentEdited(commentId, newContent);
}
function deleteComment(uint256 commentId) external {
require(commentId < nextCommentId, "Invalid comment ID");
Comment storage comment = comments[commentId];
require(
comment.author == msg.sender,
"Only the author can delete the comment"
);
delete comments[commentId];
emit CommentDeleted(commentId);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment