```solidity=
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {ERC1155Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC1155/ERC1155Upgradeable.sol";
import {ERC1155BurnableUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC1155/extensions/ERC1155BurnableUpgradeable.sol";
import {ERC1155PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC1155/extensions/ERC1155PausableUpgradeable.sol";
import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import {TokenUtils} from "./libraries/TokenUtils.sol";
import {IVerifier} from "./interfaces/IVerifier.sol";
import {CommitProtocolBase} from "./CommitProtocolBase.sol";
contract CommitProtocol is CommitProtocolBase {
event Created(uint256 commitId, Commit config);
event Funded(
uint256 commitId,
address funder,
address token,
uint256 amount
);
event Joined(uint256 commitId, address participant);
event Verified(uint256 commitId, address participant, bool isVerified);
event Claimed(
uint256 commitId,
address participant,
address token,
uint256 amount
);
event Withdraw(address recipient, address token, uint256 amount);
struct Commit {
address owner;
// Commit details
string metadataURI; // { title, image, description, tags } - Use a standard NFT format
// Commit period
uint256 joinBefore;
uint256 verifyBefore;
uint256 maxParticipants; // (Optional) Limit how many participants can join (this just sets the ERC721 supply)
// Verifiers
Verifier joinVerifier;
Verifier fulfillVerifier;
// Stake
address token;
uint256 stake; // Cost to join Commit
// Fees
Fee creatorFee;
Fee clientFee;
}
struct Verifier {
address target;
bytes data;
}
struct Fee {
address recipient;
uint256 fee;
uint256 shareBps;
}
struct ClientConfig {
address recipient;
uint256 fee;
uint256 share;
}
struct Milestone {
uint256 deadline; // Timestamp when participant must verify before
string metadataURI; // (Optional) Details about milestone
}
uint256 public commitIds;
mapping(uint256 => Commit) public commits;
enum ParticipantStatus {
init,
joined,
verified,
claimed
}
Fee public protocolFee;
mapping(uint256 => mapping(address => ParticipantStatus))
public participants;
mapping(address => mapping(address => uint256)) public claims;
mapping(address => mapping(uint256 => uint256)) public funds;
mapping(address => mapping(uint256 => uint256)) public rewards;
mapping(uint256 => uint256) public verifiedCount;
function create(Commit calldata config) public payable whenNotPaused {
uint256 commitId = commitIds++;
commits[commitId] = config;
emit Created(commitId, config);
}
function fund(
uint256 commitId,
address token,
uint256 amount
) public payable whenNotPaused {
funds[token][commitId] += amount;
TokenUtils.transferFrom(token, _msgSender(), address(this), amount);
emit Funded(commitId, _msgSender(), token, amount);
}
function join(
uint256 commitId,
address referral, // Transfer referral bonus from sharing link to Commit
bytes calldata data
) public payable nonReentrant {
require(
participants[commitId][_msgSender()] == ParticipantStatus.init,
"Already joined"
);
participants[commitId][_msgSender()] = ParticipantStatus.joined;
Commit memory commit = getCommit(commitId);
require(block.timestamp < commit.joinBefore, "Join period ended");
// Check the conditions to join the Commit (ie token holdings, attestation, signature, etc)
if (commit.joinVerifier.target != address(0)) {
bool verified = IVerifier(commit.joinVerifier.target).verify(
_msgSender(),
commit.joinVerifier.data,
data
);
require(verified, "Not verified to join");
}
// Transfer stake
uint256 totalAmount = commit.stake +
commit.creatorFee.fee +
commit.clientFee.fee;
funds[commit.token][commitId] += commit.stake;
TokenUtils.transferFrom(
commit.token,
_msgSender(),
address(this),
totalAmount
);
// Transfer fees
uint256 creatorFee = commit.creatorFee.fee;
uint256 clientFee = commit.clientFee.fee;
uint256 fee = protocolFee.fee;
claims[commit.token][commit.creatorFee.recipient] += creatorFee;
claims[commit.token][commit.clientFee.recipient] += clientFee;
claims[commit.token][protocolFee.recipient] += fee;
// Mint ERC1155 NFT
_mint(_msgSender(), commitId, 1, "");
emit Joined(commitId, _msgSender());
}
function verify(
uint256 commitId,
address participant,
bytes calldata data
) public payable returns (bool) {
require(
participants[commitId][participant] == ParticipantStatus.joined,
"Already verified"
);
Commit memory commit = getCommit(commitId);
require(
block.timestamp < commit.verifyBefore,
"Verification period ended"
);
// Check the conditions to claim the Commit rewards (ie token holdings, attestation, signature, etc)
bool verified = IVerifier(commit.fulfillVerifier.target).verify(
participant,
commit.fulfillVerifier.data,
data
);
// Mark as verified
if (verified) {
participants[commitId][participant] = ParticipantStatus.verified;
}
emit Verified(commitId, participant, verified);
verifiedCount[commitId]++;
return verified;
}
function claim(
uint256 commitId,
address token
) public payable nonReentrant {
require(
participants[commitId][_msgSender()] == ParticipantStatus.verified,
"Must be verified"
);
participants[commitId][_msgSender()] = ParticipantStatus.claimed;
Commit memory commit = getCommit(commitId);
require(block.timestamp > commit.verifyBefore, "Still verifying");
uint256 amount = rewards[token][commitId];
if (amount == 0) {
calculate(commitId, token);
}
TokenUtils.transfer(commit.token, _msgSender(), amount);
emit Claimed(commitId, _msgSender(), commit.token, amount);
}
function calculate(uint256 commitId, address token) public {
require(verifiedCount[commitId] > 0, "No verified participants");
Commit memory commit = getCommit(commitId);
uint256 amount = funds[token][commitId];
uint256 creatorShare = (amount * commit.creatorFee.shareBps) / 10000;
uint256 clientShare = (amount * commit.clientFee.shareBps) / 10000;
uint256 protocolShare = (amount * protocolFee.shareBps) / 10000;
claims[commit.token][commit.creatorFee.recipient] += creatorShare;
claims[commit.token][commit.clientFee.recipient] += clientShare;
claims[commit.token][protocolFee.recipient] += protocolShare;
uint256 rewardsPool = amount -
creatorShare -
clientShare -
protocolShare;
funds[token][commitId] = 0;
rewards[token][commitId] = rewardsPool / verifiedCount[commitId];
}
function withdraw(address token) public payable nonReentrant {
uint256 amount = claims[token][_msgSender()];
claims[token][_msgSender()] = 0;
TokenUtils.transfer(token, _msgSender(), amount);
emit Withdraw(_msgSender(), token, amount);
}
function getCommit(
uint256 commitId
) public view returns (Commit memory commit) {
require(commitId < commitIds, "Commit not found");
return commits[commitId];
}
function approveToken(address token, bool isApproved) public onlyOwner {}
}
```