diff --git a/.gitmodules b/.gitmodules index f19c139..f2bae02 100644 --- a/.gitmodules +++ b/.gitmodules @@ -12,3 +12,6 @@ [submodule "lib/cyberconnect"] path = lib/cyberconnect url = https://github.com/cyberconnecthq/cybercontracts.git +[submodule "lib/@openzeppelin-upgradeable"] + path = lib/@openzeppelin-upgradeable + url = https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable.git diff --git a/lib/@openzeppelin-upgradeable b/lib/@openzeppelin-upgradeable new file mode 160000 index 0000000..dd8ca8a --- /dev/null +++ b/lib/@openzeppelin-upgradeable @@ -0,0 +1 @@ +Subproject commit dd8ca8adc47624c5c5e2f4d412f5f421951dcc25 diff --git a/remappings.txt b/remappings.txt index b395073..e2b1250 100644 --- a/remappings.txt +++ b/remappings.txt @@ -2,5 +2,4 @@ forge-std/=lib/forge-std/src/ @openzeppelin/=lib/@openzeppelin/contracts/ solmate/=lib/solmate/src/ cyberconnect/=lib/cyberconnect/src/ -openzeppelin-contracts/=lib/openzeppelin-contracts/ -chainlink/=lib/chainlink/ +@openzeppelin-upgradeable/=lib/@openzeppelin-upgradeable/contracts/ diff --git a/src/Ghosts.sol b/src/Ghosts.sol index d5c52bd..9d2ec90 100644 --- a/src/Ghosts.sol +++ b/src/Ghosts.sol @@ -1,334 +1,476 @@ -// // SPDX-License-Identifier: MIT -// pragma solidity ^0.8.0; - -// import {ERC721} from "@openzeppelin/token/ERC721/ERC721.sol"; -// import {IERC721} from "@openzeppelin/token/ERC721/IERC721.sol"; -// import {Ownable} from "@openzeppelin/access/Ownable.sol"; -// import {ERC721Enumerable} from "@openzeppelin/token/ERC721/extensions/ERC721Enumerable.sol"; -// import {ERC721Burnable} from "@openzeppelin/token/ERC721/extensions/ERC721Burnable.sol"; -// import {Counters} from "@openzeppelin/utils/Counters.sol"; -// import {Strings} from "@openzeppelin/utils/Strings.sol"; -// import "@openzeppelin/token/ERC721/IERC721Receiver.sol"; - -// import {DataTypes, IProfileNFT} from "./interfaces/IProfileNFT.sol"; -// import {IGhosts, IGhostsData} from './interfaces/IGhosts.sol'; -// import {GhostsFeats} from './GhostsFeats.sol'; - - -// contract Ghosts is GhostsFeats, IERC721Receiver, ERC721, ERC721Enumerable, ERC721Burnable, Ownable { -// using Counters for Counters.Counter; -// using Strings for uint256; -// Counters.Counter private _tokenIdCounter; -// Counters.Counter public _userCounter; +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {DataTypes, IProfileNFT} from "./interfaces/IProfileNFT.sol"; +import {IGhosts, IGhostsData} from './interfaces/IGhosts.sol'; +import {GhostsFeats} from './GhostsFeats.sol'; + +import "@openzeppelin-upgradeable/token/ERC721/ERC721Upgradeable.sol"; +import "@openzeppelin-upgradeable/token/ERC721/extensions/ERC721EnumerableUpgradeable.sol"; +import "@openzeppelin-upgradeable/access/AccessControlUpgradeable.sol"; +import "@openzeppelin-upgradeable/proxy/utils/Initializable.sol"; +import "@openzeppelin-upgradeable/proxy/utils/UUPSUpgradeable.sol"; +import "@openzeppelin-upgradeable/utils/CountersUpgradeable.sol"; +import "@openzeppelin-upgradeable/utils/StringsUpgradeable.sol"; +import "@openzeppelin-upgradeable/token/ERC721/IERC721Upgradeable.sol"; +import "@openzeppelin-upgradeable/token/ERC721/IERC721ReceiverUpgradeable.sol"; +import "forge-std/Test.sol"; + +import "@openzeppelin/proxy/ERC1967/ERC1967Proxy.sol"; +contract UUPSProxy is ERC1967Proxy { + constructor(address _implementation, bytes memory _data) + ERC1967Proxy(_implementation, _data) + {} +} + +contract Ghosts is + Initializable, + ERC721Upgradeable, + ERC721EnumerableUpgradeable, + AccessControlUpgradeable, + GhostsFeats, + IERC721ReceiverUpgradeable + { + using CountersUpgradeable for CountersUpgradeable.Counter; + using StringsUpgradeable for uint256; + CountersUpgradeable.Counter private _tokenIdCounter; + CountersUpgradeable.Counter public _userCounter; -// IProfileNFT internal constant ProfileNFT = -// IProfileNFT(0x57e12b7a5F38A7F9c23eBD0400e6E53F2a45F271); - -// string public tokenBaseURI; -// uint internal raceCount; - -// mapping(address=>IGhostsData.User) public userMap; // used for address(0) and ownership checks -// mapping(uint=>IGhostsData.User) public idToUser; -// mapping(address=>IGhostsData.WarmUpNFT) private warmUpNFTs; // used to store the User's current Warmup NFT (if any) -// mapping(address=>IGhostsData.RaceNFT) private raceNFTs; // used to store the User's current Race NFT (if any) - -// mapping(uint=>IGhostsData.RaceNFT) public finalRaceNfts; // stores the final race NFT for each race to compare against - -// mapping(uint=>bool) private graduatedNFTs; // "pops" a warmUp NFT and upgrades it to a RaceNFT. URI relies on this. -// mapping(uint=>uint) private tokenIdToRaceId; // gates access to uncompleted races. URI relies on this. - -// error IncorrectSubmission(); -// error AccountExists(address who, uint ccID); -// error AlreadySubmitted(uint raceID); - -// event RaceCreated(uint indexed id); -// event UserCreated(address indexed who, uint indexed ccID); -// event RaceCompleted(address indexed who, uint indexed raceID, uint indexed ccID); -// event RaceStarted(address indexed who, uint indexed raceID, uint indexed ccID); - - -// /** -// * @dev hashes are imprinted into finalRaceNFTs for comparison for submissions. -// * @param dunno bytes32[] of hashes for the initial round of race content. -// */ -// constructor(bytes32[] memory dunno) ERC721("TEST", "TST") payable { -// uint len = dunno.length; -// raceCount = len; -// for(uint x = 0; x < len; x++){ -// finalRaceNfts[x] = IGhostsData.RaceNFT({ -// submittedAnswers: bytes32('0x'), -// answer: dunno[x], -// performance: 0, -// currentTaskId: x, -// tokenId: x, -// userAddress: address(0) -// }); -// } -// tokenBaseURI = "ipfs://QmU3hHax9mtBJcWD3JvS2uDSdpvjATCWkdR3kwxEfg54bw/"; -// ghostsAddr = address(this); -// } - -// ///////////////////////////////// -// /// /// -// /// External Functions /// -// /// /// -// ///////////////////////////////// - - -// function ccGhostMaker(string memory ghosts, string[] memory hashes) external onlyOwner { -// uint id = createGhostsCC(ghosts, hashes, address(this)); -// ghostsCCID = id; -// } - -// function ccAchievementMaker( -// string calldata name, -// string calldata symbol, -// string[] calldata essenceURI, -// string calldata description, -// uint16 weight, -// uint16 tier -// ) external onlyOwner { -// for(uint x = 0; x < essenceURI.length; ++x){ -// createAchievements(name, symbol, essenceURI[x], description, weight , tier); -// } -// } - -// /** -// * @param races of race content hashes -// */ -// function addRaces(bytes32[] memory races) external onlyOwner { -// uint r = raceCount; -// uint s = races.length; -// for(uint x = r; x < r; ++x){ -// finalRaceNfts[x] = IGhostsData.RaceNFT({ -// submittedAnswers: bytes32('0x'), -// answer: races[x], -// performance: 0, -// currentTaskId: x, -// tokenId: x, -// userAddress: address(0) -// }); - -// } -// raceCount += s; -// } - -// /** -// * @param uri of new IPFS URI "ipfs://hash..../" -// */ -// function setBaseURI(string calldata uri) external onlyOwner { -// tokenBaseURI = uri; -// } - -// /** -// * @dev Metadata is reliant on graduatedNFTs[id] checks -// * @dev TokenId is uncapped but raceIDs are aren't so tokenIdToRaceId ensures the metadata for the correct race is returned -// * @param id of the token -// */ -// function tokenURI(uint id) public view override returns (string memory) { -// require(_exists(id), "ERC721Metadata: URI query for nonexistent token"); -// uint tokenRaceId = tokenIdToRaceId[id]; - -// tokenRaceId++; - -// if(!graduatedNFTs[id]){ -// return string(abi.encodePacked(_baseURI(), "WarmUpNFT", tokenRaceId.toString(), ".json")); -// }else{ -// return string(abi.encodePacked(_baseURI(), "RaceNFT", tokenRaceId.toString(), ".json")); -// } -// } - -// /** -// * @dev creates a profile with Ghosts as well as minting a CC NFT to the msg.sender. -// * @param handle of the user -// * @param hashes of profiles hash[0]: avatar, hash[1]: metadata -// */ -// function createUser(string memory handle, string[] memory hashes) external { -// uint ccID = userMap[msg.sender].ccID; - -// if(ccID != 0){ -// revert AccountExists(msg.sender, ccID); -// } - -// _userCounter.increment(); + IProfileNFT internal constant ProfileNFT = + IProfileNFT(0x57e12b7a5F38A7F9c23eBD0400e6E53F2a45F271); + + string public tokenBaseURI; + uint internal raceCount; + uint internal collabCount; + + mapping(address=>IGhostsData.User) public userMap; // used for address(0) and ownership checks + mapping(uint=>IGhostsData.User) public idToUser; + mapping(address=>IGhostsData.WarmUpNFT) private warmUpNFT; // used to store the User's current Warmup NFT (if any) + mapping(address=>IGhostsData.RaceNFT) private raceNFT; // used to store the User's current Race NFT (if any) + mapping(address=>IGhostsData.CollabNFT) private collabNFT; // used to store the User's current Collab NFT (if any) + + mapping(uint=>IGhostsData.CollabNFT) public collabNFTs; // stores the final collab NFTs for each collab to compare against + mapping(uint=>IGhostsData.RaceNFT) public finalRaceNfts; // stores the final race NFT for each race to compare against + + mapping(uint=>bool) private graduatedNFTs; // "pops" a warmUp NFT and upgrades it to a RaceNFT. URI relies on this. + mapping(uint=>bool) private graduatedCollabNFTs; // "pops" a warmUp NFT and upgrades it to a CollabNFT. URI relies on this. + mapping(uint=>uint) private tokenIdToRaceId; // gates access to uncompleted races. URI relies on this. + mapping(uint=>uint) private tokenIdToCollabId; // tracks id > id for uri purposes + error IncorrectSubmission(); + error AccountExists(address who, uint ccID); + error AlreadySubmitted(uint raceID); + error FinishFirst(uint taskId); + error Soulbound(); + + event RaceCreated(uint indexed id); + event UserCreated(address indexed who, uint indexed ccID); + event RaceCompleted(address indexed who, uint indexed raceID, uint indexed ccID); + event RaceStarted(address indexed who, uint indexed raceID, uint indexed ccID); + event CollabStarted(address indexed user, uint indexed currentCollab); + + constructor() { + _disableInitializers(); + } + + /** + * @dev hashes are imprinted into finalRaceNFTs for comparison for submissions. + * @param dunno bytes32[] of hashes for the initial round of race content. + */ + function initialize(bytes32[] memory dunno) initializer public { + __ERC721_init("TESTTEST", "TST"); + __ERC721Enumerable_init(); + __AccessControl_init(); + __UUPSUpgradeable_init(); + __GhostsFeats_init(msg.sender); + + _grantRole(DEFAULT_ADMIN_ROLE, msg.sender); + _grantRole(UPGRADER_ROLE, msg.sender); + + uint len = dunno.length; + raceCount = len; + for(uint x = 0; x < len; x++){ + finalRaceNfts[x] = IGhostsData.RaceNFT({ + submittedAnswers: bytes32('0x'), + answer: dunno[x], + performance: 0, + currentTaskId: x, + tokenId: x, + userAddress: address(0) + }); + } + tokenBaseURI = "ipfs://QmU3hHax9mtBJcWD3JvS2uDSdpvjATCWkdR3kwxEfg54bw/"; + ghostsAddr = address(this); + } + + function _authorizeUpgrade(address newImplementation) + internal + onlyRole(UPGRADER_ROLE) + override + {} + + function getImplementation() external view returns (address) { + return _getImplementation(); + } + + ///////////////////////////////// + /// /// + /// External Functions /// + /// /// + ///////////////////////////////// + + + function ccGhostMaker(string memory ghosts, string[] memory hashes, address proxy) external onlyRole(UPGRADER_ROLE){ + ghostsAddr = proxy; + uint id = createGhostsCC(ghosts, hashes, proxy); + userIdToFollowed[proxy].push(0); // tests UserId + userIdToFollowers[proxy].push(0); // tests UserId + } + + function ccAchievementMaker( + string calldata name, + string calldata symbol, + string[] calldata essenceURI, + string calldata description, + uint16 weight, + uint16 tier + ) external onlyRole(UPGRADER_ROLE) { + bytes memory payload = abi.encodeWithSignature( + "ccRegEssence(uint256, string, string, string, bytes32)", + ghostsCCID, name, symbol, essenceURI, address(0)); + for(uint x = 0; x < essenceURI.length; ++x){ + createAchievements(name, symbol, essenceURI[x], description, weight , tier, payload); + } + } + + /** + * @param races of race content hashes + */ + function addRaces(bytes32[] memory races) external onlyRole(UPGRADER_ROLE) { + uint r = raceCount; + uint s = races.length; + + for(uint x = r; x< r+s; ++x){ + console.log("step1", x); + for(uint y = 0; y< s; ++y){ + console.log("step2", y, (r+y)); + finalRaceNfts[r+y] = IGhostsData.RaceNFT({ + submittedAnswers: bytes32('0x'), + answer: races[y], + performance: 0, + currentTaskId: (r+y), + tokenId: (r+y), + userAddress: address(0) + }); + } + } + } + + function addCollab(bytes32[] calldata collab, bytes calldata author, bytes calldata title) external onlyRole(UPGRADER_ROLE) { + + uint r = collabCount == 0 ? 1 : collabCount; + collabCount = r; + collabNFTs[r] = IGhostsData.CollabNFT({ + author: author, + title: title, + answers: collab, + collabID: r, + tokenID: uint(0), + currentId: 0, + completed: false, + userAddress: address(0) + }); + collabCount ++; + userMap[msg.sender].ctfStbContribs++; + } + + + /** + * @param uri of new IPFS URI "ipfs://hash..../" + */ + function setBaseURI(string calldata uri) external onlyRole(UPGRADER_ROLE) { + tokenBaseURI = uri; + } + + /** + * @dev Metadata is reliant on graduatedNFTs[id] checks + * @dev TokenId is uncapped but raceIDs are aren't so tokenIdToRaceId ensures the metadata for the correct race is returned + * @param id of the token + */ + function tokenURI(uint id) public view override returns (string memory) { + require(_exists(id), "ERC721Metadata: URI query for nonexistent token"); + uint tokenRaceId = tokenIdToRaceId[id]; + uint tokenCollabId = tokenIdToCollabId[id]; + + + if(graduatedNFTs[id]){ + return string(abi.encodePacked(_baseURI(), "RaceNFT", tokenRaceId.toString(), ".json")); + } -// DataTypes.CreateProfileParams memory params; -// params.to = msg.sender; -// params.handle = handle; -// params.avatar = hashes[0]; -// params.metadata = hashes[1]; -// params.operator = address(this); - -// ProfileNFT.createProfile(params, '',''); -// ccID = ProfileNFT.getProfileIdByHandle(handle); -// uint id = _userCounter.current(); - -// IGhostsData.User memory user = IGhostsData.User( -// msg.sender, -// 0, // raceId -// 0, // completedTasks -// 0, // performance -// 0, // spotTheBugs -// 0, // contentPosts -// 0, // ctfStbContribs -// ccID, // CyberConnect Profile ID -// id // Ghosts User ID -// ); -// userMap[msg.sender] = user; -// idToUser[id] = user; -// userAwardFeats(1, 1, msg.sender, ccID); -// emit UserCreated(msg.sender, ccID); -// } - -// /** -// * @dev Ghosts profile required. Mints WarmUpNFT for current race in progress. -// * @notice User profiles are created with zero values by choice for frontend purposes whereas tokenId starts from 1. -// * @notice User.raceId is only incremented after submitting which means 1st warmUp raceId = 0 balance = 1, after submit raceId = 1 && balance = 1. -// */ -// function startNextRace() external { -// require(userMap[msg.sender].userAddress != address(0) , "No User Account"); -// IGhostsData.User memory user = userMap[msg.sender]; -// uint currentRace = user.raceId; -// uint nextId = (_tokenIdCounter.current() + 1); -// IGhostsData.WarmUpNFT memory warmUp = IGhostsData.WarmUpNFT({ -// userAddress: msg.sender, -// currentTaskId: currentRace, -// submittedAnswers: bytes32('0x'), -// tokenId: nextId -// }); -// warmUpNFTs[msg.sender] = warmUp; -// tokenIdToRaceId[nextId] = currentRace; - -// if(balanceOf(msg.sender) == 0){ -// safeMint(msg.sender); -// emit RaceStarted(msg.sender, currentRace, user.ccID); -// }else{ -// require(balanceOf(msg.sender) == user.raceId, "Finish your active race first."); -// safeMint(msg.sender); -// emit RaceStarted(msg.sender, currentRace, user.ccID); -// } -// } - -// /** -// * @dev WarmUpNFT required. Pops current warmUpNFT from mapping + pushes tokenId to graduatedNFTs. Metadata will return RaceNFT JSON. -// * @notice User.raceId is incremented after a task has been submitted successfully. -// * @param answers of user total user submissions. The hash of the hashes of individual answers. -// * @param perf of user current submissions -// */ -// function submitCompletedTask(bytes32 answers, uint perf) external { -// IGhostsData.User storage user = userMap[msg.sender]; -// require(user.userAddress != address(0) , "No User Account"); -// require(balanceOf(msg.sender) != 0 , "cannot submit a task without the warmUp NFT"); - -// IGhostsData.WarmUpNFT memory warmUp = warmUpNFTs[msg.sender]; - -// IGhostsData.RaceNFT memory raceNFT = finalRaceNfts[warmUp.currentTaskId]; - -// if(raceNFT.submittedAnswers == answers) revert AlreadySubmitted(warmUp.currentTaskId); -// warmUp.submittedAnswers = answers; - -// if(answers != raceNFT.answer) { -// revert IncorrectSubmission(); -// }else{ -// emit RaceCompleted(msg.sender, user.raceId, user.ccID); - -// delete warmUpNFTs[msg.sender]; -// graduatedNFTs[warmUp.tokenId] = true; -// user.raceId += 1; -// user.completedTasks++; - -// uint currentPerformance = user.performance; -// uint newPerformance = (currentPerformance + perf) / raceCount; -// user.performance = newPerformance; - -// IGhostsData.RaceNFT memory completedNFT = IGhostsData.RaceNFT({ -// submittedAnswers: answers, -// answer: answers, -// performance: perf, -// currentTaskId: user.raceId, -// tokenId: warmUp.tokenId, -// userAddress: msg.sender -// }); - -// raceNFTs[msg.sender] = completedNFT; -// delete completedNFT; -// delete raceNFT; -// delete warmUp; -// } -// } + if(graduatedCollabNFTs[id]){ + return string(abi.encodePacked(_baseURI(), "CollabNFT", tokenCollabId.toString(), ".json")); + } + + if(tokenCollabId != 0 && !graduatedCollabNFTs[id]){ + return string(abi.encodePacked(_baseURI(), "v0CollabNFT", tokenCollabId.toString(), ".json")); + }else{ + return string(abi.encodePacked(_baseURI(), "WarmUpNFT", tokenRaceId.toString(), ".json")); + } + } + + /** + * @dev creates a profile with Ghosts as well as minting a CC NFT to the msg.sender. + * @param handle of the user + * @param hashes of profiles hash[0]: avatar, hash[1]: metadata + */ + function createUser(string memory handle, string[] memory hashes) external returns (uint){ + uint ccID = userMap[msg.sender].ccID; + + if(ccID != 0){ + revert AccountExists(msg.sender, ccID); + } + + _userCounter.increment(); + + DataTypes.CreateProfileParams memory params; + params.to = msg.sender; + params.handle = handle; + params.avatar = hashes[0]; + params.metadata = hashes[1]; + params.operator = address(this); + + ProfileNFT.createProfile(params, '',''); + ccID = ProfileNFT.getProfileIdByHandle(handle); + uint id = _userCounter.current(); + + IGhostsData.User memory user = IGhostsData.User( + msg.sender, + 0, // raceId + 0, // collabId + 0, // completedTasks + 0, // performance + 0, // spotTheBugs + 0, // contentPosts + 0, // ctfStbContribs + ccID, // CyberConnect Profile ID + id // Ghosts User ID + ); + userMap[msg.sender] = user; + idToUser[id] = user; + userAwardFeats(1, 1, msg.sender, ccID); + emit UserCreated(msg.sender, ccID); + delete user; + return ccID; + } + + function submitCollab(bytes32[] memory guesses) external { + require(userMap[msg.sender].userAddress != address(0) , "No User Account"); + IGhostsData.User storage user = userMap[msg.sender]; + IGhostsData.CollabNFT storage collab = collabNFT[msg.sender]; + console2.log("collabID", collab.collabID); + console2.log("user.collabId", user.collabId); + require(collab.collabID > user.collabId, "Not Started Yet!"); + + uint len = guesses.length; + bytes32[] memory answers = new bytes32[](len); + for(uint x = 0; x < len; ++x){ + bytes32 guess = guesses[x]; + answers[x] = keccak256(abi.encodePacked(guess)); + } + + if(keccak256(abi.encodePacked(collab.answers)) == keccak256(abi.encodePacked(answers))){ + user.spotTheBugs += 1; + + console2.log("stbs", user.spotTheBugs); + + + uint currentPerformance = user.performance; + uint newPerformance = (currentPerformance + 100) / (raceCount + collabCount); + user.performance = newPerformance; + + graduatedCollabNFTs[collab.tokenID] = true; + collab.completed = true; + }else{ + revert IncorrectSubmission(); + } + } + + function startNextCollab() external { + require(userMap[msg.sender].userAddress != address(0) , "No User Account"); + IGhostsData.User memory user = userMap[msg.sender]; + uint currentCollab = user.collabId == 0 ? 1 : user.collabId; + IGhostsData.CollabNFT memory fCollab = collabNFTs[currentCollab]; + + console2.log("currentCollab", currentCollab); + console2.log("collabNFTs[currentCollab].collabID", collabNFTs[currentCollab].collabID); + + + if(user.collabId == fCollab.collabID){ + if(!fCollab.completed){ + revert FinishFirst(fCollab.collabID); + } + } + + uint nextId = (_tokenIdCounter.current() + 1); + + fCollab.tokenID = nextId; + fCollab.userAddress = msg.sender; + fCollab.currentId = currentCollab; + collabNFT[msg.sender] = fCollab; + user.collabId = fCollab.collabID; + + safeMint(msg.sender); + emit CollabStarted(msg.sender, fCollab.collabID); + } + + /** + * @dev Ghosts profile required. Mints WarmUpNFT for current race in progress. + * @notice User profiles are created with zero values by choice for frontend purposes whereas tokenId starts from 1. + * @notice User.raceId is only incremented after submitting which means 1st warmUp raceId = 0 balance = 1, after submit raceId = 1 && balance = 1. + */ + function startNextRace() external { + require(userMap[msg.sender].userAddress != address(0) , "No User Account"); + IGhostsData.User storage user = userMap[msg.sender]; + uint currentRace = user.raceId == 0 ? 1 : user.raceId; + + console2.log("raceID:", currentRace); + uint nextId = (_tokenIdCounter.current() + 1); + IGhostsData.WarmUpNFT memory warmUp = IGhostsData.WarmUpNFT({ + userAddress: msg.sender, + currentTaskId: currentRace, + submittedAnswers: bytes32('0x'), + tokenId: nextId + }); + warmUpNFT[msg.sender] = warmUp; + if(balanceOf(msg.sender) == 0){ + userMap[msg.sender].raceId += 1; + safeMint(msg.sender); + emit RaceStarted(msg.sender, 1, user.ccID); + }else{ + require(graduatedNFTs[user.raceId], "Finish your active race first."); + userMap[msg.sender].raceId +=1; + safeMint(msg.sender); + emit RaceStarted(msg.sender, userMap[msg.sender].raceId, user.ccID); + } + + } + + /** + * @dev WarmUpNFT required. Pops current warmUpNFT from mapping + pushes tokenId to graduatedNFTs. Metadata will return RaceNFT JSON. + * @notice User.raceId is incremented after a task has been submitted successfully. + * @param answers of user total user submissions. The hash of the hashes of individual answers. + * @param perf of user current submissions + */ + function submitCompletedTask(bytes32 answers, uint perf) external { + IGhostsData.User storage user = userMap[msg.sender]; + require(user.userAddress != address(0) , "No User Account"); + require(balanceOf(msg.sender) != 0 , "cannot submit a task without the warmUp NFT"); + + IGhostsData.WarmUpNFT memory warmUp = warmUpNFT[msg.sender]; + IGhostsData.RaceNFT memory raceNFT_ = finalRaceNfts[user.raceId]; + + if(raceNFT_.submittedAnswers == answers) revert AlreadySubmitted(user.raceId); + warmUp.submittedAnswers = answers; + + if(answers != raceNFT_.answer) { + revert IncorrectSubmission(); + }else{ + require(warmUp.userAddress == msg.sender, "already graduated warmup NFT"); + delete warmUpNFT[msg.sender]; + graduatedNFTs[warmUp.tokenId] = true; + user.completedTasks +=1; + + uint currentPerformance = user.performance; + uint newPerformance = (currentPerformance + perf) / (raceCount + collabCount); + user.performance = newPerformance; + + IGhostsData.RaceNFT memory completedNFT = IGhostsData.RaceNFT({ + submittedAnswers: answers, + answer: answers, + performance: perf, + currentTaskId: user.raceId, + tokenId: warmUp.tokenId, + userAddress: msg.sender + }); + emit RaceCompleted(msg.sender, user.raceId, user.ccID); + + raceNFT[msg.sender] = completedNFT; + delete completedNFT; + delete raceNFT_; + delete warmUp; + } + } -// ///////////////////////////////// -// /// /// -// /// Internal Functions /// -// /// /// -// ///////////////////////////////// - -// /** -// * @dev E Z P Z minting, doesn't care about warmUps or raceNFTs. Only called by this address. -// * @param to, the address of the user -// */ -// function safeMint(address to) internal { -// _tokenIdCounter.increment(); -// uint256 tokenId = _tokenIdCounter.current(); -// _safeMint(to, tokenId); -// } + ///////////////////////////////// + /// /// + /// Internal Functions /// + /// /// + ///////////////////////////////// + /** + * @dev E Z P Z minting, doesn't care about warmUps or raceNFT. Only called by this address. + * @param to, the address of the user + */ + function safeMint(address to) internal { + _tokenIdCounter.increment(); + uint nextId = _tokenIdCounter.current(); + tokenIdToRaceId[nextId] = userMap[msg.sender].raceId ; + _safeMint(to, nextId); + } -// function _baseURI() internal override view returns (string memory) { -// return tokenBaseURI; -// } -// function ccSubscribe( -// DataTypes.SubscribeParams calldata params, -// bytes[] calldata pre, -// bytes[] calldata post -// ) external onlyOwner { -// ccProfileNFT.subscribe(params, pre, post); -// } + function _baseURI() internal override view returns (string memory) { + return tokenBaseURI; + } + function ccSubscribe( + DataTypes.SubscribeParams calldata params, + bytes[] calldata pre, + bytes[] calldata post + ) external onlyRole(UPGRADER_ROLE) { + ccProfileNFT.subscribe(params, pre, post); + } -// ///////////////////////////////// -// /// /// -// /// Overrides /// -// /// /// -// ///////////////////////////////// + ///////////////////////////////// + /// /// + /// Overrides /// + /// /// + ///////////////////////////////// -// // The following functions are overrides required by Solidity. -// // soulbound -// function transferFrom(address,address,uint256) public pure override(ERC721, IERC721) { -// return; -// } -// function safeTransferFrom(address,address,uint256) public pure override(ERC721, IERC721) { -// return; -// } -// function _beforeTokenTransfer(address from, address to, uint256 tokenId, uint256 batchSize) -// internal -// override(ERC721, ERC721Enumerable) -// { -// super._beforeTokenTransfer(from, to, tokenId, batchSize); -// } - -// function supportsInterface(bytes4 interfaceId) -// public -// view -// override(ERC721, ERC721Enumerable) -// returns (bool) -// { -// return super.supportsInterface(interfaceId); -// } - -// function onERC721Received( -// address operator, -// address from, -// uint256 tokenId, -// bytes calldata data -// ) external returns (bytes4) { -// return IERC721Receiver.onERC721Received.selector; -// } -// } \ No newline at end of file + // The following functions are overrides required by Solidity. + function transferFrom(address,address,uint256) public pure override(ERC721Upgradeable, IERC721Upgradeable) { + revert Soulbound(); + } + function safeTransferFrom(address,address,uint256) public pure override(ERC721Upgradeable, IERC721Upgradeable) { + revert Soulbound(); + } + function _beforeTokenTransfer(address from, address to, uint256 tokenId, uint256 batchSize) + internal + override(ERC721Upgradeable, ERC721EnumerableUpgradeable) + { + super._beforeTokenTransfer(from, to, tokenId, batchSize); + } + + function supportsInterface(bytes4 interfaceId) + public + view + override(ERC721Upgradeable, ERC721EnumerableUpgradeable, AccessControlUpgradeable) + returns (bool) + { + return super.supportsInterface(interfaceId); + } + + function onERC721Received( + address operator, + address from, + uint256 tokenId, + bytes calldata data + ) external view returns (bytes4) { + return IERC721ReceiverUpgradeable.onERC721Received.selector; + } +} \ No newline at end of file diff --git a/src/GhostsFeats.sol b/src/GhostsFeats.sol index f7e478e..cedafa1 100644 --- a/src/GhostsFeats.sol +++ b/src/GhostsFeats.sol @@ -1,718 +1,808 @@ -// // SPDX-License-Identifier: MIT -// pragma solidity ^0.8.9; - -// import {DataTypes, IProfileNFT} from "./interfaces/IProfileNFT.sol"; -// import {IGhosts, IGhostsData} from "./interfaces/IGhosts.sol"; -// import {Ownable} from "@openzeppelin/access/Ownable.sol"; -// import {IERC721} from "@openzeppelin/token/ERC721/IERC721.sol"; -// import {ICyberNFTBase} from "./interfaces/ICyberNFTBase.sol"; - -// contract GhostsFeats { -// bytes32 internal constant FOLLOWUSER = keccak256("FollowUser"); -// bytes32 internal constant UNFOLLOWUSER = keccak256("UnfollowUser"); -// bytes32 internal constant CONSUMECONTENT = keccak256("ConsumeContent"); -// bytes32 internal constant CREATECONTENT = keccak256("CreateContent"); -// bytes32 internal constant COMMENTCONTENT = keccak256("CreateContent"); - -// address internal ghostsAddr; -// IProfileNFT internal constant ccProfileNFT = -// IProfileNFT(0x57e12b7a5F38A7F9c23eBD0400e6E53F2a45F271); -// uint public ghostsCCID; -// uint public postCount; +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.9; + +import {DataTypes, IProfileNFT} from "./interfaces/IProfileNFT.sol"; +import {IGhosts, IGhostsData} from "./interfaces/IGhosts.sol"; +import {ICyberNFTBase} from "./interfaces/ICyberNFTBase.sol"; + +import "@openzeppelin-upgradeable/proxy/utils/Initializable.sol"; +import "@openzeppelin-upgradeable/proxy/utils/UUPSUpgradeable.sol"; +import "@openzeppelin-upgradeable/token/ERC721/IERC721Upgradeable.sol"; +import "@openzeppelin-upgradeable/access/AccessControlUpgradeable.sol"; +import "forge-std/Test.sol"; + +contract GhostsFeats is Initializable, AccessControlUpgradeable, UUPSUpgradeable { + bytes32 internal constant FOLLOWUSER = keccak256("FollowUser"); + bytes32 internal constant UNFOLLOWUSER = keccak256("UnfollowUser"); + bytes32 internal constant CONSUMECONTENT = keccak256("ConsumeContent"); + bytes32 internal constant CREATECONTENT = keccak256("CreateContent"); + bytes32 internal constant COMMENTCONTENT = keccak256("CreateContent"); + bytes32 internal constant MINTER_ROLE = keccak256("MINTER_ROLE"); + bytes32 internal constant UPGRADER_ROLE = keccak256("UPGRADER_ROLE"); + + address internal ghostsAddr; + IProfileNFT internal constant ccProfileNFT = + IProfileNFT(0x57e12b7a5F38A7F9c23eBD0400e6E53F2a45F271); + uint public ghostsCCID; + uint public postCount; -// IGhostsData.Feat[] public featsMasterList; // list of feats -// mapping(uint256 => IGhostsData.Feat) public feats; // feat ID => feat data -// mapping(address => IGhostsData.UserFeats) public getUser; // stores the list of feats for each user -// mapping(address => IGhostsData.Feat[]) public earnedFeats; // feat ID => feat data - -// mapping(address => mapping(uint=>uint)) public addrToFeatToTiers; // user > featId > tier -// mapping(address => mapping(bytes32=>uint)) internal protocolActions; // allow protocol data to be stored in mapping -// mapping(uint=>uint) internal featsIdToEssID; // mapping featID => essID - -// mapping(uint=>bytes[]) public idToComment; // postId => commentID => comment -// mapping(uint=> uint[]) public userIdToFollowers; // userId => list of followers -// mapping(uint=> uint[]) public userIdToFollowed; // userId => list of followed -// mapping(uint=>uint) public postLikes; // postId => likes -// mapping(uint=>uint) public postDislikes; // postId => dislikes -// mapping(address=>miniPost[]) public userPosts; // user => postList -// mapping(uint=>miniPost) public posts; // postId => mini post - -// struct miniPost{ -// address creator; -// uint creatorCCID; -// uint postId; -// bool allowComments; -// string uri; -// bytes[] comments; -// } - -// event NewFollow(uint indexed follower, uint indexed followed); -// event Unfollowed(uint indexed follower, uint indexed followed); -// error NoComments(); - -// /** -// * @dev Creates the Essence NFT representing each achievement -// * @param name - essence name (the feat name?) -// * @param symbol - essence symbol -// * @param essenceURI - essence URI -// */ -// function createAchievements( -// string calldata name, -// string calldata symbol, -// string calldata essenceURI, -// string calldata description, -// uint16 weight, -// uint16 essTier -// ) internal { -// uint essID = ccRegEssence(ghostsCCID, name, symbol, essenceURI, address(0), true, true); - -// uint featId = featsMasterList.length; - -// if(featId == 0){ -// IGhostsData.Feat memory placeholder = IGhostsData.Feat({ -// name: bytes('placeholder'), -// desc: bytes('placeholder'), -// imageUrl: bytes('placeholder'), -// weight: 1, -// essId: 1, -// essTier: 0, -// earnedAt: 1 -// }); -// featsMasterList.push(placeholder); -// feats[featId] = placeholder; -// featsIdToEssID[featId] = essID; -// } - -// IGhostsData.Feat memory feat = IGhostsData.Feat({ -// name: bytes(name), -// desc: bytes(description), -// imageUrl: bytes(essenceURI), -// weight: weight, -// essId: essID, -// essTier: essTier, -// earnedAt: block.timestamp -// }); - -// feats[featId] = feat; -// featsMasterList.push(feat); -// featsIdToEssID[featId] = essID; -// } - -// function awardAchievements(address userAddr, uint profileId) public { -// _userFeatCheck(userAddr, profileId); -// } - -// function _userFeatCheck(address userAddr, uint profileId) internal { -// uint raceFeat = raceFeatCheck(userAddr); -// uint stbFeat = stbFeatCheck(userAddr); -// uint contribFeat = contribFeatCheck(userAddr); -// uint followingFeat = followingFeatCheck(userAddr); -// uint followerFeat = followerFeatCheck(userAddr); -// uint subscriberFeat = subscriberFeatCheck(userAddr); -// uint contentCreateFeat = contentFeatCheck(userAddr); -// uint contentConsumeFeat = gigaBrainCheck(userAddr); - -// uint[] memory tmp = new uint[](8); -// tmp[0] = raceFeat; -// tmp[1] = stbFeat; -// tmp[2] = contribFeat; -// tmp[3] = followerFeat; -// tmp[4] = followingFeat; -// tmp[5] = subscriberFeat; -// tmp[6] = contentCreateFeat; -// tmp[7] = contentConsumeFeat; - -// for(uint256 i = 0; i < 8; i++) { -// if(tmp[i] > 0) { -// userAwardFeats(i, tmp[i], userAddr, profileId); -// } -// } -// delete tmp; -// } - -// /** -// * @dev adds a feat to User.feats unless the feat is already owned by the user -// * @param featId the feat ID -// * @param tier Tier of feat the User has earned; ex "low = 1, medium = 2, high = 3, T4 = 4" -// * @param userAddr the user's address -// */ -// function userAwardFeats(uint featId, uint tier, address userAddr, uint profileId) internal { -// IGhostsData.Feat memory feat = feats[featId]; -// IGhostsData.UserFeats storage u = getUser[userAddr]; - -// if(u.userAddress == address(0)){ -// IGhostsData.UserFeats memory newUser = IGhostsData.UserFeats({ -// userAddress: userAddr, -// ccID: profileId, -// followCount: 0, -// followerCount: 0, -// commentCount: 0, -// consumeCount: 0, -// createCount: 0 -// }); -// getUser[userAddr] = newUser; -// delete newUser; -// return; -// }else{ + IGhostsData.Feat[] public featsMasterList; // list of feats + mapping(uint => IGhostsData.Feat) public feats; // feat ID => feat data + mapping(address => IGhostsData.UserFeats) public getUser; // stores the list of feats for each user + mapping(address => IGhostsData.Feat[]) public earnedFeats; // feat ID => feat data + + mapping(address => mapping(uint=>uint)) public addrToFeatToTiers; // user > featId > tier + mapping(address => mapping(bytes32=>uint)) internal protocolActions; // allow protocol data to be stored in mapping + mapping(uint=>uint) internal featsIdToEssID; // mapping featID => essID + + mapping(uint=>bytes[]) public idToComment; // postId => commentID => comment + mapping(address=> uint[]) public userIdToFollowers; // userId => list of followers + mapping(address=> uint[]) public userIdToFollowed; // userId => list of followed + mapping(uint=>uint) public postLikes; // postId => likes + mapping(uint=>uint) public postDislikes; // postId => dislikes + mapping(address=>IGhostsData.MiniPost[]) public userPosts; // user => postList + mapping(uint=>IGhostsData.MiniPost) public posts; // postId => mini post + + event NewFollow(uint indexed follower, uint indexed followed); + event Unfollowed(uint indexed follower, uint indexed followed); + error NoComments(); + error AlreadyFollowed(address userAddress); + + // constructor() { + // _disableInitializers(); + // } + + function __GhostsFeats_init(address who) initializer public { + __UUPSUpgradeable_init(); + __AccessControl_init(); + _grantRole(DEFAULT_ADMIN_ROLE, who); + _grantRole(UPGRADER_ROLE, who); + } + + function _authorizeUpgrade(address newImplementation) + internal + virtual + onlyRole(UPGRADER_ROLE) + override + {} + + + /** + * @dev Creates the Essence NFT representing each achievement + * @param name - essence name (the feat name?) + * @param symbol - essence symbol + * @param essenceURI - essence URI + */ + function createAchievements( + string calldata name, + string calldata symbol, + string calldata essenceURI, + string calldata description, + uint16 weight, + uint16 essTier, + bytes memory payload + ) internal { -// require(featId < featsMasterList.length, "featId out of range"); - - -// if(addrToFeatToTiers[userAddr][featId] >= tier) { -// return; -// } - -// uint256 profileId = ghostsCCID; // get users raceID - -// IGhostsData.Feat memory tmp = featsMasterList[featId]; -// tmp.earnedAt = block.timestamp; -// tmp.essTier = uint16(tier); - -// addrToFeatToTiers[userAddr][featId] = tier; - -// earnedFeats[userAddr].push(tmp); - -// ccCollectEss(userAddr, profileId, feat.essId); -// } -// } - -// /** -// * @dev Stores the action to the user addr then subscribes to the CC profile -// * @notice This is the protocol's follow user function -// * @param idToFollow the address of the user being followed -// * @param idFollowing the address of the user following another user -// */ -// function followUser( -// uint idToFollow, -// uint idFollowing, -// address userToFollow -// ) public returns (bool) { -// getUser[userToFollow].followerCount += 1; -// getUser[msg.sender].followCount += 1; - -// protocolActions[msg.sender][FOLLOWUSER] += 1; -// userIdToFollowers[idToFollow].push(idFollowing); - -// userIdToFollowed[idFollowing].push(idToFollow); - -// emit NewFollow(idFollowing, idToFollow); -// return true; -// } - -// function unfollowUser( -// uint idToUnfollow, -// uint idUnfollowing, -// address userToUnfollow -// ) public returns (bool) { -// getUser[userToUnfollow].followerCount -= 1; -// getUser[msg.sender].followCount -= 1; - -// protocolActions[msg.sender][UNFOLLOWUSER] += 1; - -// delete userIdToFollowed[idUnfollowing][idToUnfollow]; -// delete userIdToFollowers[idToUnfollow][idUnfollowing]; - -// emit Unfollowed(idUnfollowing, idToUnfollow); -// return true; -// } + {(bool succ,) = ghostsAddr.call(payload);} + uint featLen = featsMasterList.length; + uint featId = featLen == 0 ? 1 : featLen++; + + { IGhostsData.Feat memory feat = IGhostsData.Feat({ + name: bytes(name), + desc: bytes(description), + imageUrl: bytes(essenceURI), + weight: weight, + essId: featId, + essTier: essTier, + earnedAt: block.timestamp + }); + + feats[featId] = feat; + featsMasterList.push(feat); + featsIdToEssID[featId] = featId; + delete feat;} + } + + function awardAchievements(address userAddr, uint profileId) public { + _userFeatCheck(userAddr, profileId); + } + + function _userFeatCheck(address userAddr, uint profileId) internal { + uint raceFeat = raceFeatCheck(userAddr); + uint stbFeat = stbFeatCheck(userAddr); + uint contribFeat = contribFeatCheck(userAddr); + uint followingFeat = followingFeatCheck(userAddr); + uint followerFeat = followerFeatCheck(userAddr); + uint subscriberFeat = subscriberFeatCheck(userAddr); + uint contentCreateFeat = contentFeatCheck(userAddr); + uint contentConsumeFeat = gigaBrainCheck(userAddr); + + uint[] memory tmp = new uint[](8); + tmp[0] = raceFeat; + tmp[1] = stbFeat; + tmp[2] = contribFeat; + tmp[3] = followerFeat; + tmp[4] = followingFeat; + tmp[5] = subscriberFeat; + tmp[6] = contentCreateFeat; + tmp[7] = contentConsumeFeat; + + for(uint256 i = 0; i < 8; i++) { + if(tmp[i] > 0) { + userAwardFeats(i, tmp[i], userAddr, profileId); + } + } + delete tmp; + } + + /** + * @dev adds a feat to User.feats unless the feat is already owned by the user + * @param featId the feat ID + * @param tier Tier of feat the User has earned; ex "low = 1, medium = 2, high = 3, T4 = 4" + * @param userAddr the user's address + */ + function userAwardFeats(uint featId, uint tier, address userAddr, uint profileId) internal { + IGhostsData.Feat memory feat = feats[featId]; + IGhostsData.UserFeats storage u = getUser[userAddr]; + uint[] memory empty; + + if(u.userAddress == address(0)){ + IGhostsData.UserFeats memory newUser = IGhostsData.UserFeats({ + userAddress: userAddr, + ccID: profileId, + followCount: 0, + followerCount: 0, + commentCount: 0, + consumeCount: 0, + createCount: 0, + followers: empty, + following: empty + }); + getUser[userAddr] = newUser; + delete newUser; + return; + }else{ + require(featId < featsMasterList.length, "featId out of range"); + + if(addrToFeatToTiers[userAddr][featId] >= tier) { + return; + } + + uint256 profileId = ghostsCCID; // get users raceID + + IGhostsData.Feat memory tmp = featsMasterList[featId]; + tmp.earnedAt = block.timestamp; + tmp.essTier = uint16(tier); + + addrToFeatToTiers[userAddr][featId] = tier; + + earnedFeats[userAddr].push(tmp); + + ccCollectEss(userAddr, profileId, feat.essId); + } + } + + /** + * @dev Stores the action to the user addr then subscribes to the CC profile + * @notice This is the protocol's follow user function + * @param idToFollow the address of the user being followed + * @param idFollowing the address of the user following another user + */ + function followUser( + uint idToFollow, + uint idFollowing, + address userToFollow + ) public returns (bool) { + IGhostsData.UserFeats storage caller = getUser[msg.sender]; + IGhostsData.UserFeats storage user = getUser[userToFollow]; + uint[] memory followersArr = user.followers; + uint len = followersArr.length; + if (msg.sender == userToFollow) { + return false; + } + + for (uint i = 0; i < len; ++i) { + require(followersArr[i] != idFollowing, "You already follow this user"); + } + + caller.following.push(idToFollow); + user.followers.push(idFollowing); + caller.followCount ++; + user.followerCount ++; + protocolActions[msg.sender][FOLLOWUSER] ++; + emit NewFollow(idFollowing, idToFollow); + delete followersArr; + return true; + } + + function unfollowUser( + uint idToUnfollow, + uint idUnfollowing, + address userToUnfollow + ) public returns (bool) { + getUser[userToUnfollow].followerCount -= 1; + getUser[msg.sender].followCount -= 1; + + protocolActions[msg.sender][UNFOLLOWUSER] += 1; + + delete userIdToFollowed[msg.sender][idToUnfollow]; + delete userIdToFollowers[userToUnfollow][idUnfollowing]; + + emit Unfollowed(idUnfollowing, idToUnfollow); + return true; + } -// function consumeContent(uint256 profileId, uint256 essID) public { -// protocolActions[msg.sender][CONSUMECONTENT] += 1; -// getUser[msg.sender].consumeCount += 1; -// ccCollectEss(msg.sender, profileId, essID); -// } - -// /** -// * @dev Create Content for protocol uses this function to perform the following: -// * 1. update protocolAction[] stores the amount of calls to this function -// * 2. update User.createCount, increment by 1 -// * 3. call function ccRegEssence() to register the essence with the profile -// */ -// function createContent( -// uint256 profileId, -// string calldata name, -// string calldata symbol, -// string calldata essenceURI, -// address essenceMw, -// bool transferable, -// bool deployAtReg, -// bool allowComments -// ) public { -// ++postCount; -// protocolActions[msg.sender][CREATECONTENT] += 1; -// getUser[msg.sender].createCount += 1; -// uint id = ccRegEssence(profileId, name, symbol, essenceURI, essenceMw, transferable, deployAtReg); -// bytes[] memory emptyComments; -// miniPost memory newPost = miniPost(msg.sender, profileId,id,allowComments, essenceURI,emptyComments); -// userPosts[msg.sender].push(newPost); -// posts[postCount] = newPost; -// delete newPost; -// } - -// function commentContent( -// bytes calldata comment, -// uint postId -// ) public { -// miniPost storage post = posts[postId]; -// if(post.allowComments){ - -// protocolActions[msg.sender][COMMENTCONTENT] += 1; -// getUser[msg.sender].commentCount += 1; -// idToComment[postId].push(comment); -// post.comments.push(comment); -// }else{revert NoComments();} -// } - -// function deleteComment( -// uint postId, -// address userAddr -// ) public { -// if (userAddr == msg.sender) { -// delete idToComment[postId]; -// getUser[msg.sender].commentCount -= 1; -// } -// } - -// /** -// * @dev check User.raceID and returns feat level based on completion -// */ -// function raceFeatCheck(address userAddr) internal returns (uint256) { -// IGhostsData.User memory user = IGhosts(ghostsAddr).getGhostsProfile(userAddr); -// uint256 currentRaceID = user.raceId; - -// if (currentRaceID >= 2) { -// if (currentRaceID < 8) { -// delete user; -// return 1; -// } -// } else if (currentRaceID >= 8) { -// if (currentRaceID < 15) { -// delete user; -// return 2; -// } -// } else if (currentRaceID >= 15) { -// if (currentRaceID < 22) { -// delete user; -// return 3; -// } -// } else if (currentRaceID >= 22) { -// delete user; -// return 4; -// } else { -// delete user; -// return 0; -// } -// } - -// /** -// * @dev check User.spotTheBugs and returns feat level based on completion -// */ -// function stbFeatCheck(address userAddr) internal returns (uint256) { -// uint256 spotTheBugs = IGhosts(ghostsAddr).getGhostsProfile(userAddr).spotTheBugs; // get users spotTheBugs count -// if (spotTheBugs >= 5) { -// if (spotTheBugs < 20) { -// return 1; -// } -// } else if (spotTheBugs >= 20) { -// if (spotTheBugs < 50) { -// return 2; -// } -// } else if (spotTheBugs >= 50) { -// if (spotTheBugs < 100) { -// return 3; -// } -// } else if (spotTheBugs >= 100) { -// return 4; -// } else { -// return 0; -// } -// return 0; -// } - -// /** -// * @dev check User.ctfStbContribs and returns feat level based on completion -// */ -// function contribFeatCheck(address userAddr) -// internal -// returns (uint256) -// { -// uint256 contribCount = IGhosts(ghostsAddr).getGhostsProfile(userAddr).ctfStbContribs; // get users contribCount -// if (contribCount >= 1) { -// if (contribCount < 5) { -// return 1; -// } -// } else if (contribCount >= 5) { -// if (contribCount < 15) { -// return 2; -// } -// } else if (contribCount >= 15) { -// if (contribCount < 30) { -// return 3; -// } -// } else if (contribCount >= 30) { -// return 4; -// } else { -// return 0; -// } -// return 0; -// } - -// /** -// * @dev checks protocolActions[userAddr][activityType] and returns feat level based on count -// */ -// function followingFeatCheck(address userAddr) internal view returns (uint256) { -// uint256 activityCount = protocolActions[userAddr][FOLLOWUSER]; // get users social activity count -// if (activityCount >= 1) { -// if (activityCount < 5) { -// return 1; -// } -// } else if (activityCount >= 5) { -// if (activityCount < 15) { -// return 2; -// } -// } else if (activityCount >= 15) { -// if (activityCount < 30) { -// return 3; -// } -// } else if (activityCount >= 30) { -// return 4; -// } else { -// return 0; -// } -// return 0; -// } - -// function followerFeatCheck(address userAddr) internal view returns (uint256) { - -// uint256 activityCount = getUser[userAddr].followerCount; // get users social activity count - -// if (activityCount >= 1) { -// if (activityCount < 5) { -// return 1; -// } -// } else if (activityCount >= 5) { -// if (activityCount < 15) { -// return 2; -// } -// } else if (activityCount >= 15) { -// if (activityCount < 30) { -// return 3; -// } -// } else if(activityCount >= 30) { -// return 4; -// }else{ -// return 0; -// } -// } - -// /** -// * @dev checks getUser[userAddr].followCount and returns feat level based on count -// */ -// function subscriberFeatCheck(address userAddr) internal returns (uint256) { + function consumeContent(uint256 profileId, uint256 essID) public { + protocolActions[msg.sender][CONSUMECONTENT] += 1; + getUser[msg.sender].consumeCount += 1; + ccCollectEss(msg.sender, profileId, essID); + } + + /** + * @dev Create Content for protocol uses this function to perform the following: + * 1. update protocolAction[] stores the amount of calls to this function + * 2. update User.createCount, increment by 1 + * 3. call function ccRegEssence() to register the essence with the profile + */ + function createContent( + uint256 profileId, + string calldata name, + string calldata symbol, + string calldata essenceURI, + address essenceMw, + bool transferable, + bool deployAtReg, + bool allowComments + ) public returns (uint) { + IGhostsData.UserFeats storage caller = getUser[msg.sender]; + bytes[] memory emptyComments; + + if(postCount == 0) { + IGhostsData.MiniPost memory newPost; + newPost.creator = msg.sender; + newPost.creatorCCID = profileId; + newPost.postId = postCount; + newPost.likes = 0; + newPost.dislikes = 0; + newPost.allowComments = allowComments; + newPost.uri = essenceURI; + newPost.comments = emptyComments; + + userPosts[msg.sender].push(newPost); + posts[postCount] = newPost; + postCount++; + delete newPost; + } + uint actions = protocolActions[msg.sender][CREATECONTENT] += 1; + {bytes memory payload = abi.encodeWithSignature( + "ccRegEssence(uint256, string, string, string, bytes32)", + ghostsCCID, name, symbol, essenceURI, address(0)); + (bool success,) = ghostsAddr.call(payload);} + + IGhostsData.MiniPost memory newPost; + newPost.creator = msg.sender; + newPost.creatorCCID = profileId; + newPost.postId = postCount; + newPost.likes = 0; + newPost.dislikes = 0; + newPost.allowComments = allowComments; + newPost.uri = essenceURI; + newPost.comments = emptyComments; + + userPosts[msg.sender].push(newPost); + posts[postCount] = newPost; + delete newPost; + + + caller.createCount = actions; + ++postCount; + return caller.createCount; + } + + function commentContent( + bytes calldata comment, + uint postId + ) public { + IGhostsData.MiniPost storage post = posts[postId]; + if(post.comments.length == 0){ + idToComment[postId].push(bytes(".")); + getUser[ghostsAddr].commentCount = 1; + post.comments.push(bytes(".")); + } + + if(post.allowComments){ + uint count = getUser[msg.sender].commentCount; + if(count == 0) { + protocolActions[msg.sender][COMMENTCONTENT] = 1; + getUser[msg.sender].commentCount = 1; + idToComment[postId].push(comment); + post.comments.push(comment); + }else{ + protocolActions[msg.sender][COMMENTCONTENT] += 1; + getUser[msg.sender].commentCount += 1; + idToComment[postId].push(comment); + post.comments.push(comment); + } + }else{revert NoComments();} + } + function likePost(uint postId) external { + IGhostsData.MiniPost storage post = posts[postId]; + post.likes = post.likes + 1; + postLikes[postId] = postLikes[postId] + 1; + } + + function dislikePost(uint postId) external { + IGhostsData.MiniPost storage post = posts[postId]; + post.dislikes = post.dislikes + 1; + postDislikes[postId] = postDislikes[postId] + 1; + } + + function getPost(uint postId) external view returns (IGhostsData.MiniPost memory){ + return posts[postId]; + } + + function getAllUserPosts(address userAddr) external view returns (IGhostsData.MiniPost[] memory){ + return userPosts[userAddr]; + } + + function getPostLikeCount(uint postId) external view returns (uint256) { + return postLikes[postId]; + } + + function getPostDislikeCount(uint postId) external view returns (uint256) { + return postDislikes[postId]; + } + + function getPostComments(uint postId) external view returns (bytes[] memory) { + return idToComment[postId]; + } + /** + * @dev check User.raceID and returns feat level based on completion + */ + function raceFeatCheck(address userAddr) internal returns (uint8 tier) { + IGhostsData.User memory user = IGhosts(ghostsAddr).getGhostsProfile(userAddr); + uint256 currentRaceID = user.raceId; + + if (currentRaceID >= 2) { + if (currentRaceID < 8) { + delete user; + tier = 1; + } + } else if (currentRaceID >= 8) { + if (currentRaceID < 15) { + delete user; + tier = 2; + } + } else if (currentRaceID >= 15) { + if (currentRaceID < 22) { + delete user; + tier = 3; + } + } else if (currentRaceID >= 22) { + delete user; + tier = 4; + } else { + delete user; + tier = 0; + } + } + + /** + * @dev check User.spotTheBugs and returns feat level based on completion + */ + function stbFeatCheck(address userAddr) internal returns (uint8 tier) { + uint256 spotTheBugs = IGhosts(ghostsAddr).getGhostsProfile(userAddr).spotTheBugs; // get users spotTheBugs count + if (spotTheBugs >= 5) { + if (spotTheBugs < 20) { + tier = 1; + } + } else if (spotTheBugs >= 20) { + if (spotTheBugs < 50) { + tier = 2; + } + } else if (spotTheBugs >= 50) { + if (spotTheBugs < 100) { + tier = 3; + } + } else if (spotTheBugs >= 100) { + tier = 4; + } else { + tier = 0; + } + } + + /** + * @dev check User.ctfStbContribs and returns feat level based on completion + */ + function contribFeatCheck(address userAddr) + internal + returns (uint8 tier) + { + uint256 contribCount = IGhosts(ghostsAddr).getGhostsProfile(userAddr).ctfStbContribs; // get users contribCount + if (contribCount >= 1) { + if (contribCount < 5) { + tier = 1; + } + } else if (contribCount >= 5) { + if (contribCount < 15) { + tier = 2; + } + } else if (contribCount >= 15) { + if (contribCount < 30) { + tier = 3; + } + } else if (contribCount >= 30) { + tier = 4; + } else { + tier = 0; + } + } + + /** + * @dev checks protocolActions[userAddr][activityType] and returns feat level based on count + */ + function followingFeatCheck(address userAddr) internal view returns (uint8 tier) { + uint256 activityCount = protocolActions[userAddr][FOLLOWUSER]; // get users social activity count + if (activityCount >= 1) { + if (activityCount < 5) { + tier = 1; + } + } else if (activityCount >= 5) { + if (activityCount < 15) { + tier = 2; + } + } else if (activityCount >= 15) { + if (activityCount < 30) { + tier = 3; + } + } else if (activityCount >= 30) { + tier = 4; + } else { + tier = 0; + } + } + + function followerFeatCheck(address userAddr) internal view returns (uint8 tier) { + + uint256 activityCount = getUser[userAddr].followerCount; // get users social activity count + + if (activityCount >= 1) { + if (activityCount < 5) { + tier = 1; + } + } else if (activityCount >= 5) { + if (activityCount < 15) { + tier = 2; + } + } else if (activityCount >= 15) { + if (activityCount < 30) { + tier = 3; + } + } else if(activityCount >= 30) { + tier = 4; + }else{ + tier = 0; + } + } + + /** + * @dev checks getUser[userAddr].followCount and returns feat level based on count + */ + function subscriberFeatCheck(address userAddr) internal returns (uint8 tier) { + + uint profileId = IGhosts(ghostsAddr).getGhostsProfile(userAddr).ccID; + address subNFT = ccProfileNFT.getSubscribeNFT(profileId); + + if(subNFT != address(0)) { + + uint subCount = ICyberNFTBase(subNFT).totalMinted(); + + if (subCount >= 1) { + if (subCount < 5) { + tier = 1; + } + } else if (subCount >= 5) { + if (subCount < 15) { + tier = 2; + } + } else if (subCount >= 15) { + if (subCount < 30) { + tier = 3; + } + } else if (subCount >= 30) { + tier = 4; + } else { + tier = 0; + } + }else{ + tier = 0; + } + } + + /** + * @dev check User.contentPosts and returns feat level based on completion + */ + function contentFeatCheck(address userAddr) internal view returns (uint8 tier) { + uint256 contentCount = protocolActions[userAddr][CREATECONTENT]; // get users post count + if (contentCount >= 1) { + if (contentCount < 5) { + tier = 1; + } + } else if (contentCount >= 5) { + if (contentCount < 15) { + tier = 2; + } + } else if (contentCount >= 15) { + if (contentCount < 30) { + tier = 3; + } + } else if (contentCount >= 30) { + tier = 4; + } else { + tier = 0; + } + } + + /** + * @dev checks getUser[userAddr].consumeContent and returns feat level based on count + + */ + function gigaBrainCheck(address userAddr) internal view returns (uint8 tier) { + uint256 consumeCount = protocolActions[userAddr][CONSUMECONTENT]; // get users post count + if (consumeCount >= 1) { + if (consumeCount < 5) { + tier = 1; + } + } else if (consumeCount >= 5) { + if (consumeCount < 15) { + tier = 2; + } + } else if (consumeCount >= 15) { + if (consumeCount < 30) { + tier = 3; + } + } else if (consumeCount >= 30) { + tier = 4; + } else { + tier = 0; + } + } + + function tier4Check(address userAddr) external returns(uint8[4] memory tier) { -// uint profileId = IGhosts(ghostsAddr).getGhostsProfile(userAddr).ccID; -// address subNFT = ccProfileNFT.getSubscribeNFT(profileId); - -// if(subNFT != address(0)) { - -// uint subCount = ICyberNFTBase(subNFT).totalMinted(); - -// if (subCount >= 1) { -// if (subCount < 5) { -// return 1; -// } -// } else if (subCount >= 5) { -// if (subCount < 15) { -// return 2; -// } -// } else if (subCount >= 15) { -// if (subCount < 30) { -// return 3; -// } -// } else if (subCount >= 30) { -// return 4; -// } else { -// return 0; -// } -// return 0; -// }else{ -// return 0;} -// } - -// /** -// * @dev check User.contentPosts and returns feat level based on completion -// */ -// function contentFeatCheck(address userAddr) internal view returns (uint256) { -// uint256 contentCount = protocolActions[userAddr][CREATECONTENT]; // get users post count -// if (contentCount >= 1) { -// if (contentCount < 5) { -// return 1; -// } -// } else if (contentCount >= 5) { -// if (contentCount < 15) { -// return 2; -// } -// } else if (contentCount >= 15) { -// if (contentCount < 30) { -// return 3; -// } -// } else if (contentCount >= 30) { -// return 4; -// } else { -// return 0; -// } -// return 0; -// } - -// /** -// * @dev checks getUser[userAddr].consumeContent and returns feat level based on count - -// */ -// function gigaBrainCheck(address userAddr) internal view returns (uint256) { -// uint256 consumeCount = protocolActions[userAddr][CONSUMECONTENT]; // get users post count -// if (consumeCount >= 1) { -// if (consumeCount < 5) { -// return 1; -// } -// } else if (consumeCount >= 5) { -// if (consumeCount < 15) { -// return 2; -// } -// } else if (consumeCount >= 15) { -// if (consumeCount < 30) { -// return 3; -// } -// } else if (consumeCount >= 30) { -// return 4; -// } else { -// return 0; -// } -// return 0; -// } - - -// ///////////////////////////////// -// /// /// -// /// Internal Functions /// -// /// /// -// ///////////////////////////////// -// function ccGetMetadata(uint256 profileId) -// public -// view -// returns (string memory) -// { -// return ccProfileNFT.getMetadata(profileId); -// } - -// function ccGetAvatar(uint256 profileId) -// public -// view -// returns (string memory) -// { -// return ccProfileNFT.getAvatar(profileId); -// } - -// function ccGetSubNFTAddr(uint256 profileId) -// public -// view -// returns (address) -// { -// return ccProfileNFT.getSubscribeNFT(profileId); -// } - -// function ccGetSubURI(uint256 profileId) -// public -// view -// returns (string memory) -// { -// return ccProfileNFT.getSubscribeNFTTokenURI(profileId); -// } - -// function ccGetEssNFTAddr(uint256 profileId, uint256 essId) -// public -// view -// returns (address) -// { -// return ccProfileNFT.getEssenceNFT(profileId, essId); -// } - -// function ccGetEssURI(uint256 profileId, uint256 essId) -// public -// view -// returns (string memory) -// { -// return ccProfileNFT.getEssenceNFTTokenURI(profileId, essId); -// } - -// /** -// * @dev sets the namespace owner of the ccProfileNFT to the provided address. -// * @param addr of new namespace owner -// */ -// function ccSetNSOwner(address addr) public { -// ccProfileNFT.setNamespaceOwner(addr); -// } - -// function ccRegEssence( -// uint256 profileId, -// string calldata name, -// string calldata symbol, -// string calldata essenceURI, -// address essenceMw, -// bool transferable, -// bool deployAtReg -// ) public returns (uint256) { -// DataTypes.RegisterEssenceParams memory params; - -// params.profileId = profileId; -// params.name = name; -// params.symbol = symbol; -// params.essenceTokenURI = essenceURI; -// params.essenceMw = essenceMw; -// params.transferable = transferable; -// params.deployAtRegister = deployAtReg; - -// return _ccRegEssence(params); -// } - -// function ccCollectEss( -// address who, -// uint256 profileId, -// uint256 essenceId -// ) public { -// DataTypes.CollectParams memory params; -// params.collector = who; -// params.profileId = profileId; -// params.essenceId = essenceId; - -// _ccCollectEss(params); -// } - -// function ccSetMetadata(uint256 profileId, string calldata metadata) -// public -// { -// _ccSetMetadata(profileId, metadata); -// } - -// function ccSetSubData( -// uint256 profileId, -// string calldata uri, -// address mw, -// bytes calldata mwData -// ) public { -// _ccSetSubData(profileId, uri, mw, mwData); -// } - -// function ccSetEssData( -// uint256 profileId, -// uint256 essId, -// string calldata uri, -// address mw, -// bytes calldata mwData -// ) public { -// _ccSetEssData(profileId, essId, uri, mw, mwData); -// } - -// function ccSetPrimary(uint256 profileId) public { -// _ccSetPrimary(profileId); -// } - -// function _ccRegEssence(DataTypes.RegisterEssenceParams memory params) -// internal -// returns (uint256) -// { -// uint id = ccProfileNFT.registerEssence(params, ''); -// return id; -// } - -// function _ccCollectEss(DataTypes.CollectParams memory params) internal { -// ccProfileNFT.collect(params, "", ""); -// } - -// function _ccSetMetadata(uint256 profileId, string calldata metadata) -// internal -// { -// ccProfileNFT.setMetadata(profileId, metadata); -// } - -// function _ccSetSubData( -// uint256 profileId, -// string calldata uri, -// address mw, -// bytes calldata mwData -// ) internal { -// ccProfileNFT.setSubscribeData(profileId, uri, mw, mwData); -// } - -// function _ccSetEssData( -// uint256 profileId, -// uint256 essId, -// string calldata uri, -// address mw, -// bytes calldata mwData -// ) internal { -// ccProfileNFT.setEssenceData(profileId, essId, uri, mw, mwData); -// } - -// function _ccSetPrimary(uint256 profileId) internal { -// ccProfileNFT.setPrimaryProfile(profileId); -// } - -// /** -// * @notice Check if the profile issued EssenceNFT is collected by me. -// * -// * @param profileId The profile id. -// * @param essenceId The essence id. -// * @param me The address to check. -// * @param _namespace The address of the ccProfileNFT -// */ -// function isCollectedByMe( -// uint256 profileId, -// uint256 essenceId, -// address me, -// address _namespace -// ) external view returns (bool) { -// address essNFTAddr = IProfileNFT(_namespace).getEssenceNFT( -// profileId, -// essenceId -// ); -// if (essNFTAddr == address(0)) { -// return false; -// } - -// return IERC721(essNFTAddr).balanceOf(me) > 0; -// } - -// /** -// * @notice Check if the profile is subscribed by me. -// * -// * @param profileId The profile id. -// * @param me The address to check. -// * @param _namespace The address of the ProfileNFT -// */ -// function isSubscribedByMe(uint256 profileId, address me, address _namespace) -// external -// view -// returns (bool) -// { -// address subNFTAddr = IProfileNFT(_namespace).getSubscribeNFT(profileId); -// if (subNFTAddr == address(0)) { -// return false; -// } -// return IERC721(subNFTAddr).balanceOf(me) > 0; -// } - -// function createGhostsCC(string memory handle, string[] memory hashes, address op) internal returns(uint) { -// DataTypes.CreateProfileParams memory params; -// params.to = msg.sender; -// params.handle = handle; -// params.avatar = hashes[0]; -// params.metadata = hashes[1]; -// params.operator = op; -// ccProfileNFT.createProfile(params, '',''); -// uint ccID = ccProfileNFT.getProfileIdByHandle(handle); -// ghostsCCID = ccID; -// return ccID; -// } - -// } + } + + + ///////////////////////////////// + /// /// + /// Internal Functions /// + /// /// + ///////////////////////////////// + function ccGetMetadata(uint256 profileId) + public + view + returns (string memory) + { + return ccProfileNFT.getMetadata(profileId); + } + + function ccGetAvatar(uint256 profileId) + public + view + returns (string memory) + { + return ccProfileNFT.getAvatar(profileId); + } + + function ccGetSubNFTAddr(uint256 profileId) + public + view + returns (address) + { + return ccProfileNFT.getSubscribeNFT(profileId); + } + + function ccGetSubURI(uint256 profileId) + public + view + returns (string memory) + { + return ccProfileNFT.getSubscribeNFTTokenURI(profileId); + } + + function ccGetEssNFTAddr(uint256 profileId, uint256 essId) + public + view + returns (address) + { + return ccProfileNFT.getEssenceNFT(profileId, essId); + } + + function ccGetEssURI(uint256 profileId, uint256 essId) + public + view + returns (string memory) + { + return ccProfileNFT.getEssenceNFTTokenURI(profileId, essId); + } + + + function ccRegEssence( + uint256 profileId, + string calldata name, + string calldata symbol, + string calldata essenceURI, + address essenceMw + ) internal returns (uint256) { + DataTypes.RegisterEssenceParams memory params; + + params.profileId = profileId; + params.name = name; + params.symbol = symbol; + params.essenceTokenURI = essenceURI; + params.essenceMw = essenceMw; + params.transferable = true; + params.deployAtRegister = true; + + return _ccRegEssence(params); + } + + function ccCollectEss( + address who, + uint256 profileId, + uint256 essenceId + ) internal { + DataTypes.CollectParams memory params; + params.collector = who; + params.profileId = profileId; + params.essenceId = essenceId; + + _ccCollectEss(params); + } + + function ccSetMetadata(uint256 profileId, string calldata metadata) + public + { + _ccSetMetadata(profileId, metadata); + } + + function ccSetSubData( + uint256 profileId, + string calldata uri, + address mw, + bytes calldata mwData + ) public { + _ccSetSubData(profileId, uri, mw, mwData); + } + + function ccSetEssData( + uint256 profileId, + uint256 essId, + string calldata uri, + address mw, + bytes calldata mwData + ) public { + _ccSetEssData(profileId, essId, uri, mw, mwData); + } + + function ccSetPrimary(uint256 profileId) public { + _ccSetPrimary(profileId); + } + + function _ccRegEssence(DataTypes.RegisterEssenceParams memory params) + internal + returns (uint256) + { + uint id = ccProfileNFT.registerEssence(params, ''); + return id; + } + + function _ccCollectEss(DataTypes.CollectParams memory params) internal { + ccProfileNFT.collect(params, "", ""); + } + + function _ccSetMetadata(uint256 profileId, string calldata metadata) + internal + { + ccProfileNFT.setMetadata(profileId, metadata); + } + + function _ccSetSubData( + uint256 profileId, + string calldata uri, + address mw, + bytes calldata mwData + ) internal { + ccProfileNFT.setSubscribeData(profileId, uri, mw, mwData); + } + + function _ccSetEssData( + uint256 profileId, + uint256 essId, + string calldata uri, + address mw, + bytes calldata mwData + ) internal { + ccProfileNFT.setEssenceData(profileId, essId, uri, mw, mwData); + } + + function _ccSetPrimary(uint256 profileId) internal { + ccProfileNFT.setPrimaryProfile(profileId); + } + + /** + * @notice Check if the profile issued EssenceNFT is collected by me. + * + * @param profileId The profile id. + * @param essenceId The essence id. + * @param me The address to check. + * @param _namespace The address of the ccProfileNFT + */ + function isCollectedByMe( + uint256 profileId, + uint256 essenceId, + address me, + address _namespace + ) external view returns (bool) { + address essNFTAddr = IProfileNFT(_namespace).getEssenceNFT( + profileId, + essenceId + ); + if (essNFTAddr == address(0)) { + return false; + } + + return IERC721Upgradeable(essNFTAddr).balanceOf(me) > 0; + } + + /** + * @notice Check if the profile is subscribed by me. + * + * @param profileId The profile id. + * @param me The address to check. + * @param _namespace The address of the ProfileNFT + */ + function isSubscribedByMe(uint256 profileId, address me, address _namespace) + external + view + returns (bool) + { + address subNFTAddr = IProfileNFT(_namespace).getSubscribeNFT(profileId); + if (subNFTAddr == address(0)) { + return false; + } + return IERC721Upgradeable(subNFTAddr).balanceOf(me) > 0; + } + + function createGhostsCC(string memory handle, string[] memory hashes, address op) internal returns(uint) { + DataTypes.CreateProfileParams memory params; + params.to = msg.sender; + params.handle = handle; + params.avatar = hashes[0]; + params.metadata = hashes[1]; + params.operator = op; + ccProfileNFT.createProfile(params, '',''); + uint ccID = ccProfileNFT.getProfileIdByHandle(handle); + IGhostsData.UserFeats memory u = getUser[ghostsAddr]; + uint[] memory empty; + if(u.userAddress == address(0)){ + IGhostsData.UserFeats memory newUser = IGhostsData.UserFeats({ + userAddress: ghostsAddr, + ccID: ccID, + followCount: 0, + followerCount: 0, + commentCount: 0, + consumeCount: 0, + createCount: 0, + followers: empty, + following: empty + }); + getUser[ghostsAddr] = newUser; + ghostsCCID = ccID; + delete newUser; + delete u; + return 0; + } + } +} \ No newline at end of file diff --git a/src/interfaces/IGhosts.sol b/src/interfaces/IGhosts.sol index 57aa91f..e5977c4 100644 --- a/src/interfaces/IGhosts.sol +++ b/src/interfaces/IGhosts.sol @@ -5,6 +5,7 @@ interface IGhostsData { struct User { address userAddress; // user address uint raceId; // the race they are currently on + uint collabId; // the collab they are currently on uint completedTasks; // completed tasks uint performance; // a percentage based on previous task performance uint spotTheBugs; // completed spot the bugs tasks @@ -31,6 +32,17 @@ interface IGhostsData { address userAddress; } + struct CollabNFT { + bytes author; + bytes title; + bytes32[] answers; + uint collabID; + uint tokenID; + uint currentId; + bool completed; + address userAddress; + } + struct Feat { bytes name; // feat name bytes desc; // description of feat diff --git a/test/Ghosts.t.sol b/test/Ghosts.t.sol index e758d30..55a55bb 100644 --- a/test/Ghosts.t.sol +++ b/test/Ghosts.t.sol @@ -2,10 +2,18 @@ pragma solidity ^0.8.13; import "forge-std/Test.sol"; -import "./mocks/Ghosts.sol"; +import "../src/Ghosts.sol"; +import {Strings} from "@openzeppelin/utils/Strings.sol"; +import "@openzeppelin/proxy/ERC1967/ERC1967Proxy.sol"; +import "@openzeppelin-upgradeable/token/ERC721/IERC721Upgradeable.sol"; + contract GhostsTest is Test { + Ghosts implV1; + Ghosts wrappedProxyV1; Ghosts public ghosts; + using Strings for uint; + ERC1967Proxy internal proxy; address public user1; address public user2; @@ -13,6 +21,20 @@ contract GhostsTest is Test { address public immutable user4 = 0x45484441b8f59a0245a71aa5437C994f982056C0; function setUp() public { + bytes32[] memory answers = new bytes32[](5); + answers[0] = 0x0000000000000000000000000000000000000000000000000000000000000000; + answers[1] = 0xbfa17807147311c915e5edfc6f73dc05a30eeda3aa16ce069a8b823a5ce31276; + answers[2] = 0x47d6c2e892d5fcccee0f0f709099f4bede9338e572cd32b514241874300f777e; + answers[3] = 0x048ad91aa2911660d1c9d2f885090ec56e3334cdb30970a7fc9fa7195f440cc4; + answers[4] = 0x88ed3ec42dab95394f28600182a62493e05b714842e7f5cc236296486adb2a31; + vm.startPrank(user4); + + bytes memory payload = abi.encodeWithSignature("initialize(bytes32[])", answers); + + + implV1 = new Ghosts(); + proxy = new UUPSProxy(address(implV1), payload); + wrappedProxyV1 = Ghosts(address(proxy)); user1 = vm.addr(0x11); user2 = vm.addr(0x12); user3 = vm.addr(0x13); @@ -20,108 +42,106 @@ contract GhostsTest is Test { vm.deal(user2, 10 ether); vm.deal(user3, 10 ether); vm.deal(user4, 10 ether); - - bytes32[] memory answers = new bytes32[](4); - answers[0] = 0xbfa17807147311c915e5edfc6f73dc05a30eeda3aa16ce069a8b823a5ce31276; - answers[1] = 0x47d6c2e892d5fcccee0f0f709099f4bede9338e572cd32b514241874300f777e; - answers[2] = 0x048ad91aa2911660d1c9d2f885090ec56e3334cdb30970a7fc9fa7195f440cc4; - answers[3] = 0x88ed3ec42dab95394f28600182a62493e05b714842e7f5cc236296486adb2a31; - vm.startPrank(user4); - ghosts = new Ghosts(answers); - string[] memory hashes = new string[](8); - hashes[0] = 'lol'; - hashes[1] = 'sdasd'; - ghosts.ccGhostMaker("GHOSTS", hashes); + string[] memory hashes = new string[](2); + hashes[0] = 'avatarrr'; + hashes[1] = 'metadata'; + + wrappedProxyV1.ccGhostMaker("ghostssssssss", hashes, msg.sender); string[] memory hashess = new string[](8); - hashes[0] = 'lol'; - hashes[1] = 'sdasd'; - hashes[2] = 'sdd'; - hashes[3] = 'dsa'; - hashes[4] = 'lol'; - hashes[5] = 'sdasd'; - hashes[6] = 'sdd'; - hashes[7] = 'dsa'; - ghosts.ccAchievementMaker("test", "test", hashess, "desc", 10, 1); + hashess[0] = 'lol'; + hashess[1] = 'sdasd'; + hashess[2] = 'sdd'; + hashess[3] = 'dsa'; + hashess[4] = 'lol'; + hashess[5] = 'sdasd'; + hashess[6] = 'sdd'; + hashess[7] = 'dsa'; + wrappedProxyV1.ccAchievementMaker("test", "test", hashess, "desc", 10, 1); } function test_CreateUser() external { - string[] memory hashes = new string[](8); + string[] memory hashes = new string[](2); hashes[0] = 'lol'; hashes[1] = 'sdasd'; - ghosts.createUser("TEST1", hashes); - ghosts.startNextRace(); + wrappedProxyV1.createUser("testinggggg", hashes); + wrappedProxyV1.startNextRace(); - (address addr, uint raceId, uint comTask, uint perf, uint stbs, uint posts, uint contribs, uint ccID, uint ghostsID) = ghosts.userMap(user4); + (address addr, uint raceId, uint collabId, uint comTask, uint perf, uint stbs, uint posts, uint contribs, uint ccID, uint ghostsID) = wrappedProxyV1.userMap(user4); assertEq(addr, user4); assertTrue(ccID != 0); - console.log("ccId", ccID); + console2.log("ccId", ccID); } function test_StartNextRace() external { - string[] memory hashes = new string[](8); + string[] memory hashes = new string[](2); hashes[0] = 'lol'; hashes[1] = 'sdasd'; - ghosts.createUser("TEST1", hashes); - ghosts.startNextRace(); - (address addr, uint raceId, uint comTask, uint perf, uint stbs, uint posts, uint contribs, uint ccID, uint ghostsID) = ghosts.userMap(user4); + wrappedProxyV1.createUser("testinggggg", hashes); + wrappedProxyV1.startNextRace(); + (address addr, uint raceId, uint collabId, uint comTask, uint perf, uint stbs, uint posts, uint contribs, uint ccID, uint ghostsID) = wrappedProxyV1.userMap(user4); - assertEq(raceId, 0); + assertEq(raceId, 1); - assertEq(ghosts.balanceOf(user4), 1); + assertEq(wrappedProxyV1.balanceOf(user4), 1); } function test_SubmitRace() external { - string[] memory hashes = new string[](8); + string[] memory hashes = new string[](2); hashes[0] = 'lol'; hashes[1] = 'sdasd'; - ghosts.createUser("TEST1", hashes); - ghosts.startNextRace(); - string memory tokenURI = ghosts.tokenURI(1); - - ghosts.submitCompletedTask(0xbfa17807147311c915e5edfc6f73dc05a30eeda3aa16ce069a8b823a5ce31276, 100); + wrappedProxyV1.createUser("testinggggg", hashes); + wrappedProxyV1.startNextRace(); + string memory tokenURI = wrappedProxyV1.tokenURI(1); + (,bytes32 a,,,,) = wrappedProxyV1.finalRaceNfts(1); + (,bytes32 b,,,,) = wrappedProxyV1.finalRaceNfts(2); + (, uint collabIdd, uint raceIdd,,,,,,,) = wrappedProxyV1.userMap(user4); + + wrappedProxyV1.submitCompletedTask(0xbfa17807147311c915e5edfc6f73dc05a30eeda3aa16ce069a8b823a5ce31276, 100); vm.warp(1); vm.roll(1); - string memory tokenURINew = ghosts.tokenURI(1); + string memory tokenURINew = wrappedProxyV1.tokenURI(1); assertEq(tokenURI, "ipfs://QmU3hHax9mtBJcWD3JvS2uDSdpvjATCWkdR3kwxEfg54bw/WarmUpNFT1.json"); assertEq(tokenURINew, "ipfs://QmU3hHax9mtBJcWD3JvS2uDSdpvjATCWkdR3kwxEfg54bw/RaceNFT1.json"); - ghosts.startNextRace(); + wrappedProxyV1.startNextRace(); - string memory uriToken = ghosts.tokenURI(2); + string memory uriToken = wrappedProxyV1.tokenURI(2); - (address addr, uint raceId, uint comTask, uint perf, uint stbs, uint posts, uint contribs, uint ccID, uint ghostsID) = ghosts.userMap(user4); + (address addr, uint raceId ,uint collabId, uint comTask, uint perf, uint stbs, uint posts, uint contribs, uint ccID, uint ghostsID) = wrappedProxyV1.userMap(user4); assertEq(uriToken, "ipfs://QmU3hHax9mtBJcWD3JvS2uDSdpvjATCWkdR3kwxEfg54bw/WarmUpNFT2.json"); - assertEq( ghosts.balanceOf(user4), 2); - assertEq(raceId, 1); + assertEq(wrappedProxyV1.balanceOf(user4), 2); + assertEq(raceId, 2); assertEq(comTask, 1); - assertEq(perf, 25); + assertEq(perf, 20); - ghosts.submitCompletedTask(0x47d6c2e892d5fcccee0f0f709099f4bede9338e572cd32b514241874300f777e, 100); - string memory uriTokenNew = ghosts.tokenURI(2); + wrappedProxyV1.submitCompletedTask(0x47d6c2e892d5fcccee0f0f709099f4bede9338e572cd32b514241874300f777e, 100); + string memory uriTokenNew = wrappedProxyV1.tokenURI(2); assertEq(uriTokenNew, "ipfs://QmU3hHax9mtBJcWD3JvS2uDSdpvjATCWkdR3kwxEfg54bw/RaceNFT2.json"); + (,,uint raceId1, ,,,, ,,) = wrappedProxyV1.userMap(user4); + assertEq(raceId, 2); vm.expectRevert(); - ghosts.submitCompletedTask(0x47d6c2e892d5fcccee0f0f709099f4bede9338e572cd32b514241874300f777e, 100); + wrappedProxyV1.submitCompletedTask(0x47d6c2e892d5fcccee0f0f709099f4bede9338e572cd32b514241874300f777e, 100); - ghosts.startNextRace(); + wrappedProxyV1.startNextRace(); } function test_AddRaces() external { - string[] memory hashes = new string[](8); + string[] memory hashes = new string[](2); hashes[0] = 'lol'; hashes[1] = 'sdasd'; - ghosts.createUser("TEST1", hashes); + wrappedProxyV1.createUser("testinggggg", hashes); bytes32[] memory ass = new bytes32[](2); ass[0] = 0xda6d4713cd0f9761c5b424bd121fa20ccd8996de39f8ef0277b45c6a9606dc3f; ass[1] = 0xec553c39b395ed4e9f6c6b782d68087d16410c651001f38b158de7b9703b52f6; - ghosts.addRaces(ass); + wrappedProxyV1.addRaces(ass); ( bytes32 submittedAnswers, bytes32 answer, @@ -129,87 +149,112 @@ contract GhostsTest is Test { uint currentTaskId, uint tokenId, address userAddress - ) = ghosts.finalRaceNfts(0); - ghosts.startNextRace(); + ) = wrappedProxyV1.finalRaceNfts(0); + wrappedProxyV1.startNextRace(); - string memory tokenURI = ghosts.tokenURI(1); + string memory tokenURI = wrappedProxyV1.tokenURI(1); - ghosts.submitCompletedTask(0xbfa17807147311c915e5edfc6f73dc05a30eeda3aa16ce069a8b823a5ce31276, 100); + wrappedProxyV1.submitCompletedTask(0xbfa17807147311c915e5edfc6f73dc05a30eeda3aa16ce069a8b823a5ce31276, 100); vm.warp(1); vm.roll(1); - string memory tokenURINew = ghosts.tokenURI(1); + string memory tokenURINew = wrappedProxyV1.tokenURI(1); assertEq(tokenURI, "ipfs://QmU3hHax9mtBJcWD3JvS2uDSdpvjATCWkdR3kwxEfg54bw/WarmUpNFT1.json"); assertEq(tokenURINew, "ipfs://QmU3hHax9mtBJcWD3JvS2uDSdpvjATCWkdR3kwxEfg54bw/RaceNFT1.json"); - ghosts.transferFrom(msg.sender, address(this), 1); - ghosts.safeTransferFrom(msg.sender, user2, 1); - assertEq(ghosts.balanceOf(address(this)), 0); - assertEq(ghosts.balanceOf(user2), 0); + vm.expectRevert(); + wrappedProxyV1.transferFrom(msg.sender, address(this), 1); + vm.expectRevert(); + wrappedProxyV1.safeTransferFrom(msg.sender, user2, 1); + assertEq(wrappedProxyV1.balanceOf(address(this)), 0); + assertEq(wrappedProxyV1.balanceOf(user2), 0); - ghosts.startNextRace(); - ghosts.submitCompletedTask(0x47d6c2e892d5fcccee0f0f709099f4bede9338e572cd32b514241874300f777e, 100); + wrappedProxyV1.startNextRace(); + wrappedProxyV1.submitCompletedTask(0x47d6c2e892d5fcccee0f0f709099f4bede9338e572cd32b514241874300f777e, 100); - ghosts.startNextRace(); - ghosts.submitCompletedTask(0x048ad91aa2911660d1c9d2f885090ec56e3334cdb30970a7fc9fa7195f440cc4, 100); + wrappedProxyV1.startNextRace(); + wrappedProxyV1.submitCompletedTask(0x048ad91aa2911660d1c9d2f885090ec56e3334cdb30970a7fc9fa7195f440cc4, 100); - ghosts.startNextRace(); - ghosts.submitCompletedTask(0x88ed3ec42dab95394f28600182a62493e05b714842e7f5cc236296486adb2a31, 100); - (,bytes32 nftAnswwer,,,,) = ghosts.finalRaceNfts(5); + wrappedProxyV1.startNextRace(); + wrappedProxyV1.submitCompletedTask(0x88ed3ec42dab95394f28600182a62493e05b714842e7f5cc236296486adb2a31, 100); + (,bytes32 nftAnswwer,,,,) = wrappedProxyV1.finalRaceNfts(5); - ghosts.startNextRace(); + wrappedProxyV1.startNextRace(); vm.expectRevert(); - ghosts.submitCompletedTask(0x0000000000000000000000000000000000000000000000000000000000000000, 100); + wrappedProxyV1.submitCompletedTask(0x0000000000000000000000000000000000000000000000000000000000000000, 100); + + assertEq(wrappedProxyV1.balanceOf(user4),5); + wrappedProxyV1.submitCompletedTask(0xda6d4713cd0f9761c5b424bd121fa20ccd8996de39f8ef0277b45c6a9606dc3f, 100); + + assertEq(wrappedProxyV1.balanceOf(user4),5); + + wrappedProxyV1.startNextRace(); + wrappedProxyV1.submitCompletedTask(0xec553c39b395ed4e9f6c6b782d68087d16410c651001f38b158de7b9703b52f6, 100); - assertEq(ghosts.balanceOf(user4),5); + assertEq(wrappedProxyV1.balanceOf(user4),6); } function test_SetURI() external { - string[] memory hashes = new string[](8); + string[] memory hashes = new string[](2); hashes[0] = 'lol'; hashes[1] = 'sdasd'; - ghosts.createUser("TEST1", hashes); - ghosts.startNextRace(); - (address addr, uint raceId, uint comTask, uint perf, uint stbs, uint posts, uint contribs, uint ccID, uint ghostsID) = ghosts.userMap(user4); + wrappedProxyV1.createUser("testinggggg", hashes); + wrappedProxyV1.startNextRace(); + (address addr, uint raceId, uint collabId, uint comTask, uint perf, uint stbs, uint posts, uint contribs, uint ccID, uint ghostsID) = wrappedProxyV1.userMap(user4); - string memory oldUri = ghosts.tokenURI(1); - ghosts.setBaseURI("www.ipfs.com/"); - string memory newUri = ghosts.tokenURI(1); + string memory oldUri = wrappedProxyV1.tokenURI(1); + wrappedProxyV1.setBaseURI("www.ipfs.com/"); + string memory newUri = wrappedProxyV1.tokenURI(1); assertEq(oldUri, "ipfs://QmU3hHax9mtBJcWD3JvS2uDSdpvjATCWkdR3kwxEfg54bw/WarmUpNFT1.json"); assertEq(newUri, "www.ipfs.com/WarmUpNFT1.json"); } - // function test_AchievementCreated() external { - // string[] memory hashes = new string[](2); - // hashes[0] = 'lol'; - // hashes[1] = 'sdasd'; - // ghosts.createUser('lollkdasajd', hashes); - // ghosts.startNextRace(); - - // (,uint ccID,,,,,) = ghosts.getUser(user4); + function test_AchievementCreated() external { + string[] memory hashes = new string[](2); + hashes[0] = 'lol'; + hashes[1] = 'sdasd'; + wrappedProxyV1.createUser('lollkdasajd', hashes); + wrappedProxyV1.startNextRace(); - // address essenceAddr = ghosts.ccGetEssNFTAddr(ccID, 1); + (,uint ccID,,,,,) = wrappedProxyV1.getUser(user4); - // (bytes memory name, bytes memory desc, bytes memory imageUrl, uint256 weight, uint256 essId, uint16 essTier, uint earnedAt) = ghosts.feats(0); + (bytes memory name, bytes memory desc, bytes memory imageUrl, uint256 weight, uint256 essId, uint16 essTier, uint earnedAt) = wrappedProxyV1.feats(0); - // console.log("name", string(name)); - // console.log("desc", string(desc)); - // console.log("imageUrl", string(imageUrl)); - // console.log("weight", weight); - // console.log("essenceId", essId); - // console.log("essenceTier", essTier); - // console.log("earnedAt", earnedAt); - // console.log("essenceAddr", essenceAddr); - - // assertTrue(essenceAddr != address(0)); + console2.log("name", string(name)); + console2.log("desc", string(desc)); + console2.log("imageUrl", string(imageUrl)); + console2.log("weight", weight); + console2.log("essenceId", essId); + console2.log("essenceTier", essTier); + console2.log("earnedAt", earnedAt); - // ghosts.awardAchievements(user4, ccID); + // wrappedProxyV1.awardAchievements(user4, ccID); - // } + } function test_followUser() external { - uint id = createUser("TEST1"); + uint id = createUser("testinggggg"); + vm.roll(1); + vm.warp(1); + + uint idd = createUser('asdawsadsadacdsvfdsd'); + vm.roll(1); + vm.warp(1); + + assertTrue(idIGhostsData.User) public userMap; // used for address(0) and ownership checks - mapping(uint=>IGhostsData.User) public idToUser; - mapping(address=>IGhostsData.WarmUpNFT) private warmUpNFTs; // used to store the User's current Warmup NFT (if any) - mapping(address=>IGhostsData.RaceNFT) private raceNFTs; // used to store the User's current Race NFT (if any) - - mapping(uint=>IGhostsData.RaceNFT) public finalRaceNfts; // stores the final race NFT for each race to compare against - - mapping(uint=>bool) private graduatedNFTs; // "pops" a warmUp NFT and upgrades it to a RaceNFT. URI relies on this. - mapping(uint=>uint) private tokenIdToRaceId; // gates access to uncompleted races. URI relies on this. - - error IncorrectSubmission(); - error AccountExists(address who, uint ccID); - error AlreadySubmitted(uint raceID); - - event RaceCreated(uint indexed id); - event UserCreated(address indexed who, uint indexed ccID); - event RaceCompleted(address indexed who, uint indexed raceID, uint indexed ccID); - event RaceStarted(address indexed who, uint indexed raceID, uint indexed ccID); - - - /** - * @dev hashes are imprinted into finalRaceNFTs for comparison for submissions. - * @param dunno bytes32[] of hashes for the initial round of race content. - */ - constructor(bytes32[] memory dunno) ERC721("TEST", "TST") payable { - uint len = dunno.length; - raceCount = len; - for(uint x = 0; x < len; x++){ - finalRaceNfts[x] = IGhostsData.RaceNFT({ - submittedAnswers: bytes32('0x'), - answer: dunno[x], - performance: 0, - currentTaskId: x, - tokenId: x, - userAddress: address(0) - }); - } - tokenBaseURI = "ipfs://QmU3hHax9mtBJcWD3JvS2uDSdpvjATCWkdR3kwxEfg54bw/"; - - } - - ///////////////////////////////// - /// /// - /// External Functions /// - /// /// - ///////////////////////////////// - - - function ccGhostMaker(string memory ghosts, string[] memory hashes) external onlyOwner { - ghostsAddr = address(this); - uint id = createGhostsCC(ghosts, hashes, address(this)); - userIdToFollowed[address(this)].push(0); // tests UserId - userIdToFollowers[address(this)].push(0); // tests UserId - } - - function ccAchievementMaker( - string calldata name, - string calldata symbol, - string[] calldata essenceURI, - string calldata description, - uint16 weight, - uint16 tier - ) external onlyOwner { - for(uint x = 0; x < essenceURI.length; ++x){ - createAchievements(name, symbol, essenceURI[x], description, weight , tier); - } - } - - /** - * @param races of race content hashes - */ - function addRaces(bytes32[] memory races) external onlyOwner { - uint r = raceCount; - uint s = races.length; - for(uint x = r; x < s; ++x){ - finalRaceNfts[x] = IGhostsData.RaceNFT({ - submittedAnswers: bytes32('0x'), - answer: races[r+x], - performance: 0, - currentTaskId: (r+x), - tokenId: x, - userAddress: address(0) - }); - - } - raceCount += s; - } - - /** - * @param uri of new IPFS URI "ipfs://hash..../" - */ - function setBaseURI(string calldata uri) external onlyOwner { - tokenBaseURI = uri; - } - - /** - * @dev Metadata is reliant on graduatedNFTs[id] checks - * @dev TokenId is uncapped but raceIDs are aren't so tokenIdToRaceId ensures the metadata for the correct race is returned - * @param id of the token - */ - function tokenURI(uint id) public view override returns (string memory) { - require(_exists(id), "ERC721Metadata: URI query for nonexistent token"); - uint tokenRaceId = tokenIdToRaceId[id]; - - tokenRaceId++; - - if(!graduatedNFTs[id]){ - return string(abi.encodePacked(_baseURI(), "WarmUpNFT", tokenRaceId.toString(), ".json")); - }else{ - return string(abi.encodePacked(_baseURI(), "RaceNFT", tokenRaceId.toString(), ".json")); - } - } - - /** - * @dev creates a profile with Ghosts as well as minting a CC NFT to the msg.sender. - * @param handle of the user - * @param hashes of profiles hash[0]: avatar, hash[1]: metadata - */ - function createUser(string memory handle, string[] memory hashes) external returns (uint){ - uint ccID = userMap[msg.sender].ccID; - - if(ccID != 0){ - revert AccountExists(msg.sender, ccID); - } - - _userCounter.increment(); +// IProfileNFT internal constant ProfileNFT = +// IProfileNFT(0x57e12b7a5F38A7F9c23eBD0400e6E53F2a45F271); + +// string public tokenBaseURI; +// uint internal raceCount; + +// mapping(address=>IGhostsData.User) public userMap; // used for address(0) and ownership checks +// mapping(uint=>IGhostsData.User) public idToUser; +// mapping(address=>IGhostsData.WarmUpNFT) private warmUpNFTs; // used to store the User's current Warmup NFT (if any) +// mapping(address=>IGhostsData.RaceNFT) private raceNFTs; // used to store the User's current Race NFT (if any) + +// mapping(uint=>IGhostsData.RaceNFT) public finalRaceNfts; // stores the final race NFT for each race to compare against + +// mapping(uint=>bool) private graduatedNFTs; // "pops" a warmUp NFT and upgrades it to a RaceNFT. URI relies on this. +// mapping(uint=>uint) private tokenIdToRaceId; // gates access to uncompleted races. URI relies on this. + +// error IncorrectSubmission(); +// error AccountExists(address who, uint ccID); +// error AlreadySubmitted(uint raceID); + +// event RaceCreated(uint indexed id); +// event UserCreated(address indexed who, uint indexed ccID); +// event RaceCompleted(address indexed who, uint indexed raceID, uint indexed ccID); +// event RaceStarted(address indexed who, uint indexed raceID, uint indexed ccID); + + +// /** +// * @dev hashes are imprinted into finalRaceNFTs for comparison for submissions. +// * @param dunno bytes32[] of hashes for the initial round of race content. +// */ +// constructor(bytes32[] memory dunno) ERC721("TEST", "TST") payable { +// uint len = dunno.length; +// raceCount = len; +// for(uint x = 0; x < len; x++){ +// finalRaceNfts[x] = IGhostsData.RaceNFT({ +// submittedAnswers: bytes32('0x'), +// answer: dunno[x], +// performance: 0, +// currentTaskId: x, +// tokenId: x, +// userAddress: address(0) +// }); +// } +// tokenBaseURI = "ipfs://QmU3hHax9mtBJcWD3JvS2uDSdpvjATCWkdR3kwxEfg54bw/"; + +// } + +// ///////////////////////////////// +// /// /// +// /// External Functions /// +// /// /// +// ///////////////////////////////// + + +// function ccGhostMaker(string memory ghosts, string[] memory hashes) external onlyOwner { +// ghostsAddr = address(this); +// uint id = createGhostsCC(ghosts, hashes, address(this)); +// userIdToFollowed[address(this)].push(0); // tests UserId +// userIdToFollowers[address(this)].push(0); // tests UserId +// } + +// function ccAchievementMaker( +// string calldata name, +// string calldata symbol, +// string[] calldata essenceURI, +// string calldata description, +// uint16 weight, +// uint16 tier +// ) external onlyOwner { +// for(uint x = 0; x < essenceURI.length; ++x){ +// createAchievements(name, symbol, essenceURI[x], description, weight , tier); +// } +// } + +// /** +// * @param races of race content hashes +// */ +// function addRaces(bytes32[] memory races) external onlyOwner { +// uint r = raceCount; +// uint s = races.length; +// for(uint x = r; x < s; ++x){ +// finalRaceNfts[x] = IGhostsData.RaceNFT({ +// submittedAnswers: bytes32('0x'), +// answer: races[r+x], +// performance: 0, +// currentTaskId: (r+x), +// tokenId: x, +// userAddress: address(0) +// }); + +// } +// raceCount += s; +// } + +// /** +// * @param uri of new IPFS URI "ipfs://hash..../" +// */ +// function setBaseURI(string calldata uri) external onlyOwner { +// tokenBaseURI = uri; +// } + +// /** +// * @dev Metadata is reliant on graduatedNFTs[id] checks +// * @dev TokenId is uncapped but raceIDs are aren't so tokenIdToRaceId ensures the metadata for the correct race is returned +// * @param id of the token +// */ +// function tokenURI(uint id) public view override returns (string memory) { +// require(_exists(id), "ERC721Metadata: URI query for nonexistent token"); +// uint tokenRaceId = tokenIdToRaceId[id]; + +// tokenRaceId++; + +// if(!graduatedNFTs[id]){ +// return string(abi.encodePacked(_baseURI(), "WarmUpNFT", tokenRaceId.toString(), ".json")); +// }else{ +// return string(abi.encodePacked(_baseURI(), "RaceNFT", tokenRaceId.toString(), ".json")); +// } +// } + +// /** +// * @dev creates a profile with Ghosts as well as minting a CC NFT to the msg.sender. +// * @param handle of the user +// * @param hashes of profiles hash[0]: avatar, hash[1]: metadata +// */ +// function createUser(string memory handle, string[] memory hashes) external returns (uint){ +// uint ccID = userMap[msg.sender].ccID; + +// if(ccID != 0){ +// revert AccountExists(msg.sender, ccID); +// } + +// _userCounter.increment(); - // DataTypes.CreateProfileParams memory params; - // params.to = msg.sender; - // params.handle = handle; - // params.avatar = hashes[0]; - // params.metadata = hashes[1]; - // params.operator = address(this); - - // ProfileNFT.createProfile(params, '',''); - // ccID = ProfileNFT.getProfileIdByHandle(handle); - ccID = block.number; - uint id = _userCounter.current(); - - IGhostsData.User memory user = IGhostsData.User( - msg.sender, - 0, // raceId - 0, // completedTasks - 0, // performance - 0, // spotTheBugs - 0, // contentPosts - 0, // ctfStbContribs - id, // CyberConnect Profile ID // ghosts for tests - id // Ghosts User ID - ); - userMap[msg.sender] = user; - idToUser[id] = user; - - userAwardFeats(0, 0, msg.sender, id); - emit UserCreated(msg.sender, id); - return id; - } - - /** - * @dev Ghosts profile required. Mints WarmUpNFT for current race in progress. - * @notice User profiles are created with zero values by choice for frontend purposes whereas tokenId starts from 1. - * @notice User.raceId is only incremented after submitting which means 1st warmUp raceId = 0 balance = 1, after submit raceId = 1 && balance = 1. - */ - function startNextRace() external { - require(userMap[msg.sender].userAddress != address(0) , "No User Account"); - IGhostsData.User memory user = userMap[msg.sender]; - uint currentRace = user.raceId; - uint nextId = (_tokenIdCounter.current() + 1); - IGhostsData.WarmUpNFT memory warmUp = IGhostsData.WarmUpNFT({ - userAddress: msg.sender, - currentTaskId: currentRace, - submittedAnswers: bytes32('0x'), - tokenId: nextId - }); - warmUpNFTs[msg.sender] = warmUp; - tokenIdToRaceId[nextId] = currentRace; - - if(balanceOf(msg.sender) == 0){ - safeMint(msg.sender); - emit RaceStarted(msg.sender, currentRace, user.ccID); - }else{ - require(balanceOf(msg.sender) == user.raceId, "Finish your active race first."); - safeMint(msg.sender); - emit RaceStarted(msg.sender, currentRace, user.ccID); - } - } - - /** - * @dev WarmUpNFT required. Pops current warmUpNFT from mapping + pushes tokenId to graduatedNFTs. Metadata will return RaceNFT JSON. - * @notice User.raceId is incremented after a task has been submitted successfully. - * @param answers of user total user submissions. The hash of the hashes of individual answers. - * @param perf of user current submissions - */ - function submitCompletedTask(bytes32 answers, uint perf) external { - IGhostsData.User storage user = userMap[msg.sender]; - require(user.userAddress != address(0) , "No User Account"); - require(balanceOf(msg.sender) != 0 , "cannot submit a task without the warmUp NFT"); - - IGhostsData.WarmUpNFT memory warmUp = warmUpNFTs[msg.sender]; - - IGhostsData.RaceNFT memory raceNFT = finalRaceNfts[warmUp.currentTaskId]; - - if(raceNFT.submittedAnswers == answers) revert AlreadySubmitted(warmUp.currentTaskId); - - warmUp.submittedAnswers = answers; - - if(answers != raceNFT.answer) { - revert IncorrectSubmission(); - }else{ - emit RaceCompleted(msg.sender, user.raceId, user.ccID); - - delete warmUpNFTs[msg.sender]; - graduatedNFTs[warmUp.tokenId] = true; - user.raceId += 1; - user.completedTasks++; - - uint currentPerformance = user.performance; - uint newPerformance = (currentPerformance + perf) / raceCount; - user.performance = newPerformance; - - IGhostsData.RaceNFT memory completedNFT = IGhostsData.RaceNFT({ - submittedAnswers: answers, - answer: answers, - performance: perf, - currentTaskId: user.raceId, - tokenId: warmUp.tokenId, - userAddress: msg.sender - }); - - raceNFTs[msg.sender] = completedNFT; - delete completedNFT; - delete raceNFT; - delete warmUp; - } - } +// // DataTypes.CreateProfileParams memory params; +// // params.to = msg.sender; +// // params.handle = handle; +// // params.avatar = hashes[0]; +// // params.metadata = hashes[1]; +// // params.operator = address(this); + +// // ProfileNFT.createProfile(params, '',''); +// // ccID = ProfileNFT.getProfileIdByHandle(handle); +// ccID = block.number; +// uint id = _userCounter.current(); + +// IGhostsData.User memory user = IGhostsData.User( +// msg.sender, +// 0, // raceId +// 0, // completedTasks +// 0, // performance +// 0, // spotTheBugs +// 0, // contentPosts +// 0, // ctfStbContribs +// id, // CyberConnect Profile ID // ghosts for tests +// id // Ghosts User ID +// ); +// userMap[msg.sender] = user; +// idToUser[id] = user; + +// userAwardFeats(0, 0, msg.sender, id); +// emit UserCreated(msg.sender, id); +// return id; +// } + +// /** +// * @dev Ghosts profile required. Mints WarmUpNFT for current race in progress. +// * @notice User profiles are created with zero values by choice for frontend purposes whereas tokenId starts from 1. +// * @notice User.raceId is only incremented after submitting which means 1st warmUp raceId = 0 balance = 1, after submit raceId = 1 && balance = 1. +// */ +// function startNextRace() external { +// require(userMap[msg.sender].userAddress != address(0) , "No User Account"); +// IGhostsData.User memory user = userMap[msg.sender]; +// uint currentRace = user.raceId; +// uint nextId = (_tokenIdCounter.current() + 1); +// IGhostsData.WarmUpNFT memory warmUp = IGhostsData.WarmUpNFT({ +// userAddress: msg.sender, +// currentTaskId: currentRace, +// submittedAnswers: bytes32('0x'), +// tokenId: nextId +// }); +// warmUpNFTs[msg.sender] = warmUp; +// tokenIdToRaceId[nextId] = currentRace; + +// if(balanceOf(msg.sender) == 0){ +// safeMint(msg.sender); +// emit RaceStarted(msg.sender, currentRace, user.ccID); +// }else{ +// require(balanceOf(msg.sender) == user.raceId, "Finish your active race first."); +// safeMint(msg.sender); +// emit RaceStarted(msg.sender, currentRace, user.ccID); +// } +// } + +// /** +// * @dev WarmUpNFT required. Pops current warmUpNFT from mapping + pushes tokenId to graduatedNFTs. Metadata will return RaceNFT JSON. +// * @notice User.raceId is incremented after a task has been submitted successfully. +// * @param answers of user total user submissions. The hash of the hashes of individual answers. +// * @param perf of user current submissions +// */ +// function submitCompletedTask(bytes32 answers, uint perf) external { +// IGhostsData.User storage user = userMap[msg.sender]; +// require(user.userAddress != address(0) , "No User Account"); +// require(balanceOf(msg.sender) != 0 , "cannot submit a task without the warmUp NFT"); + +// IGhostsData.WarmUpNFT memory warmUp = warmUpNFTs[msg.sender]; + +// IGhostsData.RaceNFT memory raceNFT = finalRaceNfts[warmUp.currentTaskId]; + +// if(raceNFT.submittedAnswers == answers) revert AlreadySubmitted(warmUp.currentTaskId); + +// warmUp.submittedAnswers = answers; + +// if(answers != raceNFT.answer) { +// revert IncorrectSubmission(); +// }else{ +// emit RaceCompleted(msg.sender, user.raceId, user.ccID); + +// delete warmUpNFTs[msg.sender]; +// graduatedNFTs[warmUp.tokenId] = true; +// user.raceId += 1; +// user.completedTasks++; + +// uint currentPerformance = user.performance; +// uint newPerformance = (currentPerformance + perf) / raceCount; +// user.performance = newPerformance; + +// IGhostsData.RaceNFT memory completedNFT = IGhostsData.RaceNFT({ +// submittedAnswers: answers, +// answer: answers, +// performance: perf, +// currentTaskId: user.raceId, +// tokenId: warmUp.tokenId, +// userAddress: msg.sender +// }); + +// raceNFTs[msg.sender] = completedNFT; +// delete completedNFT; +// delete raceNFT; +// delete warmUp; +// } +// } - ///////////////////////////////// - /// /// - /// Internal Functions /// - /// /// - ///////////////////////////////// +// ///////////////////////////////// +// /// /// +// /// Internal Functions /// +// /// /// +// ///////////////////////////////// - /** - * @dev E Z P Z minting, doesn't care about warmUps or raceNFTs. Only called by this address. - * @param to, the address of the user - */ - function safeMint(address to) internal { - _tokenIdCounter.increment(); - uint256 tokenId = _tokenIdCounter.current(); - _safeMint(to, tokenId); - } +// /** +// * @dev E Z P Z minting, doesn't care about warmUps or raceNFTs. Only called by this address. +// * @param to, the address of the user +// */ +// function safeMint(address to) internal { +// _tokenIdCounter.increment(); +// uint256 tokenId = _tokenIdCounter.current(); +// _safeMint(to, tokenId); +// } - function _baseURI() internal override view returns (string memory) { - return tokenBaseURI; - } +// function _baseURI() internal override view returns (string memory) { +// return tokenBaseURI; +// } - function ccSubscribe( - DataTypes.SubscribeParams calldata params, - bytes[] calldata pre, - bytes[] calldata post - ) external onlyOwner { - ccProfileNFT.subscribe(params, pre, post); - } +// function ccSubscribe( +// DataTypes.SubscribeParams calldata params, +// bytes[] calldata pre, +// bytes[] calldata post +// ) external onlyOwner { +// ccProfileNFT.subscribe(params, pre, post); +// } - ///////////////////////////////// - /// /// - /// Overrides /// - /// /// - ///////////////////////////////// +// ///////////////////////////////// +// /// /// +// /// Overrides /// +// /// /// +// ///////////////////////////////// - // The following functions are overrides required by Solidity. - // soulbound - function transferFrom(address,address,uint256) public pure override(ERC721, IERC721) { - return; - } - function safeTransferFrom(address,address,uint256) public pure override(ERC721, IERC721) { - return; - } - function _beforeTokenTransfer(address from, address to, uint256 tokenId, uint256 batchSize) - internal - override(ERC721, ERC721Enumerable) - { - super._beforeTokenTransfer(from, to, tokenId, batchSize); - } - - function supportsInterface(bytes4 interfaceId) - public - view - override(ERC721, ERC721Enumerable) - returns (bool) - { - return super.supportsInterface(interfaceId); - } - - function onERC721Received( - address operator, - address from, - uint256 tokenId, - bytes calldata data - ) external returns (bytes4) { - return IERC721Receiver.onERC721Received.selector; - } -} \ No newline at end of file +// // The following functions are overrides required by Solidity. +// // soulbound +// function transferFrom(address,address,uint256) public pure override(ERC721, IERC721) { +// return; +// } +// function safeTransferFrom(address,address,uint256) public pure override(ERC721, IERC721) { +// return; +// } +// function _beforeTokenTransfer(address from, address to, uint256 tokenId, uint256 batchSize) +// internal +// override(ERC721, ERC721Enumerable) +// { +// super._beforeTokenTransfer(from, to, tokenId, batchSize); +// } + +// function supportsInterface(bytes4 interfaceId) +// public +// view +// override(ERC721, ERC721Enumerable) +// returns (bool) +// { +// return super.supportsInterface(interfaceId); +// } + +// function onERC721Received( +// address operator, +// address from, +// uint256 tokenId, +// bytes calldata data +// ) external returns (bytes4) { +// return IERC721Receiver.onERC721Received.selector; +// } +// } \ No newline at end of file diff --git a/test/mocks/GhostsFeats.sol b/test/mocks/GhostsFeats.sol index 8c69349..80a7cfa 100644 --- a/test/mocks/GhostsFeats.sol +++ b/test/mocks/GhostsFeats.sol @@ -1,811 +1,806 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.9; - -import {DataTypes, IProfileNFT} from "../../src/interfaces/IProfileNFT.sol"; -import {IGhosts, IGhostsData} from "../../src/interfaces/IGhosts.sol"; -import {Ownable} from "@openzeppelin/access/Ownable.sol"; -import {IERC721} from "@openzeppelin/token/ERC721/IERC721.sol"; -import {ICyberNFTBase} from "../../src/interfaces/ICyberNFTBase.sol"; -import "forge-std/Test.sol"; - -contract GhostsFeats { - bytes32 internal constant FOLLOWUSER = keccak256("FollowUser"); - bytes32 internal constant UNFOLLOWUSER = keccak256("UnfollowUser"); - bytes32 internal constant CONSUMECONTENT = keccak256("ConsumeContent"); - bytes32 internal constant CREATECONTENT = keccak256("CreateContent"); - bytes32 internal constant COMMENTCONTENT = keccak256("CreateContent"); - - address internal ghostsAddr; - IProfileNFT internal constant ccProfileNFT = - IProfileNFT(0x57e12b7a5F38A7F9c23eBD0400e6E53F2a45F271); - uint public ghostsCCID; - uint public postCount; +// // SPDX-License-Identifier: MIT +// pragma solidity ^0.8.9; + +// import {DataTypes, IProfileNFT} from "../../src/interfaces/IProfileNFT.sol"; +// import {IGhosts, IGhostsData} from "../../src/interfaces/IGhosts.sol"; +// import {Ownable} from "@openzeppelin/access/Ownable.sol"; +// import {IERC721} from "@openzeppelin/token/ERC721/IERC721.sol"; +// import {ICyberNFTBase} from "../../src/interfaces/ICyberNFTBase.sol"; +// import "forge-std/Test.sol"; + +// contract GhostsFeats { +// bytes32 internal constant FOLLOWUSER = keccak256("FollowUser"); +// bytes32 internal constant UNFOLLOWUSER = keccak256("UnfollowUser"); +// bytes32 internal constant CONSUMECONTENT = keccak256("ConsumeContent"); +// bytes32 internal constant CREATECONTENT = keccak256("CreateContent"); +// bytes32 internal constant COMMENTCONTENT = keccak256("CreateContent"); + +// address internal ghostsAddr; +// IProfileNFT internal constant ccProfileNFT = +// IProfileNFT(0x57e12b7a5F38A7F9c23eBD0400e6E53F2a45F271); +// uint public ghostsCCID; +// uint public postCount; - IGhostsData.Feat[] public featsMasterList; // list of feats - mapping(uint256 => IGhostsData.Feat) public feats; // feat ID => feat data - mapping(address => IGhostsData.UserFeats) public getUser; // stores the list of feats for each user - mapping(address => IGhostsData.Feat[]) public earnedFeats; // feat ID => feat data - - mapping(address => mapping(uint=>uint)) public addrToFeatToTiers; // user > featId > tier - mapping(address => mapping(bytes32=>uint)) internal protocolActions; // allow protocol data to be stored in mapping - mapping(uint=>uint) internal featsIdToEssID; // mapping featID => essID - - mapping(uint=>bytes[]) public idToComment; // postId => commentID => comment - mapping(address=>uint[]) public userIdToFollowers; // userId => list of followers - mapping(address=>uint[]) public userIdToFollowed; // userId => list of followed - mapping(uint=>uint) public postLikes; // postId => likes - mapping(uint=>uint) public postDislikes; // postId => dislikes - mapping(address=>IGhostsData.MiniPost[]) public userPosts; // user => postList - mapping(uint=>IGhostsData.MiniPost) public posts; // postId => mini post - - event NewFollow(uint indexed follower, uint indexed followed); - event Unfollowed(uint indexed follower, uint indexed followed); - error NoComments(); - - /** - * @dev Creates the Essence NFT representing each achievement - * @param name - essence name (the feat name?) - * @param symbol - essence symbol - * @param essenceURI - essence URI - */ - function createAchievements( - string calldata name, - string calldata symbol, - string calldata essenceURI, - string calldata description, - uint16 weight, - uint16 essTier - ) internal { - // uint essID = ccRegEssence(ghostsCCID, name, symbol, essenceURI, address(0), true, true); - uint essID = featsMasterList.length; - uint featId = featsMasterList.length; - - if(featId == 0){ - IGhostsData.Feat memory placeholder = IGhostsData.Feat({ - name: bytes('placeholder'), - desc: bytes('placeholder'), - imageUrl: bytes('placeholder'), - weight: 1, - essId: 1, - essTier: 0, - earnedAt: 1 - }); - featsMasterList.push(placeholder); - feats[featId] = placeholder; - featsIdToEssID[featId] = essID; - } - - IGhostsData.Feat memory feat = IGhostsData.Feat({ - name: bytes(name), - desc: bytes(description), - imageUrl: bytes(essenceURI), - weight: weight, - essId: essID, - essTier: essTier, - earnedAt: block.timestamp - }); - - feats[featId] = feat; - featsMasterList.push(feat); - featsIdToEssID[featId] = essID; - } - - function awardAchievements(address userAddr, uint profileId) public { - _userFeatCheck(userAddr, profileId); - } - - function _userFeatCheck(address userAddr, uint profileId) internal { - uint raceFeat = raceFeatCheck(userAddr); - uint stbFeat = stbFeatCheck(userAddr); - uint contribFeat = contribFeatCheck(userAddr); - uint followingFeat = followingFeatCheck(userAddr); - uint followerFeat = followerFeatCheck(userAddr); - uint subscriberFeat = subscriberFeatCheck(userAddr); - uint contentCreateFeat = contentFeatCheck(userAddr); - uint contentConsumeFeat = gigaBrainCheck(userAddr); - - uint[] memory tmp = new uint[](8); - tmp[0] = raceFeat; - tmp[1] = stbFeat; - tmp[2] = contribFeat; - tmp[3] = followerFeat; - tmp[4] = followingFeat; - tmp[5] = subscriberFeat; - tmp[6] = contentCreateFeat; - tmp[7] = contentConsumeFeat; - - for(uint256 i = 0; i < 8; i++) { - if(tmp[i] > 0) { - userAwardFeats(i, tmp[i], userAddr, profileId); - } - } - delete tmp; - } - - /** - * @dev adds a feat to User.feats unless the feat is already owned by the user - * @param featId the feat ID - * @param tier Tier of feat the User has earned; ex "low = 1, medium = 2, high = 3, T4 = 4" - * @param userAddr the user's address - */ - function userAwardFeats(uint featId, uint tier, address userAddr, uint profileId) internal { - IGhostsData.Feat memory feat = feats[featId]; - IGhostsData.UserFeats storage u = getUser[userAddr]; - uint[] memory empty; - - console.log("Step1"); - if(u.userAddress == address(0)){ - IGhostsData.UserFeats memory newUser = IGhostsData.UserFeats({ - userAddress: userAddr, - ccID: profileId, - followCount: 0, - followerCount: 0, - commentCount: 0, - consumeCount: 0, - createCount: 0, - followers: empty, - following: empty - }); - console.log("Step2"); - getUser[userAddr] = newUser; - console.log("Step3"); - delete newUser; - return; - } - console.log("Step4"); - // require(featId < featsMasterList.length, "featId out of range"); - - // console.log("Else Step1"); - - // // if(addrToFeatToTiers[userAddr][featId] >= tier) { - // // return; - // // } - - // uint256 profileId = ghostsCCID; // get users raceID - - // IGhostsData.Feat memory tmp = featsMasterList[featId]; - // tmp.earnedAt = block.timestamp; - // tmp.essTier = uint16(tier); - - // addrToFeatToTiers[userAddr][featId] = tier; - - // earnedFeats[userAddr].push(tmp); - - // ccCollectEss(userAddr, profileId, feat.essId); - } +// IGhostsData.Feat[] public featsMasterList; // list of feats +// mapping(uint256 => IGhostsData.Feat) public feats; // feat ID => feat data +// mapping(address => IGhostsData.UserFeats) public getUser; // stores the list of feats for each user +// mapping(address => IGhostsData.Feat[]) public earnedFeats; // feat ID => feat data + +// mapping(address => mapping(uint=>uint)) public addrToFeatToTiers; // user > featId > tier +// mapping(address => mapping(bytes32=>uint)) internal protocolActions; // allow protocol data to be stored in mapping +// mapping(uint=>uint) internal featsIdToEssID; // mapping featID => essID + +// mapping(uint=>bytes[]) public idToComment; // postId => commentID => comment +// mapping(address=>uint[]) public userIdToFollowers; // userId => list of followers +// mapping(address=>uint[]) public userIdToFollowed; // userId => list of followed +// mapping(uint=>uint) public postLikes; // postId => likes +// mapping(uint=>uint) public postDislikes; // postId => dislikes +// mapping(address=>IGhostsData.MiniPost[]) public userPosts; // user => postList +// mapping(uint=>IGhostsData.MiniPost) public posts; // postId => mini post + +// event NewFollow(uint indexed follower, uint indexed followed); +// event Unfollowed(uint indexed follower, uint indexed followed); +// error NoComments(); + +// /** +// * @dev Creates the Essence NFT representing each achievement +// * @param name - essence name (the feat name?) +// * @param symbol - essence symbol +// * @param essenceURI - essence URI +// */ +// function createAchievements( +// string calldata name, +// string calldata symbol, +// string calldata essenceURI, +// string calldata description, +// uint16 weight, +// uint16 essTier +// ) internal { +// // uint essID = ccRegEssence(ghostsCCID, name, symbol, essenceURI, address(0), true, true); +// uint essID = featsMasterList.length; +// uint featId = featsMasterList.length; + +// if(featId == 0){ +// IGhostsData.Feat memory placeholder = IGhostsData.Feat({ +// name: bytes('placeholder'), +// desc: bytes('placeholder'), +// imageUrl: bytes('placeholder'), +// weight: 1, +// essId: 1, +// essTier: 0, +// earnedAt: 1 +// }); +// featsMasterList.push(placeholder); +// feats[featId] = placeholder; +// featsIdToEssID[featId] = essID; +// } + +// IGhostsData.Feat memory feat = IGhostsData.Feat({ +// name: bytes(name), +// desc: bytes(description), +// imageUrl: bytes(essenceURI), +// weight: weight, +// essId: essID, +// essTier: essTier, +// earnedAt: block.timestamp +// }); + +// feats[featId] = feat; +// featsMasterList.push(feat); +// featsIdToEssID[featId] = essID; +// } + +// function awardAchievements(address userAddr, uint profileId) public { +// _userFeatCheck(userAddr, profileId); +// } + +// function _userFeatCheck(address userAddr, uint profileId) internal { +// uint raceFeat = raceFeatCheck(userAddr); +// uint stbFeat = stbFeatCheck(userAddr); +// uint contribFeat = contribFeatCheck(userAddr); +// uint followingFeat = followingFeatCheck(userAddr); +// uint followerFeat = followerFeatCheck(userAddr); +// uint subscriberFeat = subscriberFeatCheck(userAddr); +// uint contentCreateFeat = contentFeatCheck(userAddr); +// uint contentConsumeFeat = gigaBrainCheck(userAddr); + +// uint[] memory tmp = new uint[](8); +// tmp[0] = raceFeat; +// tmp[1] = stbFeat; +// tmp[2] = contribFeat; +// tmp[3] = followerFeat; +// tmp[4] = followingFeat; +// tmp[5] = subscriberFeat; +// tmp[6] = contentCreateFeat; +// tmp[7] = contentConsumeFeat; + +// for(uint256 i = 0; i < 8; i++) { +// if(tmp[i] > 0) { +// userAwardFeats(i, tmp[i], userAddr, profileId); +// } +// } +// delete tmp; +// } + +// /** +// * @dev adds a feat to User.feats unless the feat is already owned by the user +// * @param featId the feat ID +// * @param tier Tier of feat the User has earned; ex "low = 1, medium = 2, high = 3, T4 = 4" +// * @param userAddr the user's address +// */ +// function userAwardFeats(uint featId, uint tier, address userAddr, uint profileId) internal { +// IGhostsData.Feat memory feat = feats[featId]; +// IGhostsData.UserFeats storage u = getUser[userAddr]; +// uint[] memory empty; + +// if(u.userAddress == address(0)){ +// IGhostsData.UserFeats memory newUser = IGhostsData.UserFeats({ +// userAddress: userAddr, +// ccID: profileId, +// followCount: 0, +// followerCount: 0, +// commentCount: 0, +// consumeCount: 0, +// createCount: 0, +// followers: empty, +// following: empty +// }); +// getUser[userAddr] = newUser; +// delete newUser; +// return; +// } +// // require(featId < featsMasterList.length, "featId out of range"); + + +// // // if(addrToFeatToTiers[userAddr][featId] >= tier) { +// // // return; +// // // } + +// // uint256 profileId = ghostsCCID; // get users raceID + +// // IGhostsData.Feat memory tmp = featsMasterList[featId]; +// // tmp.earnedAt = block.timestamp; +// // tmp.essTier = uint16(tier); + +// // addrToFeatToTiers[userAddr][featId] = tier; + +// // earnedFeats[userAddr].push(tmp); + +// // ccCollectEss(userAddr, profileId, feat.essId); +// } - error AlreadyFollowed(address userAddress); - /** - * @dev Stores the action to the user addr then subscribes to the CC profile - * @notice This is the protocol's follow user function - * @param idToFollow the address of the user being followed - * @param idFollowing the address of the user following another user - */ - function followUser( - uint idToFollow, - uint idFollowing, - address userToFollow - ) public returns (bool) { - IGhostsData.UserFeats storage caller = getUser[msg.sender]; - IGhostsData.UserFeats storage user = getUser[userToFollow]; - uint[] memory followersArr = user.followers; - uint len = followersArr.length; - if (msg.sender == userToFollow) { - return false; - } - - for (uint i = 0; i < len; ++i) { - require(followersArr[i] != idFollowing, "You already follow this user"); - } - - caller.following.push(idToFollow); - user.followers.push(idFollowing); - caller.followCount ++; - user.followerCount ++; - protocolActions[msg.sender][FOLLOWUSER] ++; - emit NewFollow(idFollowing, idToFollow); - delete followersArr; - return true; - } +// error AlreadyFollowed(address userAddress); +// /** +// * @dev Stores the action to the user addr then subscribes to the CC profile +// * @notice This is the protocol's follow user function +// * @param idToFollow the address of the user being followed +// * @param idFollowing the address of the user following another user +// */ +// function followUser( +// uint idToFollow, +// uint idFollowing, +// address userToFollow +// ) public returns (bool) { +// IGhostsData.UserFeats storage caller = getUser[msg.sender]; +// IGhostsData.UserFeats storage user = getUser[userToFollow]; +// uint[] memory followersArr = user.followers; +// uint len = followersArr.length; +// if (msg.sender == userToFollow) { +// return false; +// } + +// for (uint i = 0; i < len; ++i) { +// require(followersArr[i] != idFollowing, "You already follow this user"); +// } + +// caller.following.push(idToFollow); +// user.followers.push(idFollowing); +// caller.followCount ++; +// user.followerCount ++; +// protocolActions[msg.sender][FOLLOWUSER] ++; +// emit NewFollow(idFollowing, idToFollow); +// delete followersArr; +// return true; +// } - function unfollowUser( - uint idToUnfollow, - uint idUnfollowing, - address userToUnfollow - ) public returns (bool) { - getUser[userToUnfollow].followerCount -= 1; - getUser[msg.sender].followCount -= 1; +// function unfollowUser( +// uint idToUnfollow, +// uint idUnfollowing, +// address userToUnfollow +// ) public returns (bool) { +// getUser[userToUnfollow].followerCount -= 1; +// getUser[msg.sender].followCount -= 1; - protocolActions[msg.sender][UNFOLLOWUSER] += 1; +// protocolActions[msg.sender][UNFOLLOWUSER] += 1; - delete userIdToFollowed[msg.sender][idToUnfollow]; - delete userIdToFollowers[userToUnfollow][idUnfollowing]; +// delete userIdToFollowed[msg.sender][idToUnfollow]; +// delete userIdToFollowers[userToUnfollow][idUnfollowing]; - emit Unfollowed(idUnfollowing, idToUnfollow); - return true; - } +// emit Unfollowed(idUnfollowing, idToUnfollow); +// return true; +// } - function consumeContent(uint256 profileId, uint256 essID) public { - protocolActions[msg.sender][CONSUMECONTENT] += 1; - getUser[msg.sender].consumeCount += 1; - ccCollectEss(msg.sender, profileId, essID); - } - - /** - * @dev Create Content for protocol uses this function to perform the following: - * 1. update protocolAction[] stores the amount of calls to this function - * 2. update User.createCount, increment by 1 - * 3. call function ccRegEssence() to register the essence with the profile - */ - function createContent( - uint256 profileId, - string calldata name, - string calldata symbol, - string calldata essenceURI, - address essenceMw, - bool transferable, - bool deployAtReg, - bool allowComments - ) public returns (uint){ - IGhostsData.UserFeats storage caller = getUser[msg.sender]; - bytes[] memory emptyComments; - if(postCount == 0){ - IGhostsData.MiniPost memory newPost; - newPost.creator = ghostsAddr; - newPost.creatorCCID = ghostsCCID; - newPost.postId = postCount; - newPost.likes = 0; - newPost.dislikes = 0; - newPost.allowComments = allowComments; - newPost.uri = essenceURI; - newPost.comments = emptyComments; - posts[postCount] = newPost; - delete newPost; - postCount++; - } - - uint actions = protocolActions[msg.sender][CREATECONTENT] += 1; - // uint id = ccRegEssence(profileId, name, symbol, essenceURI, essenceMw, transferable, deployAtReg); - IGhostsData.MiniPost memory newPost; - newPost.creator = msg.sender; - newPost.creatorCCID = profileId; - newPost.postId = postCount; - newPost.likes = 0; - newPost.dislikes = 0; - newPost.allowComments = allowComments; - newPost.uri = essenceURI; - newPost.comments = emptyComments; - - userPosts[msg.sender].push(newPost); - posts[postCount] = newPost; - caller.createCount = actions; - delete newPost; - ++postCount; - return caller.createCount; - } - - function commentContent( - bytes calldata comment, - uint postId - ) public { - IGhostsData.MiniPost storage post = posts[postId]; - if(post.comments.length == 0){ - idToComment[postId].push(bytes(".")); - getUser[ghostsAddr].commentCount = 1; - post.comments.push(bytes(".")); - } - - if(post.allowComments){ - uint count = getUser[msg.sender].commentCount; - if(count == 0) { - protocolActions[msg.sender][COMMENTCONTENT] = 1; - getUser[msg.sender].commentCount = 1; - idToComment[postId].push(comment); - post.comments.push(comment); - }else{ - protocolActions[msg.sender][COMMENTCONTENT] += 1; - getUser[msg.sender].commentCount += 1; - idToComment[postId].push(comment); - post.comments.push(comment); - } - }else{revert NoComments();} - } - - function likePost(uint postId) external { - IGhostsData.MiniPost storage post = posts[postId]; - post.likes = post.likes + 1; - postLikes[postId] = postLikes[postId] + 1; - } - - function dislikePost(uint postId) external { - IGhostsData.MiniPost storage post = posts[postId]; - post.dislikes = post.dislikes + 1; - postDislikes[postId] = postDislikes[postId] + 1; - } - - function getPost(uint postId) external view returns (IGhostsData.MiniPost memory){ - return posts[postId]; - } - - // function getAllPosts() public view returns (IGhostsData.MiniPost[] memory){ - // return posts; - // } - function getAllUserPosts(address userAddr) external view returns (IGhostsData.MiniPost[] memory){ - return userPosts[userAddr]; - } - - function getPostLikeCount(uint postId) external view returns (uint256) { - return postLikes[postId]; - } - - function getPostDislikeCount(uint postId) external view returns (uint256) { - return postDislikes[postId]; - } - - function getPostComments(uint postId) external view returns (bytes[] memory) { - return idToComment[postId]; - } - - /** - * @dev check User.raceID and returns feat level based on completion - */ - function raceFeatCheck(address userAddr) internal returns (uint256) { - IGhostsData.User memory user = IGhosts(ghostsAddr).getGhostsProfile(userAddr); - uint256 currentRaceID = user.raceId; - - if (currentRaceID >= 2) { - if (currentRaceID < 8) { - delete user; - return 1; - } - } else if (currentRaceID >= 8) { - if (currentRaceID < 15) { - delete user; - return 2; - } - } else if (currentRaceID >= 15) { - if (currentRaceID < 22) { - delete user; - return 3; - } - } else if (currentRaceID >= 22) { - delete user; - return 4; - } else { - delete user; - return 0; - } - } - - /** - * @dev check User.spotTheBugs and returns feat level based on completion - */ - function stbFeatCheck(address userAddr) internal returns (uint256) { - uint256 spotTheBugs = IGhosts(ghostsAddr).getGhostsProfile(userAddr).spotTheBugs; // get users spotTheBugs count - if (spotTheBugs >= 5) { - if (spotTheBugs < 20) { - return 1; - } - } else if (spotTheBugs >= 20) { - if (spotTheBugs < 50) { - return 2; - } - } else if (spotTheBugs >= 50) { - if (spotTheBugs < 100) { - return 3; - } - } else if (spotTheBugs >= 100) { - return 4; - } else { - return 0; - } - return 0; - } - - /** - * @dev check User.ctfStbContribs and returns feat level based on completion - */ - function contribFeatCheck(address userAddr) - internal - returns (uint256) - { - uint256 contribCount = IGhosts(ghostsAddr).getGhostsProfile(userAddr).ctfStbContribs; // get users contribCount - if (contribCount >= 1) { - if (contribCount < 5) { - return 1; - } - } else if (contribCount >= 5) { - if (contribCount < 15) { - return 2; - } - } else if (contribCount >= 15) { - if (contribCount < 30) { - return 3; - } - } else if (contribCount >= 30) { - return 4; - } else { - return 0; - } - return 0; - } - - /** - * @dev checks protocolActions[userAddr][activityType] and returns feat level based on count - */ - function followingFeatCheck(address userAddr) internal view returns (uint256) { - uint256 activityCount = protocolActions[userAddr][FOLLOWUSER]; // get users social activity count - if (activityCount >= 1) { - if (activityCount < 5) { - return 1; - } - } else if (activityCount >= 5) { - if (activityCount < 15) { - return 2; - } - } else if (activityCount >= 15) { - if (activityCount < 30) { - return 3; - } - } else if (activityCount >= 30) { - return 4; - } else { - return 0; - } - return 0; - } - - function followerFeatCheck(address userAddr) internal view returns (uint256) { - - uint256 activityCount = getUser[userAddr].followerCount; // get users social activity count - - if (activityCount >= 1) { - if (activityCount < 5) { - return 1; - } - } else if (activityCount >= 5) { - if (activityCount < 15) { - return 2; - } - } else if (activityCount >= 15) { - if (activityCount < 30) { - return 3; - } - } else if(activityCount >= 30) { - return 4; - }else{ - return 0; - } - } - - /** - * @dev checks getUser[userAddr].followCount and returns feat level based on count - */ - function subscriberFeatCheck(address userAddr) internal returns (uint256) { +// function consumeContent(uint256 profileId, uint256 essID) public { +// protocolActions[msg.sender][CONSUMECONTENT] += 1; +// getUser[msg.sender].consumeCount += 1; +// ccCollectEss(msg.sender, profileId, essID); +// } + +// /** +// * @dev Create Content for protocol uses this function to perform the following: +// * 1. update protocolAction[] stores the amount of calls to this function +// * 2. update User.createCount, increment by 1 +// * 3. call function ccRegEssence() to register the essence with the profile +// */ +// function createContent( +// uint256 profileId, +// string calldata name, +// string calldata symbol, +// string calldata essenceURI, +// address essenceMw, +// bool transferable, +// bool deployAtReg, +// bool allowComments +// ) public returns (uint){ +// IGhostsData.UserFeats storage caller = getUser[msg.sender]; +// bytes[] memory emptyComments; +// if(postCount == 0){ +// IGhostsData.MiniPost memory newPost; +// newPost.creator = ghostsAddr; +// newPost.creatorCCID = ghostsCCID; +// newPost.postId = postCount; +// newPost.likes = 0; +// newPost.dislikes = 0; +// newPost.allowComments = allowComments; +// newPost.uri = essenceURI; +// newPost.comments = emptyComments; +// posts[postCount] = newPost; +// delete newPost; +// postCount++; +// } + +// uint actions = protocolActions[msg.sender][CREATECONTENT] += 1; +// // uint id = ccRegEssence(profileId, name, symbol, essenceURI, essenceMw, transferable, deployAtReg); +// IGhostsData.MiniPost memory newPost; +// newPost.creator = msg.sender; +// newPost.creatorCCID = profileId; +// newPost.postId = postCount; +// newPost.likes = 0; +// newPost.dislikes = 0; +// newPost.allowComments = allowComments; +// newPost.uri = essenceURI; +// newPost.comments = emptyComments; + +// userPosts[msg.sender].push(newPost); +// posts[postCount] = newPost; +// caller.createCount = actions; +// delete newPost; +// ++postCount; +// return caller.createCount; +// } + +// function commentContent( +// bytes calldata comment, +// uint postId +// ) public { +// IGhostsData.MiniPost storage post = posts[postId]; +// if(post.comments.length == 0){ +// idToComment[postId].push(bytes(".")); +// getUser[ghostsAddr].commentCount = 1; +// post.comments.push(bytes(".")); +// } + +// if(post.allowComments){ +// uint count = getUser[msg.sender].commentCount; +// if(count == 0) { +// protocolActions[msg.sender][COMMENTCONTENT] = 1; +// getUser[msg.sender].commentCount = 1; +// idToComment[postId].push(comment); +// post.comments.push(comment); +// }else{ +// protocolActions[msg.sender][COMMENTCONTENT] += 1; +// getUser[msg.sender].commentCount += 1; +// idToComment[postId].push(comment); +// post.comments.push(comment); +// } +// }else{revert NoComments();} +// } + +// function likePost(uint postId) external { +// IGhostsData.MiniPost storage post = posts[postId]; +// post.likes = post.likes + 1; +// postLikes[postId] = postLikes[postId] + 1; +// } + +// function dislikePost(uint postId) external { +// IGhostsData.MiniPost storage post = posts[postId]; +// post.dislikes = post.dislikes + 1; +// postDislikes[postId] = postDislikes[postId] + 1; +// } + +// function getPost(uint postId) external view returns (IGhostsData.MiniPost memory){ +// return posts[postId]; +// } + +// // function getAllPosts() public view returns (IGhostsData.MiniPost[] memory){ +// // return posts; +// // } +// function getAllUserPosts(address userAddr) external view returns (IGhostsData.MiniPost[] memory){ +// return userPosts[userAddr]; +// } + +// function getPostLikeCount(uint postId) external view returns (uint256) { +// return postLikes[postId]; +// } + +// function getPostDislikeCount(uint postId) external view returns (uint256) { +// return postDislikes[postId]; +// } + +// function getPostComments(uint postId) external view returns (bytes[] memory) { +// return idToComment[postId]; +// } + +// /** +// * @dev check User.raceID and returns feat level based on completion +// */ +// function raceFeatCheck(address userAddr) internal returns (uint256) { +// IGhostsData.User memory user = IGhosts(ghostsAddr).getGhostsProfile(userAddr); +// uint256 currentRaceID = user.raceId; + +// if (currentRaceID >= 2) { +// if (currentRaceID < 8) { +// delete user; +// return 1; +// } +// } else if (currentRaceID >= 8) { +// if (currentRaceID < 15) { +// delete user; +// return 2; +// } +// } else if (currentRaceID >= 15) { +// if (currentRaceID < 22) { +// delete user; +// return 3; +// } +// } else if (currentRaceID >= 22) { +// delete user; +// return 4; +// } else { +// delete user; +// return 0; +// } +// } + +// /** +// * @dev check User.spotTheBugs and returns feat level based on completion +// */ +// function stbFeatCheck(address userAddr) internal returns (uint256) { +// uint256 spotTheBugs = IGhosts(ghostsAddr).getGhostsProfile(userAddr).spotTheBugs; // get users spotTheBugs count +// if (spotTheBugs >= 5) { +// if (spotTheBugs < 20) { +// return 1; +// } +// } else if (spotTheBugs >= 20) { +// if (spotTheBugs < 50) { +// return 2; +// } +// } else if (spotTheBugs >= 50) { +// if (spotTheBugs < 100) { +// return 3; +// } +// } else if (spotTheBugs >= 100) { +// return 4; +// } else { +// return 0; +// } +// return 0; +// } + +// /** +// * @dev check User.ctfStbContribs and returns feat level based on completion +// */ +// function contribFeatCheck(address userAddr) +// internal +// returns (uint256) +// { +// uint256 contribCount = IGhosts(ghostsAddr).getGhostsProfile(userAddr).ctfStbContribs; // get users contribCount +// if (contribCount >= 1) { +// if (contribCount < 5) { +// return 1; +// } +// } else if (contribCount >= 5) { +// if (contribCount < 15) { +// return 2; +// } +// } else if (contribCount >= 15) { +// if (contribCount < 30) { +// return 3; +// } +// } else if (contribCount >= 30) { +// return 4; +// } else { +// return 0; +// } +// return 0; +// } + +// /** +// * @dev checks protocolActions[userAddr][activityType] and returns feat level based on count +// */ +// function followingFeatCheck(address userAddr) internal view returns (uint256) { +// uint256 activityCount = protocolActions[userAddr][FOLLOWUSER]; // get users social activity count +// if (activityCount >= 1) { +// if (activityCount < 5) { +// return 1; +// } +// } else if (activityCount >= 5) { +// if (activityCount < 15) { +// return 2; +// } +// } else if (activityCount >= 15) { +// if (activityCount < 30) { +// return 3; +// } +// } else if (activityCount >= 30) { +// return 4; +// } else { +// return 0; +// } +// return 0; +// } + +// function followerFeatCheck(address userAddr) internal view returns (uint256) { + +// uint256 activityCount = getUser[userAddr].followerCount; // get users social activity count + +// if (activityCount >= 1) { +// if (activityCount < 5) { +// return 1; +// } +// } else if (activityCount >= 5) { +// if (activityCount < 15) { +// return 2; +// } +// } else if (activityCount >= 15) { +// if (activityCount < 30) { +// return 3; +// } +// } else if(activityCount >= 30) { +// return 4; +// }else{ +// return 0; +// } +// } + +// /** +// * @dev checks getUser[userAddr].followCount and returns feat level based on count +// */ +// function subscriberFeatCheck(address userAddr) internal returns (uint256) { - uint profileId = IGhosts(ghostsAddr).getGhostsProfile(userAddr).ccID; - address subNFT = ccProfileNFT.getSubscribeNFT(profileId); - - if(subNFT != address(0)) { - - uint subCount = ICyberNFTBase(subNFT).totalMinted(); - - if (subCount >= 1) { - if (subCount < 5) { - return 1; - } - } else if (subCount >= 5) { - if (subCount < 15) { - return 2; - } - } else if (subCount >= 15) { - if (subCount < 30) { - return 3; - } - } else if (subCount >= 30) { - return 4; - } else { - return 0; - } - return 0; - }else{ - return 0;} - } - - /** - * @dev check User.contentPosts and returns feat level based on completion - */ - function contentFeatCheck(address userAddr) internal view returns (uint256) { - uint256 contentCount = protocolActions[userAddr][CREATECONTENT]; // get users post count - if (contentCount >= 1) { - if (contentCount < 5) { - return 1; - } - } else if (contentCount >= 5) { - if (contentCount < 15) { - return 2; - } - } else if (contentCount >= 15) { - if (contentCount < 30) { - return 3; - } - } else if (contentCount >= 30) { - return 4; - } else { - return 0; - } - return 0; - } - - /** - * @dev checks getUser[userAddr].consumeContent and returns feat level based on count - - */ - function gigaBrainCheck(address userAddr) internal view returns (uint256) { - uint256 consumeCount = protocolActions[userAddr][CONSUMECONTENT]; // get users post count - if (consumeCount >= 1) { - if (consumeCount < 5) { - return 1; - } - } else if (consumeCount >= 5) { - if (consumeCount < 15) { - return 2; - } - } else if (consumeCount >= 15) { - if (consumeCount < 30) { - return 3; - } - } else if (consumeCount >= 30) { - return 4; - } else { - return 0; - } - return 0; - } - - - ///////////////////////////////// - /// /// - /// Internal Functions /// - /// /// - ///////////////////////////////// - function ccGetMetadata(uint256 profileId) - public - view - returns (string memory) - { - return ccProfileNFT.getMetadata(profileId); - } - - function ccGetAvatar(uint256 profileId) - public - view - returns (string memory) - { - return ccProfileNFT.getAvatar(profileId); - } - - function ccGetSubNFTAddr(uint256 profileId) - public - view - returns (address) - { - return ccProfileNFT.getSubscribeNFT(profileId); - } - - function ccGetSubURI(uint256 profileId) - public - view - returns (string memory) - { - return ccProfileNFT.getSubscribeNFTTokenURI(profileId); - } - - function ccGetEssNFTAddr(uint256 profileId, uint256 essId) - public - view - returns (address) - { - return ccProfileNFT.getEssenceNFT(profileId, essId); - } - - function ccGetEssURI(uint256 profileId, uint256 essId) - public - view - returns (string memory) - { - return ccProfileNFT.getEssenceNFTTokenURI(profileId, essId); - } - - /** - * @dev sets the namespace owner of the ccProfileNFT to the provided address. - * @param addr of new namespace owner - */ - function ccSetNSOwner(address addr) public { - ccProfileNFT.setNamespaceOwner(addr); - } - - function ccRegEssence( - uint256 profileId, - string calldata name, - string calldata symbol, - string calldata essenceURI, - address essenceMw, - bool transferable, - bool deployAtReg - ) public returns (uint256) { - DataTypes.RegisterEssenceParams memory params; - - params.profileId = profileId; - params.name = name; - params.symbol = symbol; - params.essenceTokenURI = essenceURI; - params.essenceMw = essenceMw; - params.transferable = transferable; - params.deployAtRegister = deployAtReg; - - return _ccRegEssence(params); - } - - function ccCollectEss( - address who, - uint256 profileId, - uint256 essenceId - ) public { - DataTypes.CollectParams memory params; - params.collector = who; - params.profileId = profileId; - params.essenceId = essenceId; - - _ccCollectEss(params); - } - - function ccSetMetadata(uint256 profileId, string calldata metadata) - public - { - _ccSetMetadata(profileId, metadata); - } - - function ccSetSubData( - uint256 profileId, - string calldata uri, - address mw, - bytes calldata mwData - ) public { - _ccSetSubData(profileId, uri, mw, mwData); - } - - function ccSetEssData( - uint256 profileId, - uint256 essId, - string calldata uri, - address mw, - bytes calldata mwData - ) public { - _ccSetEssData(profileId, essId, uri, mw, mwData); - } - - function ccSetPrimary(uint256 profileId) public { - _ccSetPrimary(profileId); - } - - function _ccRegEssence(DataTypes.RegisterEssenceParams memory params) - internal - returns (uint256) - { - uint id = ccProfileNFT.registerEssence(params, ''); - return id; - } - - function _ccCollectEss(DataTypes.CollectParams memory params) internal { - ccProfileNFT.collect(params, "", ""); - } - - function _ccSetMetadata(uint256 profileId, string calldata metadata) - internal - { - ccProfileNFT.setMetadata(profileId, metadata); - } - - function _ccSetSubData( - uint256 profileId, - string calldata uri, - address mw, - bytes calldata mwData - ) internal { - ccProfileNFT.setSubscribeData(profileId, uri, mw, mwData); - } - - function _ccSetEssData( - uint256 profileId, - uint256 essId, - string calldata uri, - address mw, - bytes calldata mwData - ) internal { - ccProfileNFT.setEssenceData(profileId, essId, uri, mw, mwData); - } - - function _ccSetPrimary(uint256 profileId) internal { - ccProfileNFT.setPrimaryProfile(profileId); - } - - /** - * @notice Check if the profile issued EssenceNFT is collected by me. - * - * @param profileId The profile id. - * @param essenceId The essence id. - * @param me The address to check. - * @param _namespace The address of the ccProfileNFT - */ - function isCollectedByMe( - uint256 profileId, - uint256 essenceId, - address me, - address _namespace - ) external view returns (bool) { - address essNFTAddr = IProfileNFT(_namespace).getEssenceNFT( - profileId, - essenceId - ); - if (essNFTAddr == address(0)) { - return false; - } - - return IERC721(essNFTAddr).balanceOf(me) > 0; - } - - /** - * @notice Check if the profile is subscribed by me. - * - * @param profileId The profile id. - * @param me The address to check. - * @param _namespace The address of the ProfileNFT - */ - function isSubscribedByMe(uint256 profileId, address me, address _namespace) - external - view - returns (bool) - { - address subNFTAddr = IProfileNFT(_namespace).getSubscribeNFT(profileId); - if (subNFTAddr == address(0)) { - return false; - } - return IERC721(subNFTAddr).balanceOf(me) > 0; - } - - function createGhostsCC(string memory handle, string[] memory hashes, address op) internal returns(uint) { - // DataTypes.CreateProfileParams memory params; - // params.to = msg.sender; - // params.handle = handle; - // params.avatar = hashes[0]; - // params.metadata = hashes[1]; - // params.operator = op; - // ccProfileNFT.createProfile(params, '',''); - // uint ccID = ccProfileNFT.getProfileIdByHandle(handle); - IGhostsData.UserFeats storage u = getUser[ghostsAddr]; - string[] memory hashes = new string[](2); - hashes[0] = 'lol'; - hashes[1] = 'sdasd'; - uint256[] memory empty; - uint256 ccID = IGhosts(ghostsAddr).createUser(handle, hashes); - if(u.userAddress == address(0)){ - IGhostsData.UserFeats memory newUser = IGhostsData.UserFeats({ - userAddress: ghostsAddr, - ccID: ccID, - followCount: 0, - followerCount: 0, - commentCount: 0, - consumeCount: 0, - createCount: 0, - followers: empty, - following: empty - }); - getUser[ghostsAddr] = newUser; - ghostsCCID = ccID; - delete newUser; - return ccID; - } - } -} +// uint profileId = IGhosts(ghostsAddr).getGhostsProfile(userAddr).ccID; +// address subNFT = ccProfileNFT.getSubscribeNFT(profileId); + +// if(subNFT != address(0)) { + +// uint subCount = ICyberNFTBase(subNFT).totalMinted(); + +// if (subCount >= 1) { +// if (subCount < 5) { +// return 1; +// } +// } else if (subCount >= 5) { +// if (subCount < 15) { +// return 2; +// } +// } else if (subCount >= 15) { +// if (subCount < 30) { +// return 3; +// } +// } else if (subCount >= 30) { +// return 4; +// } else { +// return 0; +// } +// return 0; +// }else{ +// return 0;} +// } + +// /** +// * @dev check User.contentPosts and returns feat level based on completion +// */ +// function contentFeatCheck(address userAddr) internal view returns (uint256) { +// uint256 contentCount = protocolActions[userAddr][CREATECONTENT]; // get users post count +// if (contentCount >= 1) { +// if (contentCount < 5) { +// return 1; +// } +// } else if (contentCount >= 5) { +// if (contentCount < 15) { +// return 2; +// } +// } else if (contentCount >= 15) { +// if (contentCount < 30) { +// return 3; +// } +// } else if (contentCount >= 30) { +// return 4; +// } else { +// return 0; +// } +// return 0; +// } + +// /** +// * @dev checks getUser[userAddr].consumeContent and returns feat level based on count + +// */ +// function gigaBrainCheck(address userAddr) internal view returns (uint256) { +// uint256 consumeCount = protocolActions[userAddr][CONSUMECONTENT]; // get users post count +// if (consumeCount >= 1) { +// if (consumeCount < 5) { +// return 1; +// } +// } else if (consumeCount >= 5) { +// if (consumeCount < 15) { +// return 2; +// } +// } else if (consumeCount >= 15) { +// if (consumeCount < 30) { +// return 3; +// } +// } else if (consumeCount >= 30) { +// return 4; +// } else { +// return 0; +// } +// return 0; +// } + + +// ///////////////////////////////// +// /// /// +// /// Internal Functions /// +// /// /// +// ///////////////////////////////// +// function ccGetMetadata(uint256 profileId) +// public +// view +// returns (string memory) +// { +// return ccProfileNFT.getMetadata(profileId); +// } + +// function ccGetAvatar(uint256 profileId) +// public +// view +// returns (string memory) +// { +// return ccProfileNFT.getAvatar(profileId); +// } + +// function ccGetSubNFTAddr(uint256 profileId) +// public +// view +// returns (address) +// { +// return ccProfileNFT.getSubscribeNFT(profileId); +// } + +// function ccGetSubURI(uint256 profileId) +// public +// view +// returns (string memory) +// { +// return ccProfileNFT.getSubscribeNFTTokenURI(profileId); +// } + +// function ccGetEssNFTAddr(uint256 profileId, uint256 essId) +// public +// view +// returns (address) +// { +// return ccProfileNFT.getEssenceNFT(profileId, essId); +// } + +// function ccGetEssURI(uint256 profileId, uint256 essId) +// public +// view +// returns (string memory) +// { +// return ccProfileNFT.getEssenceNFTTokenURI(profileId, essId); +// } + +// /** +// * @dev sets the namespace owner of the ccProfileNFT to the provided address. +// * @param addr of new namespace owner +// */ +// function ccSetNSOwner(address addr) public { +// ccProfileNFT.setNamespaceOwner(addr); +// } + +// function ccRegEssence( +// uint256 profileId, +// string calldata name, +// string calldata symbol, +// string calldata essenceURI, +// address essenceMw, +// bool transferable, +// bool deployAtReg +// ) public returns (uint256) { +// DataTypes.RegisterEssenceParams memory params; + +// params.profileId = profileId; +// params.name = name; +// params.symbol = symbol; +// params.essenceTokenURI = essenceURI; +// params.essenceMw = essenceMw; +// params.transferable = transferable; +// params.deployAtRegister = deployAtReg; + +// return _ccRegEssence(params); +// } + +// function ccCollectEss( +// address who, +// uint256 profileId, +// uint256 essenceId +// ) public { +// DataTypes.CollectParams memory params; +// params.collector = who; +// params.profileId = profileId; +// params.essenceId = essenceId; + +// _ccCollectEss(params); +// } + +// function ccSetMetadata(uint256 profileId, string calldata metadata) +// public +// { +// _ccSetMetadata(profileId, metadata); +// } + +// function ccSetSubData( +// uint256 profileId, +// string calldata uri, +// address mw, +// bytes calldata mwData +// ) public { +// _ccSetSubData(profileId, uri, mw, mwData); +// } + +// function ccSetEssData( +// uint256 profileId, +// uint256 essId, +// string calldata uri, +// address mw, +// bytes calldata mwData +// ) public { +// _ccSetEssData(profileId, essId, uri, mw, mwData); +// } + +// function ccSetPrimary(uint256 profileId) public { +// _ccSetPrimary(profileId); +// } + +// function _ccRegEssence(DataTypes.RegisterEssenceParams memory params) +// internal +// returns (uint256) +// { +// uint id = ccProfileNFT.registerEssence(params, ''); +// return id; +// } + +// function _ccCollectEss(DataTypes.CollectParams memory params) internal { +// ccProfileNFT.collect(params, "", ""); +// } + +// function _ccSetMetadata(uint256 profileId, string calldata metadata) +// internal +// { +// ccProfileNFT.setMetadata(profileId, metadata); +// } + +// function _ccSetSubData( +// uint256 profileId, +// string calldata uri, +// address mw, +// bytes calldata mwData +// ) internal { +// ccProfileNFT.setSubscribeData(profileId, uri, mw, mwData); +// } + +// function _ccSetEssData( +// uint256 profileId, +// uint256 essId, +// string calldata uri, +// address mw, +// bytes calldata mwData +// ) internal { +// ccProfileNFT.setEssenceData(profileId, essId, uri, mw, mwData); +// } + +// function _ccSetPrimary(uint256 profileId) internal { +// ccProfileNFT.setPrimaryProfile(profileId); +// } + +// /** +// * @notice Check if the profile issued EssenceNFT is collected by me. +// * +// * @param profileId The profile id. +// * @param essenceId The essence id. +// * @param me The address to check. +// * @param _namespace The address of the ccProfileNFT +// */ +// function isCollectedByMe( +// uint256 profileId, +// uint256 essenceId, +// address me, +// address _namespace +// ) external view returns (bool) { +// address essNFTAddr = IProfileNFT(_namespace).getEssenceNFT( +// profileId, +// essenceId +// ); +// if (essNFTAddr == address(0)) { +// return false; +// } + +// return IERC721(essNFTAddr).balanceOf(me) > 0; +// } + +// /** +// * @notice Check if the profile is subscribed by me. +// * +// * @param profileId The profile id. +// * @param me The address to check. +// * @param _namespace The address of the ProfileNFT +// */ +// function isSubscribedByMe(uint256 profileId, address me, address _namespace) +// external +// view +// returns (bool) +// { +// address subNFTAddr = IProfileNFT(_namespace).getSubscribeNFT(profileId); +// if (subNFTAddr == address(0)) { +// return false; +// } +// return IERC721(subNFTAddr).balanceOf(me) > 0; +// } + +// function createGhostsCC(string memory handle, string[] memory hashes, address op) internal returns(uint) { +// // DataTypes.CreateProfileParams memory params; +// // params.to = msg.sender; +// // params.handle = handle; +// // params.avatar = hashes[0]; +// // params.metadata = hashes[1]; +// // params.operator = op; +// // ccProfileNFT.createProfile(params, '',''); +// // uint ccID = ccProfileNFT.getProfileIdByHandle(handle); +// IGhostsData.UserFeats storage u = getUser[ghostsAddr]; +// string[] memory hashes = new string[](2); +// hashes[0] = 'lol'; +// hashes[1] = 'sdasd'; +// uint256[] memory empty; +// uint256 ccID = IGhosts(ghostsAddr).createUser(handle, hashes); +// if(u.userAddress == address(0)){ +// IGhostsData.UserFeats memory newUser = IGhostsData.UserFeats({ +// userAddress: ghostsAddr, +// ccID: ccID, +// followCount: 0, +// followerCount: 0, +// commentCount: 0, +// consumeCount: 0, +// createCount: 0, +// followers: empty, +// following: empty +// }); +// getUser[ghostsAddr] = newUser; +// ghostsCCID = ccID; +// delete newUser; +// return ccID; +// } +// } +// }