- Delete AIPowerRental.sol (566 lines) - replaced by AgentBounty.sol - Remove rental agreement system with provider/consumer model - Remove performance metrics and SLA tracking - Remove dispute resolution mechanism - Remove ZK-proof verification for performance - Remove provider/consumer authorization system - Bounty system provides superior developer incentive structure
719 lines
24 KiB
Solidity
719 lines
24 KiB
Solidity
// SPDX-License-Identifier: MIT
|
|
pragma solidity ^0.8.19;
|
|
|
|
import "@openzeppelin/contracts/access/Ownable.sol";
|
|
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
|
|
import "@openzeppelin/contracts/security/Pausable.sol";
|
|
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
|
import "./PerformanceVerifier.sol";
|
|
import "./AIToken.sol";
|
|
|
|
/**
|
|
* @title Agent Bounty System
|
|
* @dev Automated bounty board for AI agent capabilities with ZK-proof verification
|
|
* @notice Allows DAO and users to create bounties that are automatically completed when agents submit valid ZK-proofs
|
|
*/
|
|
contract AgentBounty is Ownable, ReentrancyGuard, Pausable {
|
|
|
|
// State variables
|
|
IERC20 public aitbcToken;
|
|
PerformanceVerifier public performanceVerifier;
|
|
|
|
uint256 public bountyCounter;
|
|
uint256 public creationFeePercentage = 50; // 0.5% in basis points
|
|
uint256 public successFeePercentage = 200; // 2% in basis points
|
|
uint256 public disputeFeePercentage = 10; // 0.1% in basis points
|
|
uint256 public platformFeePercentage = 100; // 1% in basis points
|
|
|
|
// Bounty tiers
|
|
enum BountyTier { BRONZE, SILVER, GOLD, PLATINUM }
|
|
|
|
// Bounty status
|
|
enum BountyStatus { CREATED, ACTIVE, SUBMITTED, VERIFIED, COMPLETED, EXPIRED, DISPUTED }
|
|
|
|
// Submission status
|
|
enum SubmissionStatus { PENDING, VERIFIED, REJECTED, DISPUTED }
|
|
|
|
// Structs
|
|
struct Bounty {
|
|
uint256 bountyId;
|
|
string title;
|
|
string description;
|
|
uint256 rewardAmount;
|
|
address creator;
|
|
BountyTier tier;
|
|
BountyStatus status;
|
|
bytes32 performanceCriteria; // Hash of performance requirements
|
|
uint256 minAccuracy;
|
|
uint256 deadline;
|
|
uint256 creationTime;
|
|
uint256 maxSubmissions;
|
|
uint256 submissionCount;
|
|
address winningSubmission;
|
|
bool requiresZKProof;
|
|
mapping(address => bool) authorizedSubmitters;
|
|
}
|
|
|
|
struct Submission {
|
|
uint256 submissionId;
|
|
uint256 bountyId;
|
|
address submitter;
|
|
bytes zkProof;
|
|
bytes32 performanceHash;
|
|
uint256 accuracy;
|
|
uint256 responseTime;
|
|
uint256 submissionTime;
|
|
SubmissionStatus status;
|
|
string disputeReason;
|
|
address verifier;
|
|
}
|
|
|
|
struct BountyStats {
|
|
uint256 totalBounties;
|
|
uint256 activeBounties;
|
|
uint256 completedBounties;
|
|
uint256 totalValueLocked;
|
|
uint256 averageReward;
|
|
uint256 successRate;
|
|
}
|
|
|
|
// Mappings
|
|
mapping(uint256 => Bounty) public bounties;
|
|
mapping(uint256 => Submission) public submissions;
|
|
mapping(uint256 => uint256[]) public bountySubmissions;
|
|
mapping(address => uint256[]) public userSubmissions;
|
|
mapping(address => uint256[]) public creatorBounties;
|
|
mapping(BountyTier => uint256) public tierRequirements;
|
|
mapping(uint256 => mapping(address => bool)) public hasSubmitted;
|
|
|
|
// Arrays
|
|
uint256[] public activeBountyIds;
|
|
address[] public authorizedCreators;
|
|
|
|
// Events
|
|
event BountyCreated(
|
|
uint256 indexed bountyId,
|
|
string title,
|
|
uint256 rewardAmount,
|
|
address indexed creator,
|
|
BountyTier tier,
|
|
uint256 deadline
|
|
);
|
|
|
|
event BountySubmitted(
|
|
uint256 indexed bountyId,
|
|
uint256 indexed submissionId,
|
|
address indexed submitter,
|
|
bytes32 performanceHash,
|
|
uint256 accuracy
|
|
);
|
|
|
|
event BountyVerified(
|
|
uint256 indexed bountyId,
|
|
uint256 indexed submissionId,
|
|
address indexed submitter,
|
|
bool success,
|
|
uint256 rewardAmount
|
|
);
|
|
|
|
event BountyCompleted(
|
|
uint256 indexed bountyId,
|
|
address indexed winner,
|
|
uint256 rewardAmount,
|
|
uint256 completionTime
|
|
);
|
|
|
|
event BountyExpired(
|
|
uint256 indexed bountyId,
|
|
uint256 refundAmount
|
|
);
|
|
|
|
event BountyDisputed(
|
|
uint256 indexed bountyId,
|
|
uint256 indexed submissionId,
|
|
address indexed disputer,
|
|
string reason
|
|
);
|
|
|
|
event PlatformFeeCollected(
|
|
uint256 indexed bountyId,
|
|
uint256 feeAmount,
|
|
address indexed collector
|
|
);
|
|
|
|
// Modifiers
|
|
modifier bountyExists(uint256 _bountyId) {
|
|
require(_bountyId < bountyCounter, "Bounty does not exist");
|
|
_;
|
|
}
|
|
|
|
modifier onlyAuthorizedCreator() {
|
|
require(isAuthorizedCreator(msg.sender), "Not authorized to create bounties");
|
|
_;
|
|
}
|
|
|
|
modifier validBountyStatus(uint256 _bountyId, BountyStatus _requiredStatus) {
|
|
require(bounties[_bountyId].status == _requiredStatus, "Invalid bounty status");
|
|
_;
|
|
}
|
|
|
|
modifier beforeDeadline(uint256 _deadline) {
|
|
require(block.timestamp <= _deadline, "Deadline passed");
|
|
_;
|
|
}
|
|
|
|
modifier sufficientBalance(uint256 _amount) {
|
|
require(aitbcToken.balanceOf(msg.sender) >= _amount, "Insufficient balance");
|
|
_;
|
|
}
|
|
|
|
constructor(address _aitbcToken, address _performanceVerifier) {
|
|
aitbcToken = IERC20(_aitbcToken);
|
|
performanceVerifier = PerformanceVerifier(_performanceVerifier);
|
|
|
|
// Set tier requirements (minimum reward amounts)
|
|
tierRequirements[BountyTier.BRONZE] = 100 * 10**18; // 100 AITBC
|
|
tierRequirements[BountyTier.SILVER] = 500 * 10**18; // 500 AITBC
|
|
tierRequirements[BountyTier.GOLD] = 1000 * 10**18; // 1000 AITBC
|
|
tierRequirements[BountyTier.PLATINUM] = 5000 * 10**18; // 5000 AITBC
|
|
}
|
|
|
|
/**
|
|
* @dev Creates a new bounty
|
|
* @param _title Bounty title
|
|
* @param _description Detailed description
|
|
* @param _rewardAmount Reward amount in AITBC tokens
|
|
* @param _tier Bounty tier
|
|
* @param _performanceCriteria Hash of performance requirements
|
|
* @param _minAccuracy Minimum accuracy required
|
|
* @param _deadline Bounty deadline
|
|
* @param _maxSubmissions Maximum number of submissions allowed
|
|
* @param _requiresZKProof Whether ZK-proof is required
|
|
*/
|
|
function createBounty(
|
|
string memory _title,
|
|
string memory _description,
|
|
uint256 _rewardAmount,
|
|
BountyTier _tier,
|
|
bytes32 _performanceCriteria,
|
|
uint256 _minAccuracy,
|
|
uint256 _deadline,
|
|
uint256 _maxSubmissions,
|
|
bool _requiresZKProof
|
|
) external
|
|
onlyAuthorizedCreator
|
|
sufficientBalance(_rewardAmount)
|
|
beforeDeadline(_deadline)
|
|
nonReentrant
|
|
returns (uint256)
|
|
{
|
|
require(_rewardAmount >= tierRequirements[_tier], "Reward below tier minimum");
|
|
require(_minAccuracy <= 100, "Invalid accuracy");
|
|
require(_maxSubmissions > 0, "Invalid max submissions");
|
|
require(_deadline > block.timestamp, "Invalid deadline");
|
|
|
|
uint256 bountyId = bountyCounter++;
|
|
|
|
Bounty storage bounty = bounties[bountyId];
|
|
bounty.bountyId = bountyId;
|
|
bounty.title = _title;
|
|
bounty.description = _description;
|
|
bounty.rewardAmount = _rewardAmount;
|
|
bounty.creator = msg.sender;
|
|
bounty.tier = _tier;
|
|
bounty.status = BountyStatus.CREATED;
|
|
bounty.performanceCriteria = _performanceCriteria;
|
|
bounty.minAccuracy = _minAccuracy;
|
|
bounty.deadline = _deadline;
|
|
bounty.creationTime = block.timestamp;
|
|
bounty.maxSubmissions = _maxSubmissions;
|
|
bounty.submissionCount = 0;
|
|
bounty.requiresZKProof = _requiresZKProof;
|
|
|
|
// Calculate and collect creation fee
|
|
uint256 creationFee = (_rewardAmount * creationFeePercentage) / 10000;
|
|
uint256 totalRequired = _rewardAmount + creationFee;
|
|
|
|
require(aitbcToken.balanceOf(msg.sender) >= totalRequired, "Insufficient total amount");
|
|
|
|
// Transfer tokens to contract
|
|
require(aitbcToken.transferFrom(msg.sender, address(this), totalRequired), "Transfer failed");
|
|
|
|
// Transfer creation fee to DAO treasury (owner for now)
|
|
if (creationFee > 0) {
|
|
require(aitbcToken.transfer(owner(), creationFee), "Fee transfer failed");
|
|
emit PlatformFeeCollected(bountyId, creationFee, owner());
|
|
}
|
|
|
|
// Update tracking arrays
|
|
activeBountyIds.push(bountyId);
|
|
creatorBounties[msg.sender].push(bountyId);
|
|
|
|
// Activate bounty
|
|
bounty.status = BountyStatus.ACTIVE;
|
|
|
|
emit BountyCreated(bountyId, _title, _rewardAmount, msg.sender, _tier, _deadline);
|
|
|
|
return bountyId;
|
|
}
|
|
|
|
/**
|
|
* @dev Submits a solution to a bounty
|
|
* @param _bountyId Bounty ID
|
|
* @param _zkProof Zero-knowledge proof (if required)
|
|
* @param _performanceHash Hash of performance metrics
|
|
* @param _accuracy Achieved accuracy
|
|
* @param _responseTime Response time in milliseconds
|
|
*/
|
|
function submitBountySolution(
|
|
uint256 _bountyId,
|
|
bytes memory _zkProof,
|
|
bytes32 _performanceHash,
|
|
uint256 _accuracy,
|
|
uint256 _responseTime
|
|
) external
|
|
bountyExists(_bountyId)
|
|
validBountyStatus(_bountyId, BountyStatus.ACTIVE)
|
|
beforeDeadline(bounties[_bountyId].deadline)
|
|
nonReentrant
|
|
returns (uint256)
|
|
{
|
|
Bounty storage bounty = bounties[_bountyId];
|
|
|
|
require(!hasSubmitted[_bountyId][msg.sender], "Already submitted");
|
|
require(bounty.submissionCount < bounty.maxSubmissions, "Max submissions reached");
|
|
|
|
if (bounty.requiresZKProof) {
|
|
require(_zkProof.length > 0, "ZK-proof required");
|
|
}
|
|
|
|
uint256 submissionId = bounty.submissionCount; // Use count as ID
|
|
|
|
Submission storage submission = submissions[submissionId];
|
|
submission.submissionId = submissionId;
|
|
submission.bountyId = _bountyId;
|
|
submission.submitter = msg.sender;
|
|
submission.zkProof = _zkProof;
|
|
submission.performanceHash = _performanceHash;
|
|
submission.accuracy = _accuracy;
|
|
submission.responseTime = _responseTime;
|
|
submission.submissionTime = block.timestamp;
|
|
submission.status = SubmissionStatus.PENDING;
|
|
|
|
// Update tracking
|
|
bounty.submissionCount++;
|
|
hasSubmitted[_bountyId][msg.sender] = true;
|
|
bountySubmissions[_bountyId].push(submissionId);
|
|
userSubmissions[msg.sender].push(submissionId);
|
|
|
|
// Auto-verify if ZK-proof is provided
|
|
if (_zkProof.length > 0) {
|
|
_verifySubmission(_bountyId, submissionId);
|
|
}
|
|
|
|
emit BountySubmitted(_bountyId, submissionId, msg.sender, _performanceHash, _accuracy);
|
|
|
|
return submissionId;
|
|
}
|
|
|
|
/**
|
|
* @dev Manually verifies a submission (oracle or automated)
|
|
* @param _bountyId Bounty ID
|
|
* @param _submissionId Submission ID
|
|
* @param _verified Whether the submission is verified
|
|
* @param _verifier Address of the verifier
|
|
*/
|
|
function verifySubmission(
|
|
uint256 _bountyId,
|
|
uint256 _submissionId,
|
|
bool _verified,
|
|
address _verifier
|
|
) external
|
|
bountyExists(_bountyId)
|
|
nonReentrant
|
|
{
|
|
Bounty storage bounty = bounties[_bountyId];
|
|
Submission storage submission = submissions[_submissionId];
|
|
|
|
require(submission.status == SubmissionStatus.PENDING, "Submission not pending");
|
|
require(submission.bountyId == _bountyId, "Submission bounty mismatch");
|
|
|
|
submission.status = _verified ? SubmissionStatus.VERIFIED : SubmissionStatus.REJECTED;
|
|
submission.verifier = _verifier;
|
|
|
|
if (_verified) {
|
|
// Check if this meets the bounty requirements
|
|
if (submission.accuracy >= bounty.minAccuracy) {
|
|
_completeBounty(_bountyId, _submissionId);
|
|
}
|
|
}
|
|
|
|
emit BountyVerified(_bountyId, _submissionId, submission.submitter, _verified, bounty.rewardAmount);
|
|
}
|
|
|
|
/**
|
|
* @dev Disputes a submission
|
|
* @param _bountyId Bounty ID
|
|
* @param _submissionId Submission ID
|
|
* @param _reason Reason for dispute
|
|
*/
|
|
function disputeSubmission(
|
|
uint256 _bountyId,
|
|
uint256 _submissionId,
|
|
string memory _reason
|
|
) external
|
|
bountyExists(_bountyId)
|
|
nonReentrant
|
|
{
|
|
Bounty storage bounty = bounties[_bountyId];
|
|
Submission storage submission = submissions[_submissionId];
|
|
|
|
require(submission.status == SubmissionStatus.VERIFIED, "Can only dispute verified submissions");
|
|
require(block.timestamp - submission.submissionTime <= 86400, "Dispute window expired"); // 24 hours
|
|
|
|
submission.status = SubmissionStatus.DISPUTED;
|
|
submission.disputeReason = _reason;
|
|
bounty.status = BountyStatus.DISPUTED;
|
|
|
|
// Collect dispute fee
|
|
uint256 disputeFee = (bounty.rewardAmount * disputeFeePercentage) / 10000;
|
|
if (disputeFee > 0) {
|
|
require(aitbcToken.transferFrom(msg.sender, address(this), disputeFee), "Dispute fee transfer failed");
|
|
}
|
|
|
|
emit BountyDisputed(_bountyId, _submissionId, msg.sender, _reason);
|
|
}
|
|
|
|
/**
|
|
* @dev Resolves a dispute
|
|
* @param _bountyId Bounty ID
|
|
* @param _submissionId Submission ID
|
|
* @param _upholdDispute Whether to uphold the dispute
|
|
*/
|
|
function resolveDispute(
|
|
uint256 _bountyId,
|
|
uint256 _submissionId,
|
|
bool _upholdDispute
|
|
) external onlyOwner bountyExists(_bountyId) nonReentrant {
|
|
Bounty storage bounty = bounties[_bountyId];
|
|
Submission storage submission = submissions[_submissionId];
|
|
|
|
require(bounty.status == BountyStatus.DISPUTED, "No dispute to resolve");
|
|
require(submission.status == SubmissionStatus.DISPUTED, "Submission not disputed");
|
|
|
|
if (_upholdDispute) {
|
|
// Reject the submission
|
|
submission.status = SubmissionStatus.REJECTED;
|
|
bounty.status = BountyStatus.ACTIVE;
|
|
|
|
// Return dispute fee
|
|
uint256 disputeFee = (bounty.rewardAmount * disputeFeePercentage) / 10000;
|
|
if (disputeFee > 0) {
|
|
require(aitbcToken.transfer(msg.sender, disputeFee), "Dispute fee return failed");
|
|
}
|
|
} else {
|
|
// Uphold the submission
|
|
submission.status = SubmissionStatus.VERIFIED;
|
|
_completeBounty(_bountyId, _submissionId);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @dev Expires a bounty and returns funds to creator
|
|
* @param _bountyId Bounty ID
|
|
*/
|
|
function expireBounty(uint256 _bountyId) external bountyExists(_bountyId) nonReentrant {
|
|
Bounty storage bounty = bounties[_bountyId];
|
|
|
|
require(bounty.status == BountyStatus.ACTIVE, "Bounty not active");
|
|
require(block.timestamp > bounty.deadline, "Deadline not passed");
|
|
|
|
bounty.status = BountyStatus.EXPIRED;
|
|
|
|
// Return funds to creator
|
|
uint256 refundAmount = bounty.rewardAmount;
|
|
require(aitbcToken.transfer(bounty.creator, refundAmount), "Refund transfer failed");
|
|
|
|
// Remove from active bounties
|
|
_removeFromActiveBounties(_bountyId);
|
|
|
|
emit BountyExpired(_bountyId, refundAmount);
|
|
}
|
|
|
|
/**
|
|
* @dev Authorizes a creator to create bounties
|
|
* @param _creator Address to authorize
|
|
*/
|
|
function authorizeCreator(address _creator) external onlyOwner {
|
|
require(_creator != address(0), "Invalid address");
|
|
require(!isAuthorizedCreator(_creator), "Already authorized");
|
|
|
|
authorizedCreators.push(_creator);
|
|
bounties[0].authorizedSubmitters[_creator] = true; // Use bounty 0 as storage
|
|
}
|
|
|
|
/**
|
|
* @dev Revokes creator authorization
|
|
* @param _creator Address to revoke
|
|
*/
|
|
function revokeCreator(address _creator) external onlyOwner {
|
|
require(isAuthorizedCreator(_creator), "Not authorized");
|
|
|
|
bounties[0].authorizedSubmitters[_creator] = false; // Use bounty 0 as storage
|
|
|
|
// Remove from array
|
|
for (uint256 i = 0; i < authorizedCreators.length; i++) {
|
|
if (authorizedCreators[i] == _creator) {
|
|
authorizedCreators[i] = authorizedCreators[authorizedCreators.length - 1];
|
|
authorizedCreators.pop();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @dev Updates fee percentages
|
|
* @param _creationFee New creation fee percentage
|
|
* @param _successFee New success fee percentage
|
|
* @param _platformFee New platform fee percentage
|
|
*/
|
|
function updateFees(
|
|
uint256 _creationFee,
|
|
uint256 _successFee,
|
|
uint256 _platformFee
|
|
) external onlyOwner {
|
|
require(_creationFee <= 500, "Creation fee too high"); // Max 5%
|
|
require(_successFee <= 500, "Success fee too high"); // Max 5%
|
|
require(_platformFee <= 500, "Platform fee too high"); // Max 5%
|
|
|
|
creationFeePercentage = _creationFee;
|
|
successFeePercentage = _successFee;
|
|
platformFeePercentage = _platformFee;
|
|
}
|
|
|
|
/**
|
|
* @dev Updates tier requirements
|
|
* @param _tier Bounty tier
|
|
* @param _minimumReward New minimum reward
|
|
*/
|
|
function updateTierRequirement(BountyTier _tier, uint256 _minimumReward) external onlyOwner {
|
|
tierRequirements[_tier] = _minimumReward;
|
|
}
|
|
|
|
// View functions
|
|
|
|
/**
|
|
* @dev Gets bounty details
|
|
* @param _bountyId Bounty ID
|
|
*/
|
|
function getBounty(uint256 _bountyId) external view bountyExists(_bountyId) returns (
|
|
string memory title,
|
|
string memory description,
|
|
uint256 rewardAmount,
|
|
address creator,
|
|
BountyTier tier,
|
|
BountyStatus status,
|
|
bytes32 performanceCriteria,
|
|
uint256 minAccuracy,
|
|
uint256 deadline,
|
|
uint256 creationTime,
|
|
uint256 maxSubmissions,
|
|
uint256 submissionCount,
|
|
bool requiresZKProof
|
|
) {
|
|
Bounty storage bounty = bounties[_bountyId];
|
|
return (
|
|
bounty.title,
|
|
bounty.description,
|
|
bounty.rewardAmount,
|
|
bounty.creator,
|
|
bounty.tier,
|
|
bounty.status,
|
|
bounty.performanceCriteria,
|
|
bounty.minAccuracy,
|
|
bounty.deadline,
|
|
bounty.creationTime,
|
|
bounty.maxSubmissions,
|
|
bounty.submissionCount,
|
|
bounty.requiresZKProof
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @dev Gets submission details
|
|
* @param _submissionId Submission ID
|
|
*/
|
|
function getSubmission(uint256 _submissionId) external view returns (
|
|
uint256 bountyId,
|
|
address submitter,
|
|
bytes32 performanceHash,
|
|
uint256 accuracy,
|
|
uint256 responseTime,
|
|
uint256 submissionTime,
|
|
SubmissionStatus status,
|
|
address verifier
|
|
) {
|
|
Submission storage submission = submissions[_submissionId];
|
|
return (
|
|
submission.bountyId,
|
|
submission.submitter,
|
|
submission.performanceHash,
|
|
submission.accuracy,
|
|
submission.responseTime,
|
|
submission.submissionTime,
|
|
submission.status,
|
|
submission.verifier
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @dev Gets all submissions for a bounty
|
|
* @param _bountyId Bounty ID
|
|
*/
|
|
function getBountySubmissions(uint256 _bountyId) external view bountyExists(_bountyId) returns (uint256[] memory) {
|
|
return bountySubmissions[_bountyId];
|
|
}
|
|
|
|
/**
|
|
* @dev Gets all bounties created by a user
|
|
* @param _creator Creator address
|
|
*/
|
|
function getCreatorBounties(address _creator) external view returns (uint256[] memory) {
|
|
return creatorBounties[_creator];
|
|
}
|
|
|
|
/**
|
|
* @dev Gets all submissions by a user
|
|
* @param _submitter Submitter address
|
|
*/
|
|
function getUserSubmissions(address _submitter) external view returns (uint256[] memory) {
|
|
return userSubmissions[_submitter];
|
|
}
|
|
|
|
/**
|
|
* @dev Gets all active bounty IDs
|
|
*/
|
|
function getActiveBounties() external view returns (uint256[] memory) {
|
|
return activeBountyIds;
|
|
}
|
|
|
|
/**
|
|
* @dev Gets bounty statistics
|
|
*/
|
|
function getBountyStats() external view returns (BountyStats memory) {
|
|
uint256 totalValue = 0;
|
|
uint256 activeCount = 0;
|
|
uint256 completedCount = 0;
|
|
|
|
for (uint256 i = 0; i < bountyCounter; i++) {
|
|
if (bounties[i].status == BountyStatus.ACTIVE) {
|
|
activeCount++;
|
|
totalValue += bounties[i].rewardAmount;
|
|
} else if (bounties[i].status == BountyStatus.COMPLETED) {
|
|
completedCount++;
|
|
totalValue += bounties[i].rewardAmount;
|
|
}
|
|
}
|
|
|
|
uint256 avgReward = bountyCounter > 0 ? totalValue / bountyCounter : 0;
|
|
uint256 successRate = completedCount > 0 ? (completedCount * 100) / bountyCounter : 0;
|
|
|
|
return BountyStats({
|
|
totalBounties: bountyCounter,
|
|
activeBounties: activeCount,
|
|
completedBounties: completedCount,
|
|
totalValueLocked: totalValue,
|
|
averageReward: avgReward,
|
|
successRate: successRate
|
|
});
|
|
}
|
|
|
|
/**
|
|
* @dev Checks if an address is authorized to create bounties
|
|
* @param _creator Address to check
|
|
*/
|
|
function isAuthorizedCreator(address _creator) public view returns (bool) {
|
|
return bounties[0].authorizedSubmitters[_creator]; // Use bounty 0 as storage
|
|
}
|
|
|
|
// Internal functions
|
|
|
|
function _verifySubmission(uint256 _bountyId, uint256 _submissionId) internal {
|
|
Bounty storage bounty = bounties[_bountyId];
|
|
Submission storage submission = submissions[_submissionId];
|
|
|
|
// Verify ZK-proof using PerformanceVerifier
|
|
bool proofValid = performanceVerifier.verifyPerformanceProof(
|
|
0, // Use dummy agreement ID for bounty verification
|
|
submission.responseTime,
|
|
submission.accuracy,
|
|
95, // Default availability
|
|
100, // Default compute power
|
|
submission.zkProof
|
|
);
|
|
|
|
if (proofValid && submission.accuracy >= bounty.minAccuracy) {
|
|
submission.status = SubmissionStatus.VERIFIED;
|
|
_completeBounty(_bountyId, _submissionId);
|
|
} else {
|
|
submission.status = SubmissionStatus.REJECTED;
|
|
}
|
|
}
|
|
|
|
function _completeBounty(uint256 _bountyId, uint256 _submissionId) internal {
|
|
Bounty storage bounty = bounties[_bountyId];
|
|
Submission storage submission = submissions[_submissionId];
|
|
|
|
require(bounty.status == BountyStatus.ACTIVE || bounty.status == BountyStatus.SUBMITTED, "Bounty not active");
|
|
|
|
bounty.status = BountyStatus.COMPLETED;
|
|
bounty.winningSubmission = submission.submitter;
|
|
|
|
// Calculate fees
|
|
uint256 successFee = (bounty.rewardAmount * successFeePercentage) / 10000;
|
|
uint256 platformFee = (bounty.rewardAmount * platformFeePercentage) / 10000;
|
|
uint256 totalFees = successFee + platformFee;
|
|
uint256 winnerReward = bounty.rewardAmount - totalFees;
|
|
|
|
// Transfer reward to winner
|
|
if (winnerReward > 0) {
|
|
require(aitbcToken.transfer(submission.submitter, winnerReward), "Reward transfer failed");
|
|
}
|
|
|
|
// Transfer fees to treasury
|
|
if (totalFees > 0) {
|
|
require(aitbcToken.transfer(owner(), totalFees), "Fee transfer failed");
|
|
emit PlatformFeeCollected(_bountyId, totalFees, owner());
|
|
}
|
|
|
|
// Remove from active bounties
|
|
_removeFromActiveBounties(_bountyId);
|
|
|
|
emit BountyCompleted(_bountyId, submission.submitter, winnerReward, block.timestamp);
|
|
}
|
|
|
|
function _removeFromActiveBounties(uint256 _bountyId) internal {
|
|
for (uint256 i = 0; i < activeBountyIds.length; i++) {
|
|
if (activeBountyIds[i] == _bountyId) {
|
|
activeBountyIds[i] = activeBountyIds[activeBountyIds.length - 1];
|
|
activeBountyIds.pop();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @dev Emergency pause function
|
|
*/
|
|
function pause() external onlyOwner {
|
|
_pause();
|
|
}
|
|
|
|
/**
|
|
* @dev Unpause function
|
|
*/
|
|
function unpause() external onlyOwner {
|
|
_unpause();
|
|
}
|
|
}
|