From 34e38c75c1d195d100522d1242a136bd6bf0e30f Mon Sep 17 00:00:00 2001 From: HamdiBenK Date: Mon, 21 Aug 2023 16:03:49 +0100 Subject: [PATCH 1/6] update smart contract validateProm/getGains and refactor differet functions --- contracts/campaign.sol | 218 ++++++++++++++++++++++++++++------------- 1 file changed, 151 insertions(+), 67 deletions(-) diff --git a/contracts/campaign.sol b/contracts/campaign.sol index 789c304f..4d5d44d1 100644 --- a/contracts/campaign.sol +++ b/contracts/campaign.sol @@ -1,5 +1,9 @@ +/** + *Submitted for verification at BscScan.com on 2023-08-21 +*/ + //SPDX-License-Identifier: Unlicense -pragma solidity 0.8.17; +pragma solidity 0.8.19; abstract contract owned { address payable public owner; @@ -30,8 +34,8 @@ abstract contract owned { constructor() { owner = payable(msg.sender); - treasory = 0x75e6ef3113266F7116B219f05Caede20889ddDf3; - oracle = 0xd99884038A064466961bB0CE6e32646abD11bA9B; + treasory = 0xCA6C8E85804d7dC2CA7EcA018de77Aa2Ab8bE52C; + oracle = 0x5F8f3Efd4118136626eE5A240139ECa7cA22C72A; } function transferOwnership(address payable newOwner) public notPaused onlyOwner { @@ -129,6 +133,7 @@ contract campaign is oracleClient { Fund funds; mapping(uint8 => cpRatio) ratios; bountyUnit[] bounties; + uint64 participationLimit; // Optional property to limit number of participation by influencer default value is 0 } struct Fund { @@ -165,18 +170,26 @@ contract campaign is oracleClient { mapping(bytes32 => promElement) public proms; mapping(bytes32 => Result) public results; mapping(bytes32 => bool) public isAlreadyUsed; + mapping(bytes32 => mapping(address => uint64)) public influencerProms; + event CampaignCreated( bytes32 indexed id, uint64 startDate, uint64 endDate, - string dataUrl + string dataUrl, + uint64 limit ); event CampaignFundsSpent(bytes32 indexed id); event CampaignApplied(bytes32 indexed id, bytes32 indexed prom); event PromAccepted(bytes32 indexed id); event PromPayed(bytes32 indexed id, uint256 amount); event CampaignFunded(bytes32 indexed id, uint256 amount); + + modifier onlyInfluencer(bytes32 idProm) { + require(proms[idProm].influencer == msg.sender, "Only influencer can call this function"); + _; +} function priceRatioCampaign( bytes32 idCampaign, @@ -198,6 +211,8 @@ contract campaign is oracleClient { ); } + + function fundCampaign( bytes32 idCampaign, address token, @@ -218,7 +233,7 @@ contract campaign is oracleClient { uint256 added_amount; uint256 trisory_amount; - if (token == 0x448bee2d93be708b54ee6353a7cc35c4933f1156) { + if (token == 0x6fAc729f346A46fC0093126f237b4A520c40eb89) { added_amount = (amount * 95) / 100; trisory_amount = amount - added_amount; } else { @@ -239,7 +254,8 @@ contract campaign is oracleClient { uint64 endDate, uint256[] memory ratios, address token, - uint256 amount + uint256 amount, + uint64 limit ) public notPaused returns (bytes32 idCampaign) { require(endDate > block.timestamp, "end date too early"); require(endDate > startDate, "end date early than start"); @@ -258,11 +274,12 @@ contract campaign is oracleClient { c.dataUrl = dataUrl; c.startDate = startDate; c.endDate = endDate; + c.participationLimit = limit; c.nbProms = 0; c.nbValidProms = 0; c.funds = Fund(address(0), 0); //campaigns[campaignId] = Campaign(msg.sender,dataUrl,startDate,endDate,0,0,Fund(address(0),0)); - emit CampaignCreated(campaignId, startDate, endDate, dataUrl); + emit CampaignCreated(campaignId, startDate, endDate, dataUrl,limit); for (uint8 i = 0; i < ratios.length; i = i + 4) { priceRatioCampaign( @@ -285,7 +302,8 @@ contract campaign is oracleClient { uint64 endDate, uint256[] memory bounties, address token, - uint256 amount + uint256 amount, + uint64 limit ) public notPaused returns (bytes32 idCampaign) { require(endDate > block.timestamp, "end date too early"); require(endDate > startDate, "end date early than start"); @@ -304,6 +322,7 @@ contract campaign is oracleClient { c.dataUrl = dataUrl; c.startDate = startDate; c.endDate = endDate; + c.participationLimit = limit; c.nbProms = 0; c.nbValidProms = 0; c.funds = Fund(address(0), 0); @@ -318,7 +337,7 @@ contract campaign is oracleClient { ); } - emit CampaignCreated(campaignId, startDate, endDate, dataUrl); + emit CampaignCreated(campaignId, startDate, endDate, dataUrl, limit); fundCampaign(campaignId, token, amount); return campaignId; @@ -336,6 +355,10 @@ contract campaign is oracleClient { address signer = ecrecover(_hashedMessage, _v, _r, _s); return signer; } + + function incrementPromotionCount(bytes32 idCampaign, address ownerLink) internal { + influencerProms[idCampaign][ownerLink]++; + } function validateProm( bytes32 idCampaign, @@ -363,7 +386,14 @@ contract campaign is oracleClient { block.timestamp ) ); + + // Ensure the promotion link has not been used before require(!isAlreadyUsed[prom], "link already sent"); + + // Get the current number of promotions used by the influencer for this campaign + uint64 influencerPromCount = influencerProms[idCampaign][ownerLink]; + require(influencerPromCount < cmp.participationLimit, "Participation limit exceeded"); + promElement storage p = proms[prom]; p.influencer = ownerLink; p.idCampaign = idCampaign; @@ -377,28 +407,36 @@ contract campaign is oracleClient { p.prevResult = 0; p.validate = block.timestamp; cmp.nbValidProms++; + + // Increment the influencer's promotion count for this campaign + incrementPromotionCount(idCampaign, ownerLink); emit PromAccepted(prom); } function updateCampaignStats(bytes32 idCampaign) public notPaused { - for (uint64 i = 0; i < campaigns[idCampaign].nbProms; i++) { - bytes32 idProm = campaigns[idCampaign].proms[i]; - if (proms[idProm].isAccepted) { + Campaign storage cmp = campaigns[idCampaign]; + + for (uint64 i = 0; i < cmp.nbProms; i++) { + bytes32 idProm = cmp.proms[i]; + // Retrieve the prom element associated with the given idProm from the proms mapping + promElement storage prom = proms[idProm]; + + if (prom.isAccepted) { bytes32 idRequest = keccak256( abi.encodePacked( - proms[idProm].typeSN, - proms[idProm].idPost, - proms[idProm].idUser, + prom.typeSN, + prom.idPost, + prom.idUser, block.timestamp ) ); results[idRequest] = Result(idProm, 0, 0, 0); - proms[idProm].results[proms[idProm].nbResults++] = idRequest; + prom.results[prom.nbResults++] = idRequest; ask( - proms[idProm].typeSN, - proms[idProm].idPost, - proms[idProm].idUser, + prom.typeSN, + prom.idPost, + prom.idUser, idRequest ); } @@ -410,32 +448,35 @@ contract campaign is oracleClient { notPaused returns (bytes32 requestId) { - require(proms[idProm].isAccepted, "link not validated"); + promElement storage prom = proms[idProm]; // Store the prom element for efficient access + + require(prom.isAccepted, "link not validated"); bytes32 idRequest = keccak256( abi.encodePacked( - proms[idProm].typeSN, - proms[idProm].idPost, - proms[idProm].idUser, + prom.typeSN, + prom.idPost, + prom.idUser, block.timestamp ) ); results[idRequest] = Result(idProm, 0, 0, 0); - proms[idProm].results[proms[idProm].nbResults++] = idRequest; + prom.results[prom.nbResults++] = idRequest; ask( - proms[idProm].typeSN, - proms[idProm].idPost, - proms[idProm].idUser, + prom.typeSN, + prom.idPost, + prom.idUser, idRequest ); return idRequest; } function updateBounty(bytes32 idProm) public notPaused { - require(proms[idProm].isAccepted, "link not validated"); + promElement storage prom = proms[idProm]; // Store the prom element for efficient access + require(prom.isAccepted, "link not validated"); askBounty( - proms[idProm].typeSN, - proms[idProm].idPost, - proms[idProm].idUser, + prom.typeSN, + prom.idPost, + prom.idUser, idProm ); } @@ -460,40 +501,52 @@ contract campaign is oracleClient { o.askBounty(typeSN, idPost, idUser, idProm); } +/** + * @dev Updates the bounty payment status for a specific campaign promotion (idProm). + * The function calculates the gain for the influencer based on the number of subscribers (nbAbos) and campaign bounties. + * If the gain is sufficient, the payment is processed, otherwise, the funds are added to the promotion's balance. + * @param idProm The unique identifier of the campaign promotion. + * @param nbAbos The number of subscribers for the promotion. + * @return ok True if the operation is successful. + */ function updateBounty(bytes32 idProm, uint256 nbAbos) external notPaused returns (bool ok) { require(msg.sender == oracle, "oracle mismatch"); - + + // Store the prom element for efficient access promElement storage prom = proms[idProm]; require(!prom.isPayed, "link already paid"); prom.isPayed = true; - prom.funds.token = campaigns[prom.idCampaign].funds.token; + // Store the campaign element for efficient access + Campaign storage cmp = campaigns[prom.idCampaign]; + + prom.funds.token = cmp.funds.token; uint256 gain = 0; for ( uint256 i = 0; - i < campaigns[prom.idCampaign].bounties.length; + i < cmp.bounties.length; i++ ) { if ( - nbAbos >= campaigns[prom.idCampaign].bounties[i].minRange && - nbAbos < campaigns[prom.idCampaign].bounties[i].maxRange && - prom.typeSN == campaigns[prom.idCampaign].bounties[i].typeSN + nbAbos >= cmp.bounties[i].minRange && + nbAbos < cmp.bounties[i].maxRange && + prom.typeSN == cmp.bounties[i].typeSN ) { - gain = campaigns[prom.idCampaign].bounties[i].amount; + gain = cmp.bounties[i].amount; } } - if (campaigns[prom.idCampaign].funds.amount <= gain) { - //campaigns[prom.idCampaign].endDate = uint64(block.timestamp); - prom.funds.amount += campaigns[prom.idCampaign].funds.amount; - campaigns[prom.idCampaign].funds.amount = 0; + if (cmp.funds.amount <= gain) { + //cmp.endDate = uint64(block.timestamp); + prom.funds.amount += cmp.funds.amount; + cmp.funds.amount = 0; emit CampaignFundsSpent(prom.idCampaign); return true; } - campaigns[prom.idCampaign].funds.amount -= gain; + cmp.funds.amount -= gain; prom.funds.amount += gain; return true; } @@ -547,21 +600,29 @@ contract campaign is oracleClient { return true; } - function getGains(bytes32 idProm) public notPaused { - require(proms[idProm].influencer == msg.sender, "link owner mismatch"); - uint256 diff = block.timestamp - proms[idProm].appliedDate; - require(diff > 86400, "less than 24h"); + function getGains(bytes32 idProm) public notPaused onlyInfluencer(idProm) { + promElement storage prom = proms[idProm]; + require(prom.influencer == msg.sender, "link owner mismatch"); + + Campaign storage cmp = campaigns[prom.idCampaign]; + // Check if the campaign is a bounty campaign + if (cmp.bounties.length > 0) { + require(cmp.endDate < block.timestamp, "Bounty campaign has not ended yet"); + } + + uint256 diff = block.timestamp - prom.appliedDate; + require(diff > 300, "less than 15min"); require( - block.timestamp - proms[idProm].lastHarvest > 86400, - "less than 24h to harvest again" + block.timestamp - prom.lastHarvest > 300, + "less than 15min to harvest again" ); - IBEP20 erc20 = IBEP20(proms[idProm].funds.token); - uint256 amount = proms[idProm].funds.amount; - proms[idProm].funds.amount = 0; - proms[idProm].lastHarvest = block.timestamp; - erc20.transfer(proms[idProm].influencer, amount); + IBEP20 erc20 = IBEP20(prom.funds.token); + uint256 amount = prom.funds.amount; + prom.funds.amount = 0; + prom.lastHarvest = block.timestamp; + erc20.transfer(prom.influencer, amount); emit PromPayed(idProm, amount); } @@ -576,7 +637,7 @@ contract campaign is oracleClient { "campaign not ended" ); require( - block.timestamp - campaigns[idCampaign].endDate > 1296000, + block.timestamp - campaigns[idCampaign].endDate > 300, "Withdraw not allowed under 15 days" ); @@ -600,6 +661,15 @@ contract campaign is oracleClient { return cproms; } +/** + * @dev Retrieves various ratios and limits associated with a campaign. + * @param idCampaign The unique identifier of the campaign. + * @return types An array of promotion types. + * @return likeRatios An array of like ratios for each promotion type. + * @return shareRatios An array of share ratios for each promotion type. + * @return viewRatios An array of view ratios for each promotion type. + * @return limits An array of reach limits for each promotion type. + */ function getRatios(bytes32 idCampaign) public notPaused view @@ -611,6 +681,10 @@ contract campaign is oracleClient { uint256[] memory limits ) { + + // Store the campaign element for efficient access + Campaign storage cmp = campaigns[idCampaign]; + uint8 l = 10; types = new uint8[](l); likeRatios = new uint256[](l); @@ -619,25 +693,34 @@ contract campaign is oracleClient { limits = new uint256[](l); for (uint8 i = 0; i < l; i++) { types[i] = i + 1; - likeRatios[i] = campaigns[idCampaign].ratios[i + 1].likeRatio; - shareRatios[i] = campaigns[idCampaign].ratios[i + 1].shareRatio; - viewRatios[i] = campaigns[idCampaign].ratios[i + 1].viewRatio; - limits[i] = campaigns[idCampaign].ratios[i + 1].reachLimit; + likeRatios[i] = cmp.ratios[i + 1].likeRatio; + shareRatios[i] = cmp.ratios[i + 1].shareRatio; + viewRatios[i] = cmp.ratios[i + 1].viewRatio; + limits[i] = cmp.ratios[i + 1].reachLimit; } return (types, likeRatios, shareRatios, viewRatios, limits); } +/** + * @dev Retrieves bounty details for a campaign. + * @param idCampaign The unique identifier of the campaign. + * @return bounty An array containing minRange, maxRange, typeSN, and amount for each bounty. + */ function getBounties(bytes32 idCampaign) public notPaused view returns (uint256[] memory bounty) { - bounty = new uint256[](campaigns[idCampaign].bounties.length * 4); - for (uint8 i = 0; i < campaigns[idCampaign].bounties.length; i++) { - bounty[i * 4] = campaigns[idCampaign].bounties[i].minRange; - bounty[i * 4 + 1] = campaigns[idCampaign].bounties[i].maxRange; - bounty[i * 4 + 2] = campaigns[idCampaign].bounties[i].typeSN; - bounty[i * 4 + 3] = campaigns[idCampaign].bounties[i].amount; + + Campaign storage cmp = campaigns[idCampaign]; + uint256 bountyCount = cmp.bounties.length; + bounty = new uint256[](bountyCount * 4); + + for (uint8 i = 0; i < bountyCount; i++) { + bounty[i * 4] = cmp.bounties[i].minRange; + bounty[i * 4 + 1] = cmp.bounties[i].maxRange; + bounty[i * 4 + 2] = cmp.bounties[i].typeSN; + bounty[i * 4 + 3] = cmp.bounties[i].amount; } return bounty; } @@ -647,10 +730,11 @@ contract campaign is oracleClient { view returns (bytes32[] memory creq) { - uint256 nbResults = proms[idProm].nbResults; + promElement storage prom = proms[idProm]; + uint256 nbResults = prom.nbResults; creq = new bytes32[](nbResults); for (uint64 i = 0; i < nbResults; i++) { - creq[i] = proms[idProm].results[i]; + creq[i] = prom.results[i]; } return creq; } From b14bdd1a02100cb67766a5c930d747f92d0397b3 Mon Sep 17 00:00:00 2001 From: HamdiBenK Date: Wed, 23 Aug 2023 15:12:26 +0100 Subject: [PATCH 2/6] update oracle contract --- contracts/oracle.sol | 48 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 39 insertions(+), 9 deletions(-) diff --git a/contracts/oracle.sol b/contracts/oracle.sol index d511eb1e..d11429fc 100644 --- a/contracts/oracle.sol +++ b/contracts/oracle.sol @@ -1,7 +1,15 @@ +/** + *Submitted for verification at BscScan.com on 2023-08-23 +*/ + +/** + *Submitted for verification at BscScan.com on 2023-01-17 +*/ + //SPDX-License-Identifier: Unlicense -pragma solidity ^0.8.17; +pragma solidity 0.8.19; contract owned { address payable public owner; @@ -62,8 +70,29 @@ contract oracle is limited { _; } - function changeAnswer (address a,bool allow,address token,uint256 fee) onlyOwner public { - oracleList[a] = oracleUnit(allow,token,fee); + /** + * @dev Modifier to prevent reentrancy attacks during function execution. + * It sets a flag before the function body and resets it after execution. + */ + modifier noReentrancy() { + // Ensure that reentrancy is not already in progress + require(!_withdrawalInProgress, "Reentrant call"); + + _withdrawalInProgress = true; // Set the flag to prevent reentrancy + _; // This underscores the location where the modified function's body will be placed. + _withdrawalInProgress = false; // Reset the flag after the function completes + } + + modifier validCampaignAddress(address campaignContract){ + require(campaignContract != address(0), "Invalid campaign address"); + _; + } + bool private _withdrawalInProgress; // Reentrancy guard + + function changeAnswer (address oracleAddress,bool allow,address token,uint256 fee) onlyOwner validCampaignAddress(oracleAddress) public { + oracleList[oracleAddress] = oracleUnit(allow,token,fee); + + emit OracleInfoUpdated(oracleAddress, allow, token, fee); } event AskRequest(bytes32 indexed idRequest, uint8 typeSN, string idPost,string idUser); @@ -71,7 +100,8 @@ contract oracle is limited { event AskRequestBounty( uint8 typeSN, string idPost,string idUser,bytes32 idProm); event AnswerRequestBounty(bytes32 indexed idProm,uint256 nbAbos); - + event OracleInfoUpdated(address indexed oracleAddress, bool allow, address token, uint256 fee); + function ask (uint8 typeSN,string memory idPost,string memory idUser, bytes32 idRequest) public onlyCanAsk { @@ -83,20 +113,20 @@ contract oracle is limited { emit AskRequestBounty( typeSN, idPost, idUser, idProm); } - function answer(address campaignContract,bytes32 idRequest,uint64 likes,uint64 shares, uint64 views) public onlyOwner { + function answer(address campaignContract,bytes32 idRequest,uint64 likes,uint64 shares, uint64 views) public onlyOwner validCampaignAddress(campaignContract){ ICampaign campaign = ICampaign(campaignContract); campaign.update(idRequest,likes,shares,views); emit AnswerRequest(idRequest,likes,shares,views); } - function answerBounty(address campaignContract,bytes32 idProm,uint256 nbAbos) public onlyOwner { + function answerBounty(address campaignContract,bytes32 idProm,uint256 nbAbos) public onlyOwner validCampaignAddress(campaignContract){ ICampaign campaign = ICampaign(campaignContract); campaign.updateBounty(idProm,nbAbos); emit AnswerRequestBounty(idProm,nbAbos); } - function thirdPartyAnswer(address campaignContract,bytes32 idRequest,uint64 likes,uint64 shares, uint64 views) public onlyCanAnswer { + function thirdPartyAnswer(address campaignContract,bytes32 idRequest,uint64 likes,uint64 shares, uint64 views) public onlyCanAnswer validCampaignAddress(campaignContract) { ICampaign campaign = ICampaign(campaignContract); campaign.update(idRequest,likes,shares,views); emit AnswerRequest(idRequest,likes,shares,views); @@ -106,8 +136,8 @@ contract oracle is limited { erc20.transfer(msg.sender,oracleList[msg.sender].fee); } - function withdraw() onlyOwner public { - owner.transfer(address(this).balance); + function withdraw() onlyOwner noReentrancy public { + owner.transfer(address(this).balance); } function transferToken (address token,address to,uint256 val) public onlyOwner { From 45bfaebbf1a1d192e3b2c0b5aadd214a5e4949b9 Mon Sep 17 00:00:00 2001 From: HamdiBenK Date: Thu, 24 Aug 2023 11:50:26 +0100 Subject: [PATCH 3/6] update campaign.sol --- contracts/campaign.sol | 294 ++++++++++++++++++++--------------------- 1 file changed, 146 insertions(+), 148 deletions(-) diff --git a/contracts/campaign.sol b/contracts/campaign.sol index 4d5d44d1..f9ab3693 100644 --- a/contracts/campaign.sol +++ b/contracts/campaign.sol @@ -1,6 +1,4 @@ -/** - *Submitted for verification at BscScan.com on 2023-08-21 -*/ + //SPDX-License-Identifier: Unlicense pragma solidity 0.8.19; @@ -35,7 +33,7 @@ abstract contract owned { constructor() { owner = payable(msg.sender); treasory = 0xCA6C8E85804d7dC2CA7EcA018de77Aa2Ab8bE52C; - oracle = 0x5F8f3Efd4118136626eE5A240139ECa7cA22C72A; + oracle = 0x72d0b60e31dFfbe31c42B926C9d3d4674098294e; } function transferOwnership(address payable newOwner) public notPaused onlyOwner { @@ -141,34 +139,34 @@ contract campaign is oracleClient { uint256 amount; } - struct Result { - bytes32 idProm; - uint64 likes; - uint64 shares; - uint64 views; + struct PostStatistics { + bytes32 idProm; // Hash of the associated prom (from SocialMediaPostData) + uint64 likes; // Number of likes + uint64 shares; // Number of shares + uint64 views; // Number of views } - struct promElement { - address influencer; - bytes32 idCampaign; - bool isAccepted; + struct SocialMediaPostData { + address influencer; // Address of the influencer who posted + bytes32 idCampaign; // Hash of the campaign associated with the post + bool isAccepted; // Flag indicating if the post is accepted bool isPayed; Fund funds; - uint8 typeSN; - uint256 appliedDate; - uint64 abosNumber; - string idPost; + uint8 typeSN; // Type of social network (e.g., Twitter, Instagram, LinkedIn,Tiktok,Facebook) + uint256 appliedDate; // Timestamp of when the post was applied + uint64 abosNumber; // Number of followers/subscribers + string idPost; // Timestamp of the last result harvest string idUser; uint64 nbResults; mapping(uint64 => bytes32) results; bytes32 prevResult; - uint256 lastHarvest; + uint256 lastHarvest; // Timestamp of the last result harvest uint256 validate; } mapping(bytes32 => Campaign) public campaigns; - mapping(bytes32 => promElement) public proms; - mapping(bytes32 => Result) public results; + mapping(bytes32 => SocialMediaPostData) public proms; + mapping(bytes32 => PostStatistics) public results; mapping(bytes32 => bool) public isAlreadyUsed; mapping(bytes32 => mapping(address => uint64)) public influencerProms; @@ -269,15 +267,15 @@ contract campaign is oracleClient { block.timestamp ) ); - Campaign storage c = campaigns[campaignId]; - c.advertiser = msg.sender; - c.dataUrl = dataUrl; - c.startDate = startDate; - c.endDate = endDate; - c.participationLimit = limit; - c.nbProms = 0; - c.nbValidProms = 0; - c.funds = Fund(address(0), 0); + Campaign storage campaignData = campaigns[campaignId]; + campaignData.advertiser = msg.sender; + campaignData.dataUrl = dataUrl; + campaignData.startDate = startDate; + campaignData.endDate = endDate; + campaignData.participationLimit = limit; + campaignData.nbProms = 0; + campaignData.nbValidProms = 0; + campaignData.funds = Fund(address(0), 0); //campaigns[campaignId] = Campaign(msg.sender,dataUrl,startDate,endDate,0,0,Fund(address(0),0)); emit CampaignCreated(campaignId, startDate, endDate, dataUrl,limit); @@ -317,17 +315,17 @@ contract campaign is oracleClient { block.timestamp ) ); - Campaign storage c = campaigns[campaignId]; - c.advertiser = msg.sender; - c.dataUrl = dataUrl; - c.startDate = startDate; - c.endDate = endDate; - c.participationLimit = limit; - c.nbProms = 0; - c.nbValidProms = 0; - c.funds = Fund(address(0), 0); + Campaign storage campaignData = campaigns[campaignId]; + campaignData.advertiser = msg.sender; + campaignData.dataUrl = dataUrl; + campaignData.startDate = startDate; + campaignData.endDate = endDate; + campaignData.participationLimit = limit; + campaignData.nbProms = 0; + campaignData.nbValidProms = 0; + campaignData.funds = Fund(address(0), 0); for (uint256 i = 0; i < bounties.length; i = i + 4) { - c.bounties.push( + campaignData.bounties.push( bountyUnit( bounties[i], bounties[i + 1], @@ -372,8 +370,8 @@ contract campaign is oracleClient { bytes32 _r, bytes32 _s ) public notPaused { - Campaign storage cmp = campaigns[idCampaign]; - require(cmp.endDate > block.timestamp, "campaign ended"); + Campaign storage campaignData = campaigns[idCampaign]; + require(campaignData.endDate > block.timestamp, "campaign ended"); address signer = VerifyMessage(_hashedMessage, _v, _r, _s); require(signer == ownerLink, "campaign applayer is mismatch"); bytes32 prom = keccak256( @@ -392,21 +390,21 @@ contract campaign is oracleClient { // Get the current number of promotions used by the influencer for this campaign uint64 influencerPromCount = influencerProms[idCampaign][ownerLink]; - require(influencerPromCount < cmp.participationLimit, "Participation limit exceeded"); - - promElement storage p = proms[prom]; - p.influencer = ownerLink; - p.idCampaign = idCampaign; - p.isAccepted = true; - p.funds = Fund(address(0), 0); - p.typeSN = typeSN; - p.idPost = idPost; - p.idUser = idUser; - p.abosNumber = abosNumber; - p.nbResults = 0; - p.prevResult = 0; - p.validate = block.timestamp; - cmp.nbValidProms++; + require(influencerPromCount < campaignData.participationLimit, "Participation limit exceeded"); + + SocialMediaPostData storage post = proms[prom]; + post.influencer = ownerLink; + post.idCampaign = idCampaign; + post.isAccepted = true; + post.funds = Fund(address(0), 0); + post.typeSN = typeSN; + post.idPost = idPost; + post.idUser = idUser; + post.abosNumber = abosNumber; + post.nbResults = 0; + post.prevResult = 0; + post.validate = block.timestamp; + campaignData.nbValidProms++; // Increment the influencer's promotion count for this campaign incrementPromotionCount(idCampaign, ownerLink); @@ -415,28 +413,28 @@ contract campaign is oracleClient { } function updateCampaignStats(bytes32 idCampaign) public notPaused { - Campaign storage cmp = campaigns[idCampaign]; + Campaign storage campaignData = campaigns[idCampaign]; - for (uint64 i = 0; i < cmp.nbProms; i++) { - bytes32 idProm = cmp.proms[i]; - // Retrieve the prom element associated with the given idProm from the proms mapping - promElement storage prom = proms[idProm]; + for (uint64 i = 0; i < campaignData.nbProms; i++) { + bytes32 idProm = campaignData.proms[i]; + // Retrieve the post element associated with the given idProm from the proms mapping + SocialMediaPostData storage post = proms[idProm]; - if (prom.isAccepted) { + if (post.isAccepted) { bytes32 idRequest = keccak256( abi.encodePacked( - prom.typeSN, - prom.idPost, - prom.idUser, + post.typeSN, + post.idPost, + post.idUser, block.timestamp ) ); - results[idRequest] = Result(idProm, 0, 0, 0); - prom.results[prom.nbResults++] = idRequest; + results[idRequest] = PostStatistics(idProm, 0, 0, 0); + post.results[post.nbResults++] = idRequest; ask( - prom.typeSN, - prom.idPost, - prom.idUser, + post.typeSN, + post.idPost, + post.idUser, idRequest ); } @@ -448,35 +446,35 @@ contract campaign is oracleClient { notPaused returns (bytes32 requestId) { - promElement storage prom = proms[idProm]; // Store the prom element for efficient access + SocialMediaPostData storage post = proms[idProm]; // Store the post element for efficient access - require(prom.isAccepted, "link not validated"); + require(post.isAccepted, "link not validated"); bytes32 idRequest = keccak256( abi.encodePacked( - prom.typeSN, - prom.idPost, - prom.idUser, + post.typeSN, + post.idPost, + post.idUser, block.timestamp ) ); - results[idRequest] = Result(idProm, 0, 0, 0); - prom.results[prom.nbResults++] = idRequest; + results[idRequest] = PostStatistics(idProm, 0, 0, 0); + post.results[post.nbResults++] = idRequest; ask( - prom.typeSN, - prom.idPost, - prom.idUser, + post.typeSN, + post.idPost, + post.idUser, idRequest ); return idRequest; } function updateBounty(bytes32 idProm) public notPaused { - promElement storage prom = proms[idProm]; // Store the prom element for efficient access - require(prom.isAccepted, "link not validated"); + SocialMediaPostData storage post = proms[idProm]; // Store the post element for efficient access + require(post.isAccepted, "link not validated"); askBounty( - prom.typeSN, - prom.idPost, - prom.idUser, + post.typeSN, + post.idPost, + post.idUser, idProm ); } @@ -516,38 +514,38 @@ contract campaign is oracleClient { require(msg.sender == oracle, "oracle mismatch"); // Store the prom element for efficient access - promElement storage prom = proms[idProm]; - require(!prom.isPayed, "link already paid"); - prom.isPayed = true; + SocialMediaPostData storage post = proms[idProm]; + require(!post.isPayed, "link already paid"); + post.isPayed = true; // Store the campaign element for efficient access - Campaign storage cmp = campaigns[prom.idCampaign]; + Campaign storage campaignData = campaigns[post.idCampaign]; - prom.funds.token = cmp.funds.token; + post.funds.token = campaignData.funds.token; uint256 gain = 0; for ( uint256 i = 0; - i < cmp.bounties.length; + i < campaignData.bounties.length; i++ ) { if ( - nbAbos >= cmp.bounties[i].minRange && - nbAbos < cmp.bounties[i].maxRange && - prom.typeSN == cmp.bounties[i].typeSN + nbAbos >= campaignData.bounties[i].minRange && + nbAbos < campaignData.bounties[i].maxRange && + post.typeSN == campaignData.bounties[i].typeSN ) { - gain = cmp.bounties[i].amount; + gain = campaignData.bounties[i].amount; } } - if (cmp.funds.amount <= gain) { - //cmp.endDate = uint64(block.timestamp); - prom.funds.amount += cmp.funds.amount; - cmp.funds.amount = 0; - emit CampaignFundsSpent(prom.idCampaign); + if (campaignData.funds.amount <= gain) { + //campaignData.endDate = uint64(block.timestamp); + post.funds.amount += campaignData.funds.amount; + campaignData.funds.amount = 0; + emit CampaignFundsSpent(post.idCampaign); return true; } - cmp.funds.amount -= gain; - prom.funds.amount += gain; + campaignData.funds.amount -= gain; + post.funds.amount += gain; return true; } @@ -559,7 +557,7 @@ contract campaign is oracleClient { ) external notPaused returns (bool ok) { require(msg.sender == oracle, "oracle mismatch"); - promElement storage prom = proms[results[idRequest].idProm]; + SocialMediaPostData storage post = proms[results[idRequest].idProm]; results[idRequest].likes = likes; results[idRequest].shares = shares; @@ -567,62 +565,62 @@ contract campaign is oracleClient { uint256 gain = 0; - if (likes > results[prom.prevResult].likes) + if (likes > results[post.prevResult].likes) gain += - (likes - results[prom.prevResult].likes) * - campaigns[prom.idCampaign].ratios[prom.typeSN].likeRatio; - if (shares > results[prom.prevResult].shares) + (likes - results[post.prevResult].likes) * + campaigns[post.idCampaign].ratios[post.typeSN].likeRatio; + if (shares > results[post.prevResult].shares) gain += - (shares - results[prom.prevResult].shares) * - campaigns[prom.idCampaign].ratios[prom.typeSN].shareRatio; - if (views > results[prom.prevResult].views) + (shares - results[post.prevResult].shares) * + campaigns[post.idCampaign].ratios[post.typeSN].shareRatio; + if (views > results[post.prevResult].views) gain += - (views - results[prom.prevResult].views) * - campaigns[prom.idCampaign].ratios[prom.typeSN].viewRatio; - prom.prevResult = idRequest; + (views - results[post.prevResult].views) * + campaigns[post.idCampaign].ratios[post.typeSN].viewRatio; + post.prevResult = idRequest; // // warn campaign low credits // - if (prom.funds.token == address(0)) { - prom.funds.token = campaigns[prom.idCampaign].funds.token; + if (post.funds.token == address(0)) { + post.funds.token = campaigns[post.idCampaign].funds.token; } - if (campaigns[prom.idCampaign].funds.amount <= gain) { - //campaigns[prom.idCampaign].endDate = uint64(block.timestamp); - prom.funds.amount += campaigns[prom.idCampaign].funds.amount; - campaigns[prom.idCampaign].funds.amount = 0; - emit CampaignFundsSpent(prom.idCampaign); + if (campaigns[post.idCampaign].funds.amount <= gain) { + //campaigns[post.idCampaign].endDate = uint64(block.timestamp); + post.funds.amount += campaigns[post.idCampaign].funds.amount; + campaigns[post.idCampaign].funds.amount = 0; + emit CampaignFundsSpent(post.idCampaign); return true; } - campaigns[prom.idCampaign].funds.amount -= gain; - prom.funds.amount += gain; + campaigns[post.idCampaign].funds.amount -= gain; + post.funds.amount += gain; return true; } function getGains(bytes32 idProm) public notPaused onlyInfluencer(idProm) { - promElement storage prom = proms[idProm]; - require(prom.influencer == msg.sender, "link owner mismatch"); + SocialMediaPostData storage post = proms[idProm]; + require(post.influencer == msg.sender, "link owner mismatch"); - Campaign storage cmp = campaigns[prom.idCampaign]; + Campaign storage campaignData = campaigns[post.idCampaign]; // Check if the campaign is a bounty campaign - if (cmp.bounties.length > 0) { - require(cmp.endDate < block.timestamp, "Bounty campaign has not ended yet"); + if (campaignData.bounties.length > 0) { + require(campaignData.endDate < block.timestamp, "Bounty campaign has not ended yet"); } - uint256 diff = block.timestamp - prom.appliedDate; + uint256 diff = block.timestamp - post.appliedDate; require(diff > 300, "less than 15min"); require( - block.timestamp - prom.lastHarvest > 300, + block.timestamp - post.lastHarvest > 300, "less than 15min to harvest again" ); - IBEP20 erc20 = IBEP20(prom.funds.token); - uint256 amount = prom.funds.amount; - prom.funds.amount = 0; - prom.lastHarvest = block.timestamp; - erc20.transfer(prom.influencer, amount); + IBEP20 erc20 = IBEP20(post.funds.token); + uint256 amount = post.funds.amount; + post.funds.amount = 0; + post.lastHarvest = block.timestamp; + erc20.transfer(post.influencer, amount); emit PromPayed(idProm, amount); } @@ -683,7 +681,7 @@ contract campaign is oracleClient { { // Store the campaign element for efficient access - Campaign storage cmp = campaigns[idCampaign]; + Campaign storage campaignData = campaigns[idCampaign]; uint8 l = 10; types = new uint8[](l); @@ -693,10 +691,10 @@ contract campaign is oracleClient { limits = new uint256[](l); for (uint8 i = 0; i < l; i++) { types[i] = i + 1; - likeRatios[i] = cmp.ratios[i + 1].likeRatio; - shareRatios[i] = cmp.ratios[i + 1].shareRatio; - viewRatios[i] = cmp.ratios[i + 1].viewRatio; - limits[i] = cmp.ratios[i + 1].reachLimit; + likeRatios[i] = campaignData.ratios[i + 1].likeRatio; + shareRatios[i] = campaignData.ratios[i + 1].shareRatio; + viewRatios[i] = campaignData.ratios[i + 1].viewRatio; + limits[i] = campaignData.ratios[i + 1].reachLimit; } return (types, likeRatios, shareRatios, viewRatios, limits); } @@ -712,15 +710,15 @@ contract campaign is oracleClient { returns (uint256[] memory bounty) { - Campaign storage cmp = campaigns[idCampaign]; - uint256 bountyCount = cmp.bounties.length; + Campaign storage campaignData = campaigns[idCampaign]; + uint256 bountyCount = campaignData.bounties.length; bounty = new uint256[](bountyCount * 4); for (uint8 i = 0; i < bountyCount; i++) { - bounty[i * 4] = cmp.bounties[i].minRange; - bounty[i * 4 + 1] = cmp.bounties[i].maxRange; - bounty[i * 4 + 2] = cmp.bounties[i].typeSN; - bounty[i * 4 + 3] = cmp.bounties[i].amount; + bounty[i * 4] = campaignData.bounties[i].minRange; + bounty[i * 4 + 1] = campaignData.bounties[i].maxRange; + bounty[i * 4 + 2] = campaignData.bounties[i].typeSN; + bounty[i * 4 + 3] = campaignData.bounties[i].amount; } return bounty; } @@ -730,11 +728,11 @@ contract campaign is oracleClient { view returns (bytes32[] memory creq) { - promElement storage prom = proms[idProm]; - uint256 nbResults = prom.nbResults; + SocialMediaPostData storage post = proms[idProm]; + uint256 nbResults = post.nbResults; creq = new bytes32[](nbResults); for (uint64 i = 0; i < nbResults; i++) { - creq[i] = prom.results[i]; + creq[i] = post.results[i]; } return creq; } From d9e7cd5ad376bf4b8c7057fedcb4206a2c046715 Mon Sep 17 00:00:00 2001 From: HamdiBenK Date: Thu, 24 Aug 2023 14:42:05 +0100 Subject: [PATCH 4/6] update struct name --- contracts/campaign.sol | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/contracts/campaign.sol b/contracts/campaign.sol index f9ab3693..85638622 100644 --- a/contracts/campaign.sol +++ b/contracts/campaign.sol @@ -1,4 +1,6 @@ - +/** + *Submitted for verification at BscScan.com on 2023-08-24 +*/ //SPDX-License-Identifier: Unlicense pragma solidity 0.8.19; @@ -120,7 +122,7 @@ contract campaign is oracleClient { uint256 amount; } - struct Campaign { + struct AdPool { address advertiser; string dataUrl; uint64 startDate; @@ -164,7 +166,7 @@ contract campaign is oracleClient { uint256 validate; } - mapping(bytes32 => Campaign) public campaigns; + mapping(bytes32 => AdPool) public campaigns; mapping(bytes32 => SocialMediaPostData) public proms; mapping(bytes32 => PostStatistics) public results; mapping(bytes32 => bool) public isAlreadyUsed; @@ -267,7 +269,7 @@ contract campaign is oracleClient { block.timestamp ) ); - Campaign storage campaignData = campaigns[campaignId]; + AdPool storage campaignData = campaigns[campaignId]; campaignData.advertiser = msg.sender; campaignData.dataUrl = dataUrl; campaignData.startDate = startDate; @@ -276,7 +278,7 @@ contract campaign is oracleClient { campaignData.nbProms = 0; campaignData.nbValidProms = 0; campaignData.funds = Fund(address(0), 0); - //campaigns[campaignId] = Campaign(msg.sender,dataUrl,startDate,endDate,0,0,Fund(address(0),0)); + //campaigns[campaignId] = AdPool(msg.sender,dataUrl,startDate,endDate,0,0,Fund(address(0),0)); emit CampaignCreated(campaignId, startDate, endDate, dataUrl,limit); for (uint8 i = 0; i < ratios.length; i = i + 4) { @@ -315,7 +317,7 @@ contract campaign is oracleClient { block.timestamp ) ); - Campaign storage campaignData = campaigns[campaignId]; + AdPool storage campaignData = campaigns[campaignId]; campaignData.advertiser = msg.sender; campaignData.dataUrl = dataUrl; campaignData.startDate = startDate; @@ -370,7 +372,7 @@ contract campaign is oracleClient { bytes32 _r, bytes32 _s ) public notPaused { - Campaign storage campaignData = campaigns[idCampaign]; + AdPool storage campaignData = campaigns[idCampaign]; require(campaignData.endDate > block.timestamp, "campaign ended"); address signer = VerifyMessage(_hashedMessage, _v, _r, _s); require(signer == ownerLink, "campaign applayer is mismatch"); @@ -413,7 +415,7 @@ contract campaign is oracleClient { } function updateCampaignStats(bytes32 idCampaign) public notPaused { - Campaign storage campaignData = campaigns[idCampaign]; + AdPool storage campaignData = campaigns[idCampaign]; for (uint64 i = 0; i < campaignData.nbProms; i++) { bytes32 idProm = campaignData.proms[i]; @@ -518,7 +520,7 @@ contract campaign is oracleClient { require(!post.isPayed, "link already paid"); post.isPayed = true; // Store the campaign element for efficient access - Campaign storage campaignData = campaigns[post.idCampaign]; + AdPool storage campaignData = campaigns[post.idCampaign]; post.funds.token = campaignData.funds.token; @@ -602,7 +604,7 @@ contract campaign is oracleClient { SocialMediaPostData storage post = proms[idProm]; require(post.influencer == msg.sender, "link owner mismatch"); - Campaign storage campaignData = campaigns[post.idCampaign]; + AdPool storage campaignData = campaigns[post.idCampaign]; // Check if the campaign is a bounty campaign if (campaignData.bounties.length > 0) { require(campaignData.endDate < block.timestamp, "Bounty campaign has not ended yet"); @@ -681,7 +683,7 @@ contract campaign is oracleClient { { // Store the campaign element for efficient access - Campaign storage campaignData = campaigns[idCampaign]; + AdPool storage campaignData = campaigns[idCampaign]; uint8 l = 10; types = new uint8[](l); @@ -710,7 +712,7 @@ contract campaign is oracleClient { returns (uint256[] memory bounty) { - Campaign storage campaignData = campaigns[idCampaign]; + AdPool storage campaignData = campaigns[idCampaign]; uint256 bountyCount = campaignData.bounties.length; bounty = new uint256[](bountyCount * 4); From e800c870de6bc844b3aeff0bb8e2e1e340d2b64f Mon Sep 17 00:00:00 2001 From: HamdiBenK Date: Mon, 4 Sep 2023 15:51:00 +0100 Subject: [PATCH 5/6] handle limitParticipation in campaign apis --- controllers/campaign.controller.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/controllers/campaign.controller.js b/controllers/campaign.controller.js index 560449c3..7a20a4d9 100644 --- a/controllers/campaign.controller.js +++ b/controllers/campaign.controller.js @@ -955,12 +955,6 @@ exports.apply = async (req, res) => { prom.oracle = findBountyOracle(prom.typeSN) var insert = await CampaignLink.create(prom) - await notificationManager(id, 'apply_campaign', { - cmp_name: title, - cmp_hash: idCampaign, - hash, - network: campaignDetails.token.type, - }) let socialOracle = await getPromApplyStats( prom.oracle, prom, From 26fe5ab6f481a03c98ee1612588e13b5887d2035 Mon Sep 17 00:00:00 2001 From: HamdiBenK Date: Mon, 4 Sep 2023 16:39:26 +0100 Subject: [PATCH 6/6] handle limitParticipation in campaign apis --- controllers/campaign.controller.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/controllers/campaign.controller.js b/controllers/campaign.controller.js index 7a20a4d9..9b1fb26b 100644 --- a/controllers/campaign.controller.js +++ b/controllers/campaign.controller.js @@ -467,10 +467,11 @@ module.exports.launchBounty = async (req, res) => { txhash: ret.transactionHash, contract: contract.toLowerCase(), } - await Event.create(event) - await notificationManager(id, 'create_campaign', { + + await Promise.allSettled([Event.create(event), notificationManager(id, 'create_campaign', { cmp:campaign - }) + })]) + } } }