// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; /** * @title FeeBarrier * @dev Contract for implementing economic barriers for unverified users submitting perspectives */ contract FeeBarrier { // State variables uint256 public constant FEE_AMOUNT = 0.01 ether; // POL fee amount uint256 public constant REFUND_PERIOD = 1 days; // Time period after which a fee can be refunded address public owner; // Structs struct Content { address user; string issueId; string content; string fileUrl; bool isVerified; uint256 timestamp; bool isFlagged; bool feeRefunded; } // Mappings mapping(uint256 => Content) public contents; uint256 public contentCounter; // Events event FeePaid(address indexed user, uint256 indexed contentId, uint256 fee); event FeeRefunded(address indexed user, uint256 indexed contentId, uint256 fee); event ContentFlagged(uint256 indexed contentId, address flagger); event ContentSubmitted(uint256 indexed contentId, address indexed user, string issueId); // Constructor constructor() { owner = msg.sender; contentCounter = 0; } // Modifiers modifier onlyOwner() { require(msg.sender == owner, "Only owner can call this function"); _; } /** * @dev Submit content with a fee for unverified users * @param issueId The ID of the issue this content relates to * @param content The actual content text * @param fileUrl Optional URL to an attached file * @param isVerified Whether the user is verified * @return contentId The ID of the submitted content */ function payFeeAndSubmit( string memory issueId, string memory content, string memory fileUrl, bool isVerified ) public payable returns (uint256) { // Verified users don't need to pay a fee if (!isVerified) { require(msg.value >= FEE_AMOUNT, "Insufficient fee amount"); } uint256 contentId = contentCounter++; contents[contentId] = Content({ user: msg.sender, issueId: issueId, content: content, fileUrl: fileUrl, isVerified: isVerified, timestamp: block.timestamp, isFlagged: false, feeRefunded: isVerified // Mark as refunded already if verified (no fee) }); emit ContentSubmitted(contentId, msg.sender, issueId); // Only emit fee event for unverified users if (!isVerified) { emit FeePaid(msg.sender, contentId, msg.value); } return contentId; } /** * @dev Refund fee if content is not flagged after the refund period * @param contentId The ID of the content to refund fee for */ function refundFee(uint256 contentId) public { Content storage contentItem = contents[contentId]; require(contentId < contentCounter, "Content does not exist"); require(!contentItem.isVerified, "No fee to refund for verified users"); require(!contentItem.feeRefunded, "Fee already refunded"); require(!contentItem.isFlagged, "Cannot refund fee for flagged content"); require( block.timestamp >= contentItem.timestamp + REFUND_PERIOD, "Refund period not passed yet" ); contentItem.feeRefunded = true; // Transfer the fee back to the user (bool success, ) = contentItem.user.call{value: FEE_AMOUNT}(""); require(success, "Refund failed"); emit FeeRefunded(contentItem.user, contentId, FEE_AMOUNT); } /** * @dev Flag content for moderation (callable by verified users or contract owner) * @param contentId The ID of the content to flag */ function flagContent(uint256 contentId) public { require(contentId < contentCounter, "Content does not exist"); require(!contents[contentId].isFlagged, "Content already flagged"); // Only contract owner or verified users with special permissions can flag content // (This would be implemented with a proper access control system in a real contract) contents[contentId].isFlagged = true; emit ContentFlagged(contentId, msg.sender); } /** * @dev Batch refund for multiple contents that have passed the refund period * @param contentIds Array of content IDs to refund */ function batchRefund(uint256[] memory contentIds) public { for (uint256 i = 0; i < contentIds.length; i++) { try this.refundFee(contentIds[i]) { // Refund successful } catch { // Skip failed refunds } } } /** * @dev Get content details * @param contentId The ID of the content to get */ function getContent(uint256 contentId) public view returns ( address user, string memory issueId, string memory content, string memory fileUrl, bool isVerified, uint256 timestamp, bool isFlagged, bool feeRefunded ) { require(contentId < contentCounter, "Content does not exist"); Content storage contentItem = contents[contentId]; return ( contentItem.user, contentItem.issueId, contentItem.content, contentItem.fileUrl, contentItem.isVerified, contentItem.timestamp, contentItem.isFlagged, contentItem.feeRefunded ); } /** * @dev Check if content is eligible for refund * @param contentId The ID of the content to check */ function isEligibleForRefund(uint256 contentId) public view returns (bool) { if (contentId >= contentCounter) return false; Content storage contentItem = contents[contentId]; return ( !contentItem.isVerified && !contentItem.feeRefunded && !contentItem.isFlagged && block.timestamp >= contentItem.timestamp + REFUND_PERIOD ); } /** * @dev Withdraw any extra funds from the contract (only owner) * @param amount The amount to withdraw */ function withdraw(uint256 amount) public onlyOwner { require(amount <= address(this).balance, "Insufficient balance"); (bool success, ) = owner.call{value: amount}(""); require(success, "Withdrawal failed"); } /** * @dev Emergency withdraw function (only owner) */ function emergencyWithdraw() public onlyOwner { (bool success, ) = owner.call{value: address(this).balance}(""); require(success, "Emergency withdrawal failed"); } }