Title: Shift_control_of_the_nft_token_away_from_the_marke
Category: Magicians

Author: codetributor
Date: 2024-01-09T06:50:11.612Z

pragma solidity >=0.8.0 <0.9.0;

contract Royalty {

	/**
	 * @dev I am not too familar with ERC token standards, but what if we shift the control of the token away from the marketplaces.
	 * I am suggesting to remove the transfer and approve functionality
	 * 
	 * in place of that functionality we give the token owner the ability to change the boolean value of claimble;
	 * we give the token owner the ability to change the value of the token price;
	 * and we give the token owner the option to specify the claimant;
	 * 
	 * if the claimable boolean is true, then anyone can call the claim function(if there is no specified claimant) and send value in accordance to the token price
	 * so essencially we are cutting out the marketplace and the claimant calls the nft contract directly
	 * then we are able to enforce paying out the royalties
	 * 
	 * if the token holder does not want to sell or transfer the nft token, then can keep the claimable boolean to false
	 * and no one can claim the nft token
	 * 
	 * if the token owner wants to move the nft token to a different wallet, they can set the specified claimant to that wallet address
	 * they can set the price to zero
	 * then only that wallet can claim the nft token
	 * 
	 * the token owner has to specify the correct price, because if they try to do a side deal, the claimant will have the nft already
	 * and have no incentive to pay more to the previous nft token holder. The exchange would have happened at the specified price
	 * 
	 * if people try to use marketplaces as thirdparties and give the nft token to the marketplace at zero price and have them
	 * facilitate the trade, we can blacklist that address of the marketplace
	 */

	string private _name;
	string private _symbol;
	uint256 _nextTokenId = 0;

	struct Token {
		uint256 tokenId;
		address tokenOwner;
		bool claimable;
		address specifiedClaimant;
		bool isSpecifiedClaimant;
		uint256 price;
		string uri;
		uint256 royalty;
		address marketplaceAddress;
		uint marketplaceFee;
		bool isMarketplace;
	}

	mapping(address owner => Token[]) private _balances;
	mapping(uint256 tokenId => Token) private tokens;

	address[] blacklist;
	
	constructor(string memory name, string memory symbol) {
		_name = name;
		_symbol = symbol;
	}

	function checkIfBlacklisted(address _receipient) private view returns(bool) {
		for(uint i = 0; i < blacklist.length; i++) {
			if(_receipient == blacklist[i]) {
				return true;
			}
		}
		return false;
	}

	function mint() public {
		uint256 tokenId = _nextTokenId++;
		Token memory token = Token(tokenId, msg.sender, false, address(0), false, 0, "", 20, address(0), 0, false);
		tokens[tokenId] = token;
		_balances[msg.sender].push(token);
	}

	function makeClaimable(uint256 _tokenId, uint256 _price) public {
		require(tokens[_tokenId].tokenOwner == msg.sender, "you are not the owner");
		tokens[_tokenId].claimable = true;
		tokens[_tokenId].price = _price;
	}

	function changePrice(uint256 _tokenId, uint256 _price) public {
		require(tokens[_tokenId].tokenOwner == msg.sender, "you are not the owner");
		tokens[_tokenId].price = _price;
	}

	function setSpecifiedClaimant(address _specifiedClaimant, uint256 _tokenId) public {
		require(tokens[_tokenId].tokenOwner == msg.sender, "you are not the owner");
		require(checkIfBlacklisted(_specifiedClaimant) == false, "blacklisted");
		tokens[_tokenId].isSpecifiedClaimant = true; 
		tokens[_tokenId].specifiedClaimant = _specifiedClaimant;
	}

	function addToMarketplace(uint256 _tokenId, address _marketAddress, uint256 _marketFee) public {
		tokens[_tokenId].isMarketplace = true;
		tokens[_tokenId].marketplaceFee = _marketFee;
		tokens[_tokenId].marketplaceAddress = _marketAddress;
	}

	function claim(uint256 _tokenId) public payable {
		require( tokens[_tokenId].price <= msg.value, "you did not send enough currency");
		uint royaltyPercentage = tokens[_tokenId].royalty;
		uint sellValue = msg.value * (100 - royaltyPercentage)/100;
		uint royaltyFee= msg.value * royaltyPercentage/100;
		payout(_tokenId, royaltyFee, sellValue);
		
	}

	function payout(uint256 _tokenId, uint256 _royaltyFee, uint256 _sellValue) public {
		if(tokens[_tokenId].isSpecifiedClaimant) {
			require(checkIfBlacklisted(msg.sender) == false, "blacklisted");
			require(tokens[_tokenId].specifiedClaimant == msg.sender, "you are not the specified claimant");
			(bool success0, ) = tokens[_tokenId].tokenOwner.call{value: _sellValue}(""); 
			(bool success1, ) = address(this).call{value: _royaltyFee}("");
			removeToken(_tokenId, tokens[_tokenId].tokenOwner);
			tokens[_tokenId].tokenOwner = msg.sender;
			require(success0 && success1, "distribution did not go through");
		} else {
			(bool success0, ) = tokens[_tokenId].tokenOwner.call{value: _sellValue}(""); 
			(bool success1, ) = address(this).call{value: _royaltyFee}("");
			removeToken(_tokenId, tokens[_tokenId].tokenOwner);
			tokens[_tokenId].tokenOwner = msg.sender;
			require(success0 && success1, "distribution did not go through");
		}
	}

	function removeToken(uint256 _tokenId, address _previousOwner) public {
		for(uint i = 0; i < _balances[_previousOwner].length; i++) {
			if(_tokenId == _balances[_previousOwner][i].tokenId) {
				_balances[_previousOwner][i] = _balances[_previousOwner][_balances[_previousOwner].length - 1];
				_balances[_previousOwner].pop();
			}
		}
	}
}```