diff --git a/blockchain/contracts/EMTMarketplace.sol b/blockchain/contracts/EMTMarketplace.sol index 25b8a9f..aec04c1 100644 --- a/blockchain/contracts/EMTMarketplace.sol +++ b/blockchain/contracts/EMTMarketplace.sol @@ -10,10 +10,10 @@ contract EMTMarketplace is Pausable, AccessControl { bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE"); // Event Definitions - event ContentUpVoted(uint256 indexed, uint256); - event ContentDownVoted(uint256 indexed, uint256); + event ContentUpVoted(bytes32 indexed, uint256); + event ContentDownVoted(bytes32 indexed, uint256); event MentClaimed(address indexed, uint256); - event ContentAdded(address indexed, uint256); + event ContentAdded(address indexed, bytes32); // Public Data Definitions address public mentTokenAddress; @@ -25,16 +25,21 @@ contract EMTMarketplace is Pausable, AccessControl { bool downVoted; uint256 lastVotedAt; } - struct ContentVote { - address creator; + struct CreatorVote { uint256 upVotes; uint256 downVotes; uint256 lastClaimedUpVotes; uint256 lastClaimedDownVotes; uint256 lastClaimedAt; + } + struct ContentVote { + address creator; + uint256 upVotes; + uint256 downVotes; mapping(address => MemberVote) memberVotes; } - mapping(uint256 => ContentVote) _contentVotes; + mapping(bytes32 => ContentVote) _contentVotes; + mapping(address => CreatorVote) _creatorVotes; // Constructor constructor(address defaultAdmin) { @@ -71,7 +76,7 @@ contract EMTMarketplace is Pausable, AccessControl { // For a particular content with _id it return 3 bools for upvotes, downvotes and net votes function contentVotes( - uint256 _id + bytes32 _id ) public view returns (uint256, uint256, int256) { return ( _contentVotes[_id].upVotes, @@ -83,7 +88,7 @@ contract EMTMarketplace is Pausable, AccessControl { // For a particular content with _id it returns bool for both if _member has upvoted or downvoted the content function memberVotes( - uint256 _id, + bytes32 _id, address _member ) public view returns (bool, bool) { return ( @@ -92,7 +97,7 @@ contract EMTMarketplace is Pausable, AccessControl { ); } - function addContent(uint256 _id) public { + function addContent(bytes32 _id) public { // Retrieve Content Vote ContentVote storage _contentVote = _contentVotes[_id]; // Check if no creator has been set already @@ -103,7 +108,7 @@ contract EMTMarketplace is Pausable, AccessControl { emit ContentAdded(msg.sender, _id); } - function upVoteContent(uint256 _id) public whenNotPaused { + function upVoteContent(bytes32 _id) public whenNotPaused { // Retrieve Content Vote ContentVote storage _contentVote = _contentVotes[_id]; // Ensure Content has creator @@ -116,22 +121,26 @@ contract EMTMarketplace is Pausable, AccessControl { !_contentVote.memberVotes[msg.sender].upVoted, "Member has already up voted!" ); - // Check If Claim Prevents Member from voting + // Retrieve Creator Vote + CreatorVote storage _creatorVote = _creatorVotes[_contentVote.creator]; + // Check If Claim Rules Prevents Member from voting require( _contentVote.memberVotes[msg.sender].lastVotedAt == 0 || - _contentVote.lastClaimedAt == 0 || - (_contentVote.lastClaimedAt < + _creatorVote.lastClaimedAt == 0 || + (_creatorVote.lastClaimedAt < _contentVote.memberVotes[msg.sender].lastVotedAt), "Cannot Vote Again Due to Claim Rules!" ); // Reverse if member has already downvoted if (_contentVote.memberVotes[msg.sender].downVoted) { - // Decrement Content Down Votes + // Decrement Creator & Content Down Votes + _creatorVote.downVotes--; _contentVote.downVotes--; // Update Member Down Voted _contentVote.memberVotes[msg.sender].downVoted = false; } - // Increment Content Up Votes + // Increment Creator & Content Up Votes + _creatorVote.upVotes++; _contentVote.upVotes++; // Update Member Up Voted _contentVote.memberVotes[msg.sender].upVoted = true; @@ -141,7 +150,7 @@ contract EMTMarketplace is Pausable, AccessControl { emit ContentUpVoted(_id, _contentVote.upVotes); } - function downVoteContent(uint256 _id) public whenNotPaused { + function downVoteContent(bytes32 _id) public whenNotPaused { // Retrieve Content Vote ContentVote storage _contentVote = _contentVotes[_id]; // Ensure Content has creator @@ -154,22 +163,26 @@ contract EMTMarketplace is Pausable, AccessControl { !_contentVote.memberVotes[msg.sender].downVoted, "Member has already down voted!" ); - // Check If Claim Prevents Member from voting + // Retrieve Creator Vote + CreatorVote storage _creatorVote = _creatorVotes[_contentVote.creator]; + // Check If Claim Rules Prevents Member from voting require( _contentVote.memberVotes[msg.sender].lastVotedAt == 0 || - _contentVote.lastClaimedAt == 0 || - (_contentVote.lastClaimedAt < + _creatorVote.lastClaimedAt == 0 || + (_creatorVote.lastClaimedAt < _contentVote.memberVotes[msg.sender].lastVotedAt), "Cannot Vote Again Due to Claim Rules!" ); // Reverse if member has already upvoted if (_contentVote.memberVotes[msg.sender].upVoted) { - // Decrement Content Up Votes + // Decrement Creator & Content Up Votes + _creatorVote.upVotes--; _contentVote.upVotes--; // Update Member Up Voted _contentVote.memberVotes[msg.sender].upVoted = false; } - // Increment Content Down Votes + // Increment Creator & Content Down Votes + _creatorVote.downVotes++; _contentVote.downVotes++; // Update Member Down Voted _contentVote.memberVotes[msg.sender].downVoted = true; @@ -179,29 +192,26 @@ contract EMTMarketplace is Pausable, AccessControl { emit ContentDownVoted(_id, _contentVote.downVotes); } - function claimMent(uint256 _id) public whenPaused { + function claimMent() public whenPaused { // Ensure mentTokenAddress is not the zero address require(mentTokenAddress != address(0), "Claiming is disabled!"); - // Retrieve Content Vote - ContentVote storage _contentVote = _contentVotes[_id]; + // Retrieve Creator Vote + CreatorVote storage _creatorVote = _creatorVotes[msg.sender]; // Compute claimable MENT - int256 _claimableMent = ((int256(_contentVote.upVotes) - - int256(_contentVote.lastClaimedUpVotes)) * int256(upVoteWeight)) - - ((int256(_contentVote.downVotes) - - int256(_contentVote.lastClaimedDownVotes)) * + int256 _claimableMent = ((int256(_creatorVote.upVotes) - + int256(_creatorVote.lastClaimedUpVotes)) * int256(upVoteWeight)) - + ((int256(_creatorVote.downVotes) - + int256(_creatorVote.lastClaimedDownVotes)) * int256(downVoteWeight)); // Check if Content Vote has votes to claim require(_claimableMent > 0, "No MENT to claim!"); // Mint MENT Tokens for Creator - MentorToken(mentTokenAddress).mint( - _contentVote.creator, - uint256(_claimableMent) - ); + MentorToken(mentTokenAddress).mint(msg.sender, uint256(_claimableMent)); // Update Content Last Claimed, UpVotes & DownVotes - _contentVote.lastClaimedAt = block.number; - _contentVote.lastClaimedUpVotes = _contentVote.upVotes; - _contentVote.lastClaimedDownVotes = _contentVote.downVotes; + _creatorVote.lastClaimedAt = block.number; + _creatorVote.lastClaimedUpVotes = _creatorVote.upVotes; + _creatorVote.lastClaimedDownVotes = _creatorVote.downVotes; // Emit Event - emit MentClaimed(_contentVote.creator, uint256(_claimableMent)); + emit MentClaimed(msg.sender, uint256(_claimableMent)); } } diff --git a/blockchain/hardhat.config.ts b/blockchain/hardhat.config.ts index dcc478b..f9ef889 100644 --- a/blockchain/hardhat.config.ts +++ b/blockchain/hardhat.config.ts @@ -25,7 +25,12 @@ const config: HardhatUserConfig = { gasReporter: { enabled: true, gasPriceApi: 'https://api.etherscan.io/api?module=proxy&action=eth_gasPrice', - coinmarketcap: process.env.COINMARKETCAP_API + coinmarketcap: process.env.COINMARKETCAP_API_KEY + }, + etherscan: { + apiKey: { + polygonMumbai: (process.env.POLYGONSCAN_API_KEY as string) + } } }; diff --git a/blockchain/test/EMTMarketplace.ts b/blockchain/test/EMTMarketplace.ts index 9f8d607..53a0ecc 100644 --- a/blockchain/test/EMTMarketplace.ts +++ b/blockchain/test/EMTMarketplace.ts @@ -10,6 +10,7 @@ describe("EMTMarketplace", function () { async function deployEMTMarketplaceFixture() { // Contracts are deployed using the first signer/account by default const [owner, mentor, member] = await ethers.getSigners(); + const contentId = ethers.encodeBytes32String("LoOb7y8KHesW98X6MVJ5"); const EMTMarketplace = await ethers.getContractFactory("EMTMarketplace"); const emtMarketplace = await EMTMarketplace.deploy(owner.address); @@ -17,7 +18,7 @@ describe("EMTMarketplace", function () { const MentorToken = await ethers.getContractFactory("MentorToken"); const mentorToken = await MentorToken.deploy(owner.address, emtMarketplace.target); - return { emtMarketplace, mentorToken, owner, mentor, member }; + return { emtMarketplace, mentorToken, owner, mentor, member, contentId }; } // Test Goes Below @@ -56,160 +57,160 @@ describe("EMTMarketplace", function () { describe("Adding Content", function () { it("should allow member to add content", async function () { - const { emtMarketplace, mentor } = await loadFixture(deployEMTMarketplaceFixture); + const { emtMarketplace, mentor, contentId } = await loadFixture(deployEMTMarketplaceFixture); - await expect(emtMarketplace.connect(mentor).addContent(1)).to.be.emit(emtMarketplace, "ContentAdded"); + await expect(emtMarketplace.connect(mentor).addContent(contentId)).to.be.emit(emtMarketplace, "ContentAdded"); }); it("should not allow member to add same content id again", async function () { - const { emtMarketplace, mentor } = await loadFixture(deployEMTMarketplaceFixture); + const { emtMarketplace, mentor, contentId } = await loadFixture(deployEMTMarketplaceFixture); - await expect(emtMarketplace.connect(mentor).addContent(1)).to.be.emit(emtMarketplace, "ContentAdded"); - await expect(emtMarketplace.connect(mentor).addContent(1)).to.be.revertedWith("Creator already exists!"); + await expect(emtMarketplace.connect(mentor).addContent(contentId)).to.be.emit(emtMarketplace, "ContentAdded"); + await expect(emtMarketplace.connect(mentor).addContent(contentId)).to.be.revertedWith("Creator already exists!"); }); }); describe("Content Voting", function () { it("should allow a member to upvote content", async function () { - const { emtMarketplace, mentor, member } = await loadFixture(deployEMTMarketplaceFixture); + const { emtMarketplace, mentor, member, contentId } = await loadFixture(deployEMTMarketplaceFixture); - await emtMarketplace.connect(mentor).addContent(1); - await emtMarketplace.connect(member).upVoteContent(1); - expect((await emtMarketplace.connect(member).contentVotes(1))[2]).to.equal(1); - const memberVotes = await emtMarketplace.connect(member).memberVotes(1, member.address); + await emtMarketplace.connect(mentor).addContent(contentId); + await emtMarketplace.connect(member).upVoteContent(contentId); + expect((await emtMarketplace.connect(member).contentVotes(contentId))[2]).to.equal(1); + const memberVotes = await emtMarketplace.connect(member).memberVotes(contentId, member.address); expect(memberVotes[0]).to.equal(true); expect(memberVotes[1]).to.equal(false); }); it("should allow a member to upvote downvoted content", async function () { - const { emtMarketplace, mentor, member } = await loadFixture(deployEMTMarketplaceFixture); + const { emtMarketplace, mentor, member, contentId } = await loadFixture(deployEMTMarketplaceFixture); - await emtMarketplace.connect(mentor).addContent(1); - await emtMarketplace.connect(member).downVoteContent(1); - await emtMarketplace.connect(member).upVoteContent(1); - expect((await emtMarketplace.connect(member).contentVotes(1))[2]).to.equal(1); - const memberVotes = await emtMarketplace.connect(member).memberVotes(1, member.address); + await emtMarketplace.connect(mentor).addContent(contentId); + await emtMarketplace.connect(member).downVoteContent(contentId); + await emtMarketplace.connect(member).upVoteContent(contentId); + expect((await emtMarketplace.connect(member).contentVotes(contentId))[2]).to.equal(1); + const memberVotes = await emtMarketplace.connect(member).memberVotes(contentId, member.address); expect(memberVotes[0]).to.equal(true); expect(memberVotes[1]).to.equal(false); }); it("should fail to upvote content with creator address set to address(0)", async function () { - const { emtMarketplace, member } = await loadFixture(deployEMTMarketplaceFixture); + const { emtMarketplace, member, contentId } = await loadFixture(deployEMTMarketplaceFixture); - await expect(emtMarketplace.connect(member).upVoteContent(1)).to.be.revertedWith("Voting not allowed for content without creator!"); + await expect(emtMarketplace.connect(member).upVoteContent(contentId)).to.be.revertedWith("Voting not allowed for content without creator!"); }); it("should fail to upvote content when the member has already upvoted", async function () { - const { emtMarketplace, member, mentor } = await loadFixture(deployEMTMarketplaceFixture); + const { emtMarketplace, member, mentor, contentId } = await loadFixture(deployEMTMarketplaceFixture); - await emtMarketplace.connect(mentor).addContent(1); - await emtMarketplace.connect(member).upVoteContent(1); - await expect(emtMarketplace.connect(member).upVoteContent(1)).to.be.revertedWith("Member has already up voted!"); + await emtMarketplace.connect(mentor).addContent(contentId); + await emtMarketplace.connect(member).upVoteContent(contentId); + await expect(emtMarketplace.connect(member).upVoteContent(contentId)).to.be.revertedWith("Member has already up voted!"); }); it("should fail to upvote content when claim rules are not met", async function () { - const { emtMarketplace, mentorToken, owner, member, mentor } = await loadFixture(deployEMTMarketplaceFixture); + const { emtMarketplace, mentorToken, owner, member, mentor, contentId } = await loadFixture(deployEMTMarketplaceFixture); await emtMarketplace.connect(owner).setMentToken(mentorToken.target); - await emtMarketplace.connect(mentor).addContent(1); + await emtMarketplace.connect(mentor).addContent(contentId); - await emtMarketplace.connect(member).downVoteContent(1); - await emtMarketplace.connect(mentor).upVoteContent(1); - await emtMarketplace.connect(owner).upVoteContent(1); + await emtMarketplace.connect(member).downVoteContent(contentId); + await emtMarketplace.connect(mentor).upVoteContent(contentId); + await emtMarketplace.connect(owner).upVoteContent(contentId); await emtMarketplace.connect(owner).pause(); - await emtMarketplace.connect(mentor).claimMent(1); + await emtMarketplace.connect(mentor).claimMent(); await emtMarketplace.connect(owner).unpause(); - await expect(emtMarketplace.connect(member).upVoteContent(1)).to.be.revertedWith("Cannot Vote Again Due to Claim Rules!"); + await expect(emtMarketplace.connect(member).upVoteContent(contentId)).to.be.revertedWith("Cannot Vote Again Due to Claim Rules!"); }); it("should fail to upvote content if contract is paused", async function () { - const { emtMarketplace, owner, mentor, member } = await loadFixture(deployEMTMarketplaceFixture); + const { emtMarketplace, owner, mentor, member, contentId } = await loadFixture(deployEMTMarketplaceFixture); await emtMarketplace.connect(owner).pause(); - await emtMarketplace.connect(mentor).addContent(1); - await expect(emtMarketplace.connect(member).upVoteContent(1)).to.be.revertedWithCustomError(emtMarketplace, "EnforcedPause"); + await emtMarketplace.connect(mentor).addContent(contentId); + await expect(emtMarketplace.connect(member).upVoteContent(contentId)).to.be.revertedWithCustomError(emtMarketplace, "EnforcedPause"); }); it("should allow a member to downvote content", async function () { - const { emtMarketplace, mentor, member } = await loadFixture(deployEMTMarketplaceFixture); + const { emtMarketplace, mentor, member, contentId } = await loadFixture(deployEMTMarketplaceFixture); - await emtMarketplace.connect(mentor).addContent(1); - await emtMarketplace.connect(member).downVoteContent(1); - expect((await emtMarketplace.connect(member).contentVotes(1))[2]).to.equal(-1); - const memberVotes = await emtMarketplace.connect(member).memberVotes(1, member.address); + await emtMarketplace.connect(mentor).addContent(contentId); + await emtMarketplace.connect(member).downVoteContent(contentId); + expect((await emtMarketplace.connect(member).contentVotes(contentId))[2]).to.equal(-1); + const memberVotes = await emtMarketplace.connect(member).memberVotes(contentId, member.address); expect(memberVotes[0]).to.equal(false); expect(memberVotes[1]).to.equal(true); }); it("should allow a member to downvote upvoted content", async function () { - const { emtMarketplace, mentor, member } = await loadFixture(deployEMTMarketplaceFixture); + const { emtMarketplace, mentor, member, contentId } = await loadFixture(deployEMTMarketplaceFixture); - await emtMarketplace.connect(mentor).addContent(1); - await emtMarketplace.connect(member).upVoteContent(1); - await emtMarketplace.connect(member).downVoteContent(1); - expect((await emtMarketplace.connect(member).contentVotes(1))[2]).to.equal(-1); - const memberVotes = await emtMarketplace.connect(member).memberVotes(1, member.address); + await emtMarketplace.connect(mentor).addContent(contentId); + await emtMarketplace.connect(member).upVoteContent(contentId); + await emtMarketplace.connect(member).downVoteContent(contentId); + expect((await emtMarketplace.connect(member).contentVotes(contentId))[2]).to.equal(-1); + const memberVotes = await emtMarketplace.connect(member).memberVotes(contentId, member.address); expect(memberVotes[0]).to.equal(false); expect(memberVotes[1]).to.equal(true); }); it("should fail to downvote content with creator address set to address(0)", async function () { - const { emtMarketplace, member } = await loadFixture(deployEMTMarketplaceFixture); + const { emtMarketplace, member, contentId } = await loadFixture(deployEMTMarketplaceFixture); - await expect(emtMarketplace.connect(member).downVoteContent(1)).to.be.revertedWith("Voting not allowed for content without creator!"); + await expect(emtMarketplace.connect(member).downVoteContent(contentId)).to.be.revertedWith("Voting not allowed for content without creator!"); }); it("should fail to downvote content when the member has already downvoted", async function () { - const { emtMarketplace, member, mentor } = await loadFixture(deployEMTMarketplaceFixture); + const { emtMarketplace, member, mentor, contentId } = await loadFixture(deployEMTMarketplaceFixture); - await emtMarketplace.connect(mentor).addContent(1); - await emtMarketplace.connect(member).downVoteContent(1); - await expect(emtMarketplace.connect(member).downVoteContent(1)).to.be.revertedWith("Member has already down voted!"); + await emtMarketplace.connect(mentor).addContent(contentId); + await emtMarketplace.connect(member).downVoteContent(contentId); + await expect(emtMarketplace.connect(member).downVoteContent(contentId)).to.be.revertedWith("Member has already down voted!"); }); it("should fail to downvote content when claim rules are not met", async function () { - const { emtMarketplace, mentorToken, owner, member, mentor } = await loadFixture(deployEMTMarketplaceFixture); + const { emtMarketplace, mentorToken, owner, member, mentor, contentId } = await loadFixture(deployEMTMarketplaceFixture); await emtMarketplace.connect(owner).setMentToken(mentorToken.target); - await emtMarketplace.connect(mentor).addContent(1); + await emtMarketplace.connect(mentor).addContent(contentId); - await emtMarketplace.connect(member).upVoteContent(1); - await emtMarketplace.connect(mentor).upVoteContent(1); - await emtMarketplace.connect(owner).upVoteContent(1); + await emtMarketplace.connect(member).upVoteContent(contentId); + await emtMarketplace.connect(mentor).upVoteContent(contentId); + await emtMarketplace.connect(owner).upVoteContent(contentId); await emtMarketplace.connect(owner).pause(); - await emtMarketplace.connect(mentor).claimMent(1); + await emtMarketplace.connect(mentor).claimMent(); await emtMarketplace.connect(owner).unpause(); - await expect(emtMarketplace.connect(member).downVoteContent(1)).to.be.revertedWith("Cannot Vote Again Due to Claim Rules!"); + await expect(emtMarketplace.connect(member).downVoteContent(contentId)).to.be.revertedWith("Cannot Vote Again Due to Claim Rules!"); }); it("should fail to downvote content if contract is paused", async function () { - const { emtMarketplace, owner, mentor, member } = await loadFixture(deployEMTMarketplaceFixture); + const { emtMarketplace, owner, mentor, member, contentId } = await loadFixture(deployEMTMarketplaceFixture); await emtMarketplace.connect(owner).pause(); - await emtMarketplace.connect(mentor).addContent(1); - await expect(emtMarketplace.connect(member).downVoteContent(1)).to.be.revertedWithCustomError(emtMarketplace, "EnforcedPause"); + await emtMarketplace.connect(mentor).addContent(contentId); + await expect(emtMarketplace.connect(member).downVoteContent(contentId)).to.be.revertedWithCustomError(emtMarketplace, "EnforcedPause"); }); }); describe("Ment Claiming", function () { it("should successfully claim ment", async function () { - const { emtMarketplace, mentorToken, owner, mentor, member } = await loadFixture(deployEMTMarketplaceFixture); + const { emtMarketplace, mentorToken, owner, mentor, member, contentId } = await loadFixture(deployEMTMarketplaceFixture); await emtMarketplace.connect(owner).setMentToken(mentorToken.target); - await emtMarketplace.connect(mentor).addContent(1); - await emtMarketplace.connect(member).upVoteContent(1); + await emtMarketplace.connect(mentor).addContent(contentId); + await emtMarketplace.connect(member).upVoteContent(contentId); await emtMarketplace.connect(owner).pause(); - await expect(emtMarketplace.connect(mentor).claimMent(1)).to.be.emit(emtMarketplace, "MentClaimed"); + await expect(emtMarketplace.connect(mentor).claimMent()).to.be.emit(emtMarketplace, "MentClaimed"); }); it("should not claim ment if ment token address is zero", async function () { const { emtMarketplace, owner, mentor } = await loadFixture(deployEMTMarketplaceFixture); await emtMarketplace.connect(owner).pause(); - await expect(emtMarketplace.connect(mentor).claimMent(1)).to.be.revertedWith("Claiming is disabled!"); + await expect(emtMarketplace.connect(mentor).claimMent()).to.be.revertedWith("Claiming is disabled!"); }); it("should not claim ment if claimable ment is zero", async function () { @@ -217,13 +218,13 @@ describe("EMTMarketplace", function () { await emtMarketplace.connect(owner).setMentToken(mentorToken.target); await emtMarketplace.connect(owner).pause(); - await expect(emtMarketplace.connect(mentor).claimMent(1)).to.be.revertedWith("No MENT to claim!"); + await expect(emtMarketplace.connect(mentor).claimMent()).to.be.revertedWith("No MENT to claim!"); }); it("should not claim ment if contract is not paused", async function () { const { emtMarketplace, mentor } = await loadFixture(deployEMTMarketplaceFixture); - await expect(emtMarketplace.connect(mentor).claimMent(1)).to.be.revertedWithCustomError(emtMarketplace, "ExpectedPause"); + await expect(emtMarketplace.connect(mentor).claimMent()).to.be.revertedWithCustomError(emtMarketplace, "ExpectedPause"); }); });