Content is user-generated and unverified.
// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "@openzeppelin/contracts/token/common/ERC2981.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; import "@openzeppelin/contracts/utils/Counters.sol"; /** * @title RoyaltyShareToken * @dev Telif haklarını temsil eden ERC-20 token */ contract RoyaltyShareToken is ERC20 { uint256 public immutable contentTokenId; address public immutable contentContract; constructor( string memory name, string memory symbol, uint256 _contentTokenId, address _contentContract, uint256 initialSupply ) ERC20(name, symbol) { contentTokenId = _contentTokenId; contentContract = _contentContract; _mint(msg.sender, initialSupply); } } /** * @title TransferableRoyaltyNFT * @dev ERC-2981 ile telif ödemesi alan ve teliflerini tokenize edebilen NFT */ contract TransferableRoyaltyNFT is ERC721, ERC2981, Ownable, ReentrancyGuard { using Counters for Counters.Counter; Counters.Counter private _tokenIds; struct RoyaltyInfo { address paymentSplitter; address shareToken; uint256 totalRoyaltiesEarned; uint96 royaltyPercentage; // Basis points (500 = 5%) bool isTokenized; } // Token ID => Royalty bilgileri mapping(uint256 => RoyaltyInfo) public royaltyInfos; // Token ID => Pending royalty payments mapping(uint256 => uint256) public pendingRoyalties; // Share token holders => Token ID => Claimable amount mapping(address => mapping(uint256 => uint256)) public claimableRoyalties; event RoyaltyPaid(uint256 indexed tokenId, address payer, uint256 amount); event RoyaltyTokenized(uint256 indexed tokenId, address shareToken, uint256 totalShares); event RoyaltyClaimed(uint256 indexed tokenId, address claimer, uint256 amount); event RoyaltySharesTraded(uint256 indexed tokenId, address from, address to, uint256 amount); constructor() ERC721("ContentRoyaltyNFT", "CRNFT") {} /** * @dev Yeni NFT mint et ve telif ayarlarını yap * @param to Alıcı adres * @param uri Metadata URI * @param royaltyPercentage Telif yüzdesi (basis points) */ function mintWithRoyalty( address to, string memory uri, uint96 royaltyPercentage ) public returns (uint256) { require(royaltyPercentage <= 10000, "Royalty too high"); _tokenIds.increment(); uint256 newTokenId = _tokenIds.current(); _safeMint(to, newTokenId); _setTokenRoyalty(newTokenId, to, royaltyPercentage); royaltyInfos[newTokenId] = RoyaltyInfo({ paymentSplitter: address(0), shareToken: address(0), totalRoyaltiesEarned: 0, royaltyPercentage: royaltyPercentage, isTokenized: false }); return newTokenId; } /** * @dev Telif haklarını tokenize et (hisse tokenları oluştur) * @param tokenId NFT token ID * @param totalShares Toplam hisse sayısı */ function tokenizeRoyalties( uint256 tokenId, uint256 totalShares ) public returns (address) { require(ownerOf(tokenId) == msg.sender, "Not owner"); require(!royaltyInfos[tokenId].isTokenized, "Already tokenized"); require(totalShares > 0, "Invalid share amount"); // Payment splitter oluştur RoyaltyPaymentSplitter splitter = new RoyaltyPaymentSplitter( tokenId, address(this) ); // Hisse tokenları oluştur string memory name = string(abi.encodePacked("Royalty Share #", toString(tokenId))); string memory symbol = string(abi.encodePacked("RS", toString(tokenId))); RoyaltyShareToken shareToken = new RoyaltyShareToken( name, symbol, tokenId, address(this), totalShares ); // Tüm hisseleri mint eden'e ver // (shareToken constructor'da zaten mint ediyor) // Royalty info güncelle royaltyInfos[tokenId].paymentSplitter = address(splitter); royaltyInfos[tokenId].shareToken = address(shareToken); royaltyInfos[tokenId].isTokenized = true; // ERC-2981 royalty receiver'ı splitter'a ayarla _setTokenRoyalty(tokenId, address(splitter), royaltyInfos[tokenId].royaltyPercentage); emit RoyaltyTokenized(tokenId, address(shareToken), totalShares); return address(shareToken); } /** * @dev NFT satışından telif ödemesi al * @param tokenId NFT token ID * @param salePrice Satış fiyatı */ function payRoyalty(uint256 tokenId, uint256 salePrice) external payable nonReentrant { (address receiver, uint256 royaltyAmount) = royaltyInfo(tokenId, salePrice); require(msg.value >= royaltyAmount, "Insufficient royalty payment"); require(receiver != address(0), "No royalty set"); RoyaltyInfo storage info = royaltyInfos[tokenId]; info.totalRoyaltiesEarned += royaltyAmount; if (info.isTokenized) { // Tokenize edilmiş - payment splitter'a gönder pendingRoyalties[tokenId] += royaltyAmount; payable(receiver).transfer(royaltyAmount); } else { // Normal - doğrudan sahibine payable(receiver).transfer(royaltyAmount); } // Fazla ödemeyi iade et if (msg.value > royaltyAmount) { payable(msg.sender).transfer(msg.value - royaltyAmount); } emit RoyaltyPaid(tokenId, msg.sender, royaltyAmount); } /** * @dev Hisse sahiplerinin telif gelirini claim etmesi * @param tokenId NFT token ID */ function claimRoyaltyShare(uint256 tokenId) external nonReentrant { RoyaltyInfo memory info = royaltyInfos[tokenId]; require(info.isTokenized, "Royalties not tokenized"); RoyaltyShareToken shareToken = RoyaltyShareToken(info.shareToken); uint256 userShares = shareToken.balanceOf(msg.sender); require(userShares > 0, "No shares owned"); RoyaltyPaymentSplitter splitter = RoyaltyPaymentSplitter( payable(info.paymentSplitter) ); uint256 claimable = splitter.getClaimableAmount(msg.sender); require(claimable > 0, "No royalties to claim"); splitter.claim(msg.sender); emit RoyaltyClaimed(tokenId, msg.sender, claimable); } /** * @dev Hisse transferinde telif haklarını güncelle */ function _beforeTokenTransfer( address from, address to, uint256 tokenId, uint256 batchSize ) internal virtual override { super._beforeTokenTransfer(from, to, tokenId, batchSize); // Eğer tokenize edilmişse, transfer yapılamaz // Sadece hisse tokenları transfer edilebilir if (royaltyInfos[tokenId].isTokenized && from != address(0)) { revert("Cannot transfer tokenized NFT. Trade royalty shares instead."); } } /** * @dev Token URI (placeholder) */ function tokenURI(uint256 tokenId) public view override returns (string memory) { require(_exists(tokenId), "Token does not exist"); return string(abi.encodePacked("ipfs://", toString(tokenId))); } /** * @dev Utility function: uint256 to string */ function toString(uint256 value) internal pure returns (string memory) { if (value == 0) { return "0"; } uint256 temp = value; uint256 digits; while (temp != 0) { digits++; temp /= 10; } bytes memory buffer = new bytes(digits); while (value != 0) { digits -= 1; buffer[digits] = bytes1(uint8(48 + uint256(value % 10))); value /= 10; } return string(buffer); } // ERC-2981 ve ERC-721 interface desteği function supportsInterface(bytes4 interfaceId) public view override(ERC721, ERC2981) returns (bool) { return super.supportsInterface(interfaceId); } } /** * @title RoyaltyPaymentSplitter * @dev Telif ödemelerini hisse sahiplerine dağıt */ contract RoyaltyPaymentSplitter is ReentrancyGuard { uint256 public immutable tokenId; address public immutable nftContract; uint256 public totalReceived; uint256 public totalReleased; mapping(address => uint256) public released; event PaymentReceived(address from, uint256 amount); event PaymentReleased(address to, uint256 amount); constructor(uint256 _tokenId, address _nftContract) { tokenId = _tokenId; nftContract = _nftContract; } receive() external payable { totalReceived += msg.value; emit PaymentReceived(msg.sender, msg.value); } /** * @dev Kullanıcının claim edebileceği miktarı hesapla */ function getClaimableAmount(address account) public view returns (uint256) { TransferableRoyaltyNFT nft = TransferableRoyaltyNFT(nftContract); (,, address shareTokenAddr,,) = nft.royaltyInfos(tokenId); RoyaltyShareToken shareToken = RoyaltyShareToken(shareTokenAddr); uint256 totalShares = shareToken.totalSupply(); uint256 userShares = shareToken.balanceOf(account); if (totalShares == 0 || userShares == 0) return 0; uint256 totalPayment = (totalReceived * userShares) / totalShares; uint256 alreadyReleased = released[account]; return totalPayment > alreadyReleased ? totalPayment - alreadyReleased : 0; } /** * @dev Telif ödemesini claim et */ function claim(address account) external nonReentrant { uint256 payment = getClaimableAmount(account); require(payment > 0, "No payment due"); released[account] += payment; totalReleased += payment; payable(account).transfer(payment); emit PaymentReleased(account, payment); } }
Content is user-generated and unverified.
    ERC-2981 NFT Royalty Tokenization Smart Contract System | Claude