pragma solidity ^0.4.23; contract ERC20Basic { function totalSupply() public view returns (uint256); function balanceOf(address who) public view returns (uint256); function transfer(address to, uint256 value) public returns (bool); event Transfer(address indexed from, address indexed to, uint256 value); } contract ERC20 is ERC20Basic { function allowance(address owner, address spender) public view returns (uint256); function transferFrom(address from, address to, uint256 value) public returns (bool); function approve(address spender, uint256 value) public returns (bool); event Approval( address indexed owner, address indexed spender, uint256 value ); } contract SafeMathLib { function safeMul(uint a, uint b) constant returns (uint) { uint c = a * b; assert(a == 0 || c / a == b); return c; } function safeSub(uint a, uint b) constant returns (uint) { assert(b <= a); return a - b; } function safeAdd(uint a, uint b) constant returns (uint) { uint c = a + b; assert(c>=a); return c; } } //TODO: decimal point in dollar data handling contract FanTokenFactory { address appContract; modifier onlyAppContract() { require(msg.sender == appContract); _; } mapping (uint => address) public FanTokenOf; //Here, the uint is same as artist ID. THis is done to help maintain consistency in the IDs. event fanTokenCreated(uint _artistID,address tokenAddress, string _name); event userTokenBalanceUpdated(uint tokenBalance,address userAddress); constructor () public { appContract = msg.sender; } //We'll name the token as teh artist name as, to create custom token names we need to concatenate strings which consumes unecessary gas as it needss to be done manually. for reference:https://stackoverflow.com/questions/32157648/string-concatenation-in-solidity function createFanToken(uint _artistID, string _name, address _artistAddress) public onlyAppContract { require(FanTokenOf[_artistID] == 0); FanTokenOf[_artistID] = new FanToken(_name, _artistID, _artistAddress,appContract); fanTokenCreated(_artistID, FanTokenOf[_artistID], _name); } // getAll balances for user along with addresses function getTokenAddressOf(uint artistID) public view returns(address) { return FanTokenOf[artistID]; } // function updateUserBalanceOf(address _userAdr, uint _artistID, int _change) internal { // user[_userAdr].fanTokenBalanceOf[_artistID] = safeAdd(user[_userAdr].fanTokenBalanceOf[_artistID], _change); // userTokenBalanceUpdated(user[_userAdr].fanTokenBalanceOf[_artistID], msg.sender); // } function getAllTokensOwnedByUser() public returns(address[]) { address[] tokenAddresses; uint i = 0; for (i = 0;i < musicApp(appContract).getTotalArtists(); i++) { if(FanToken(FanTokenOf[i]).balanceOf(msg.sender) != 0) { tokenAddresses.push(getTokenAddressOf(i)); } } return tokenAddresses; } } contract FanToken is ERC20, SafeMathLib { FanTokenFactory factory; address ownerArtist; address appAddress; string name; uint artistID; uint currentSupply; uint TotalSupply; uint polls; mapping (address => uint) userBalanceOf; mapping (uint => _poll) poll; enum _pollStatus {LIVE, PAST} struct _poll { string question; mapping (uint => string) optionText; mapping (uint => uint) optionVotes; _pollStatus pollStatus; uint expiryVotes; uint votesPolled; mapping(address => bool) userVoted; } constructor(string _name, uint _artistID, address _artistAddress, address _appAddress) public { factory = FanTokenFactory(msg.sender); appAddress = _appAddress; name = _name; ownerArtist = _artistAddress; artistID = _artistID; polls = 0; } modifier onlyOwnerArtist() { require(msg.sender == ownerArtist); _; } //In doubt if i should implement this // modifier userOwnsTokens() { // require(msg.sender); // _; // } modifier userHasTokens() { require(balanceOf(msg.sender) != 0); _; } modifier userHasVoted(uint _pollID) { require(poll[_pollID].userVoted[msg.sender] == true); _; } modifier onlyApp() { require(msg.sender == appAddress); _; } event pollCreated(string que,uint pollID, address artistAddress); event voteAdded(uint pollID,uint optionID,uint tokens,address userAddress); event tokensTransferred(address from,address to,uint amount); //--//--//--//--//--//--//--//--//Artist Side//--//--//--//--//--//--//--//--//--//--//-- function createPoll(string _que, string _option1, string _option2, string _option3, string _option4, uint _expiryVotes) onlyOwnerArtist public returns (uint pollId) { poll[polls].question = _que; poll[polls].optionText[0] = _option1; poll[polls].optionVotes[0] = 0; poll[polls].optionText[1] = _option2; poll[polls].optionVotes[1] = 0; poll[polls].optionText[2] = _option3; poll[polls].optionVotes[2] = 0; poll[polls].optionText[3] = _option4; poll[polls].optionVotes[3] = 0; poll[polls].pollStatus = _pollStatus.LIVE; poll[polls].expiryVotes = _expiryVotes; pollCreated(_que, polls, msg.sender); polls++; } //List of all live polls //Workaround to return dynamic sized arrays from solidity function getNumberOfLivePolls() internal returns(uint) { uint counter = 0; for (var i = 0; i < polls; i++) { if(poll[i].pollStatus == _pollStatus.LIVE) { counter++; } } return counter; } function getLivePolls() public view onlyOwnerArtist returns(uint[]) { uint j = 0; uint[] memory livePolls = new uint[](getNumberOfLivePolls()); for (uint i = 0; i < polls; i++) { if(poll[i].pollStatus == _pollStatus.LIVE) { livePolls[j] = i; j++; } } return livePolls; } function getNumberOfPastPolls() internal returns(uint) { uint counter = 0; for (var i = 0; i < polls; i++) { if(poll[i].pollStatus == _pollStatus.PAST) { counter++; } } return counter; } function getPastPolls() public view onlyOwnerArtist returns(uint[]) { uint j = 0; uint[] memory pastPolls = new uint[](getNumberOfPastPolls()); for (uint i = 0; i < polls; i++) { if(poll[i].pollStatus == _pollStatus.PAST) { pastPolls[j] = i; j++; } } return pastPolls; } //Return variables of struct function getPollDetails(uint id) public view onlyOwnerArtist returns(string, string, uint, string, uint, string, uint, string, uint, uint, uint, bool) { bool pollActive; if(poll[id].pollStatus == _pollStatus.LIVE) { pollActive = true; } else { pollActive = false; } return ( poll[id].question, poll[id].optionText[0], poll[id].optionVotes[0], poll[id].optionText[1], poll[id].optionVotes[1], poll[id].optionText[2], poll[id].optionVotes[2], poll[id].optionText[3], poll[id].optionVotes[3], poll[id].votesPolled, poll[id].expiryVotes, pollActive //Check if this works ); } //Artist can view how many users own how many tokens and also total tokens used function getTokenDetails() public view onlyOwnerArtist returns(uint TotalSupply, uint currentSupply, uint burntTokens) { return (TotalSupply, currentSupply, safeSub(TotalSupply, currentSupply)); } //--//--//--//--//--//--//--//--//User Side//--//--//--//--//--//--//--//--//--//--//-- //TODO; userownstokens implementation function balanceOf(address _adr) public view returns (uint) { return userBalanceOf[_adr]; } function castVote(uint _pollID, uint _optionID, uint _tokens) userHasTokens public { require(balanceOf(msg.sender) >= _tokens); require(checkPollStatus(_pollID)); require(_pollID <= polls); require(_optionID >= 0 && _optionID < 4); poll[_pollID].optionVotes[_optionID]++; poll[_pollID].votesPolled++; userBalanceOf[msg.sender] = safeSub(userBalanceOf[msg.sender], _tokens); //factory.updateUserBalanceOf(msg.sender, artistID, safeMul(-1,_tokens)); //Check For integrity currentSupply = safeSub(currentSupply, _tokens); poll[_pollID].userVoted[msg.sender] = true; voteAdded(_pollID, _optionID, _tokens, msg.sender); } function getPastPollResult(uint _pollID) userHasVoted(_pollID) public view returns(string, string, uint, string, uint, string, uint, string, uint,uint, uint) { require(checkPollStatus(_pollID)); return (poll[_pollID].question, poll[_pollID].optionText[0], poll[_pollID].optionVotes[0], poll[_pollID].optionText[1], poll[_pollID].optionVotes[1], poll[_pollID].optionText[2], poll[_pollID].optionVotes[2], poll[_pollID].optionText[3], poll[_pollID].optionVotes[3], poll[_pollID].votesPolled, poll[_pollID].expiryVotes ); } //Check again function transferTokens(address to, uint _tokens) userHasTokens public { require(balanceOf(msg.sender) >= _tokens); userBalanceOf[msg.sender] = safeSub(userBalanceOf[msg.sender], _tokens); userBalanceOf[to] = safeAdd(userBalanceOf[to], _tokens); // factory.updateUserBalanceOf(msg.sender, artistID, safeMul(-1,_tokens)); // factory.updateUserBalanceOf(to, artistID, _tokens); tokensTransferred(msg.sender, to, _tokens); } function checkPollStatus(uint _pollID) internal returns (bool status) { if(poll[_pollID].votesPolled >= poll[_pollID].expiryVotes) { poll[_pollID].pollStatus = _pollStatus.PAST; } if(poll[_pollID].pollStatus == _pollStatus.LIVE) { return true; } else { return false; } } function addToken(address userAdr) public view onlyApp { userBalanceOf[userAdr] = safeAdd(userBalanceOf[userAdr], 1); TotalSupply++; currentSupply++; } } contract musicApp is SafeMathLib { //Global Variables struct _song { string songHash; uint songID; string name; uint artistID; //Might be redundant string songArtHash; uint256 priceInD; uint256 totalSold; uint256 totalRevenues; uint256 latestWeiPrice; } struct _artist { string artistDPHash; uint id; string name; string description; address adr; uint[] songs; // Keep track of all songs the particular artist has made. List of song IDs. uint totalEarningsInD; } struct _user { string name; uint[] songsOwned; //We cannot have an array of strings + mapping - index to song ids uint userID; // Same as mapping index //TODO: Token ownership logic //mapping(uint => uint) fanTokenBalanceOf; uint totalSongsOwned; } //Artist mappings mapping (uint => address) private artistAddressOf; mapping (address => _artist) private artist; uint artists = 0; //Song Mappings mapping (uint => string) private songHashOf; mapping (uint => _song) private song; //mapping of song ID to song struct as it is the most frequently passed parameter uint songs = 0; mapping(uint => address) artistOf; //Maps songIDs to artist address //User mappings mapping (uint => address) userAddressOf; mapping (address => _user) user; uint users = 0; FanTokenFactory tokenFactory; //Events //Modifiers modifier validArtist() { require(artist[msg.sender].id > 0 && artist[msg.sender].id < artists); require(artistAddressOf[artist[msg.sender].id] == msg.sender); //Cross checking _; } modifier ownsSong(uint _songID) { require(user[msg.sender].userID > 0 && user[msg.sender].userID < users); //Check if valid user uint f = 0; uint i = 0; while(user[msg.sender].songsOwned[i] != 0) { //Check condition. Default value for dynamic array solidity if(user[msg.sender].songsOwned[i] == _songID) { f = 1; break; } i++; } require(f==1); _; } modifier validUser(address _adr) { require(user[_adr].userID > 0 && user[_adr].userID < users); _; } //events event singleArtistRegistered(string _name, uint artistID); event songAdded(string _title,uint _priceInD,uint songID); event userRegistered(string _name,address userAddress,uint userID); event songBuySuccessful(uint _songID,address userAddress); // // Helper Functions // function compareStrings (string a, string b) public view returns (bool){ // return keccak256(a) == keccak256(b); // } //constructor // TODO: Define more state variables if needed and initialize them here constructor () { // tokenFactory = new FanTokenFactory(); } function addFactoryTokenAddress(address _tokenFactory) public { // require(tokenFactory == 0); Find another way to check this tokenFactory = FanTokenFactory(tokenFactory); } uint constant weiThreshold = 698844576966082; // 30 cents- threshold for allowing people //Artist side functions //Create artist // TODO: Multisig implementation // Called when web3 indicates single owner account function registerSingleArtist(string _name, string _desc, string _pictureHash) public { artistAddressOf[artists] = msg.sender; artist[msg.sender].artistDPHash = _pictureHash; artist[msg.sender].name = _name; artist[msg.sender].description = _desc; artist[msg.sender].adr = msg.sender; artist[msg.sender].id = artists; tokenFactory.createFanToken(artists, _name, msg.sender); emit singleArtistRegistered(_name, artists); artists++; // songs } //Upload music function addSong(string _songHash, string _title, uint256 _priceInD, string _songArtHash) public validArtist { songHashOf[songs] = _songHash; artistOf[songs] = msg.sender; song[songs].songID = songs; song[songs].songHash = _songHash; song[songs].name = _title; song[songs].artistID = artist[msg.sender].id; song[songs].priceInD = dollarToWei(_priceInD); song[songs].songArtHash = _songArtHash; emit songAdded(_title, _priceInD, songs); songs++; } function checkTotalRevenues() view public validArtist returns (uint) { return artist[msg.sender].totalEarningsInD; } function checkSongRevenues(uint _songID) view public validArtist returns (uint) { return song[_songID].totalRevenues; } //WIthdraw fund function to be implemented with multisig wallet function dollarToWei(uint _dollars) public returns(uint) { return 123; } //--//--//--//--//--//--//--//--//--//User side//--//--//--//--//--//--//--//--//--//--//-- function registerUser(string _name) public { user[msg.sender].name = _name; userAddressOf[users] = msg.sender; user[msg.sender].userID = users; emit userRegistered(_name, msg.sender, users); users++; } function initiateBuy(uint _songID) validUser(msg.sender) public returns(uint256) { song[_songID].latestWeiPrice = dollarToWei(song[_songID].priceInD); return song[_songID].latestWeiPrice; } //Buy music - ETH-$ concepth function buySong(uint _songID) validUser(msg.sender) public payable { uint256 latestPrice = song[_songID].latestWeiPrice; require(msg.value < safeAdd(latestPrice, weiThreshold) && msg.value > safeSub(latestPrice, weiThreshold)); this.transfer(msg.value); //TODO: require confirmation of funds transferred user[msg.sender].totalSongsOwned = user[msg.sender].songsOwned.push(_songID); song[_songID].totalSold++; song[_songID].totalRevenues = safeAdd(song[_songID].totalRevenues, song[_songID].priceInD); artist[artistOf[_songID]].totalEarningsInD = safeAdd(artist[artistOf[_songID]].totalEarningsInD, song[_songID].priceInD); FanToken fanToken = FanToken(tokenFactory.getTokenAddressOf(artist[artistOf[_songID]].id)); fanToken.addToken(msg.sender); emit songBuySuccessful(_songID, msg.sender); } function getSongHashByID(uint _songID) public view ownsSong(_songID) returns(string) { return songHashOf[_songID]; } function getSongDetails(uint _songID) public view ownsSong(_songID) returns(string, string, string, string) { return (songHashOf[_songID],song[_songID].name, song[_songID].songArtHash, artist[artistOf[_songID]].name); } //Vote - token contract // Display library functions function getUserSongList() public view validUser(msg.sender) returns (uint[]) { uint[] memory songList = new uint[](user[msg.sender].totalSongsOwned); songList = user[msg.sender].songsOwned; //Check if this works return songList; } // Display store list //Check if this is needed. App will have a list of all song ids with it at all times // Dont display IDs that are akready owned by user. function getStoreSongList() public view validUser(msg.sender) returns (uint[]) { uint[] memory allSongList = new uint[](songs); //Check how to make this work? uint j=0; for(uint i = 0; i < songs; i++){ if(!userOwns(i)) { allSongList[j] = i; j++; } } return allSongList; } function userOwns(uint _songId) ownsSong(_songId) internal returns(bool) { return true; } function getSongStoreDetails(uint _songID) public view validUser(msg.sender) returns(string, string, string, uint) { return (song[_songID].name, song[_songID].songArtHash, artist[artistOf[_songID]].name, song[_songID].priceInD); } //TODO:upgrade logic //TODO: Self destruct function () payable public { } function getTotalArtists() public view returns(uint) { return artists; } }
0.4.23