207 lines
6.8 KiB
Solidity
207 lines
6.8 KiB
Solidity
// 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");
|
|
}
|
|
} |