Skip to content

Commit

Permalink
Completed todo list items
Browse files Browse the repository at this point in the history
  • Loading branch information
Admin committed Nov 29, 2024
1 parent 3fc15e0 commit ecd5611
Show file tree
Hide file tree
Showing 12 changed files with 1,186 additions and 105 deletions.
35 changes: 35 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -181,3 +181,38 @@ Contributions are welcome! Please read our contributing guidelines before submit
## License

This project is licensed under the MIT License.

## TODO

### UserProfile Contract
- [x] Add access control to updateReputation function
- [x] Implement rate limiting for profile updates
- [x] Add profile verification system
- [x] Create profile indexing for efficient querying
- [x] Add profile metadata standards for better interoperability
- [x] Implement profile recovery mechanism

### LiquidityPool Contract
- [x] Add slippage protection for swaps
- [x] Implement emergency withdrawal mechanism
- [x] Add events for pool state changes

### Governance Contract
- [x] Add proposal execution timelock
- [x] Implement delegate voting
- [x] Add proposal cancellation mechanism

### Badge Contract
- [x] Add badge metadata and URI standards
- [x] Implement badge revoking mechanism
- [x] Add badge progression system

### QuadraticFunding Contract
- [x] Add round cancellation mechanism
- [x] Implement matching pool contribution mechanism
- [x] Add contribution verification and validation
- [x] Implement quadratic funding calculation formula
- [x] Create round creation and configuration system
- [x] Add participant eligibility verification
- [x] Implement fund distribution mechanism
- [x] Create reporting and analytics system
102 changes: 97 additions & 5 deletions src/Badge.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@
pragma solidity ^0.8.13;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

/**
* @title Badge
* @dev NFT-based badge system for platform achievements
*/
contract Badge is ERC721, Ownable {
contract Badge is ERC721URIStorage, Ownable {
uint256 private _tokenIds;

// Badge types and their requirements
Expand All @@ -17,19 +18,37 @@ contract Badge is ERC721, Ownable {
POWER_BACKER, // Backed more than 5 projects
LIQUIDITY_PROVIDER, // Provided significant liquidity
GOVERNANCE_ACTIVE // Participated in multiple proposals
}

// Badge progression tiers
enum BadgeTier {
BRONZE,
SILVER,
GOLD,
PLATINUM
}

// Mapping from token ID to badge type
mapping(uint256 => BadgeType) public badgeTypes;

// Mapping from token ID to badge tier
mapping(uint256 => BadgeTier) public badgeTiers;

// Mapping from address to badge type to whether they have earned it
mapping(address => mapping(BadgeType => bool)) public hasBadge;

// Mapping from badge type to its benefits multiplier (in basis points, 100 = 1%)
mapping(BadgeType => uint256) public badgeBenefits;

event BadgeAwarded(address indexed recipient, BadgeType badgeType, uint256 tokenId);
// Mapping from badge type to tier requirements (e.g., number of actions needed)
mapping(BadgeType => mapping(BadgeTier => uint256)) public tierRequirements;

// Mapping from address to badge type to number of qualifying actions
mapping(address => mapping(BadgeType => uint256)) public userActions;

event BadgeAwarded(address indexed recipient, BadgeType badgeType, BadgeTier tier, uint256 tokenId);
event BadgeRevoked(address indexed holder, uint256 tokenId);
event BadgeProgressed(address indexed holder, uint256 tokenId, BadgeTier newTier);
event BenefitUpdated(BadgeType badgeType, uint256 newBenefit);

constructor() ERC721("Platform Achievement Badge", "BADGE") Ownable() {
Expand All @@ -39,24 +58,97 @@ contract Badge is ERC721, Ownable {
badgeBenefits[BadgeType.POWER_BACKER] = 1000; // 10% discount
badgeBenefits[BadgeType.LIQUIDITY_PROVIDER] = 1500; // 15% discount
badgeBenefits[BadgeType.GOVERNANCE_ACTIVE] = 750; // 7.5% discount

// Set tier requirements
tierRequirements[BadgeType.POWER_BACKER][BadgeTier.BRONZE] = 5;
tierRequirements[BadgeType.POWER_BACKER][BadgeTier.SILVER] = 10;
tierRequirements[BadgeType.POWER_BACKER][BadgeTier.GOLD] = 20;
tierRequirements[BadgeType.POWER_BACKER][BadgeTier.PLATINUM] = 50;

tierRequirements[BadgeType.GOVERNANCE_ACTIVE][BadgeTier.BRONZE] = 3;
tierRequirements[BadgeType.GOVERNANCE_ACTIVE][BadgeTier.SILVER] = 10;
tierRequirements[BadgeType.GOVERNANCE_ACTIVE][BadgeTier.GOLD] = 25;
tierRequirements[BadgeType.GOVERNANCE_ACTIVE][BadgeTier.PLATINUM] = 100;
}

/**
* @dev Award a badge to an address
* @dev Award a badge to an address with metadata
* @param recipient Address to receive the badge
* @param badgeType Type of badge to award
* @param uri Metadata URI for the badge
*/
function awardBadge(address recipient, BadgeType badgeType) external onlyOwner {
function awardBadge(
address recipient,
BadgeType badgeType,
string memory uri
) external onlyOwner {
require(!hasBadge[recipient][badgeType], "Badge already awarded");

_tokenIds++;
uint256 newTokenId = _tokenIds;

_safeMint(recipient, newTokenId);
_setTokenURI(newTokenId, uri);

badgeTypes[newTokenId] = badgeType;
badgeTiers[newTokenId] = BadgeTier.BRONZE;
hasBadge[recipient][badgeType] = true;

emit BadgeAwarded(recipient, badgeType, newTokenId);
emit BadgeAwarded(recipient, badgeType, BadgeTier.BRONZE, newTokenId);
}

/**
* @dev Revoke a badge from an address
* @param tokenId ID of the badge to revoke
*/
function revokeBadge(uint256 tokenId) external onlyOwner {
address holder = ownerOf(tokenId);
BadgeType badgeType = badgeTypes[tokenId];

_burn(tokenId);
hasBadge[holder][badgeType] = false;
delete badgeTypes[tokenId];
delete badgeTiers[tokenId];

emit BadgeRevoked(holder, tokenId);
}

/**
* @dev Record an action for a user that counts towards badge progression
* @param user Address of the user
* @param badgeType Type of badge to record action for
*/
function recordAction(address user, BadgeType badgeType) external onlyOwner {
userActions[user][badgeType]++;

// Check if user has the badge and can progress to next tier
if (hasBadge[user][badgeType]) {
uint256 tokenId = getUserBadgeTokenId(user, badgeType);
BadgeTier currentTier = badgeTiers[tokenId];

if (currentTier != BadgeTier.PLATINUM) {
BadgeTier nextTier = BadgeTier(uint256(currentTier) + 1);
if (userActions[user][badgeType] >= tierRequirements[badgeType][nextTier]) {
badgeTiers[tokenId] = nextTier;
emit BadgeProgressed(user, tokenId, nextTier);
}
}
}
}

/**
* @dev Get the token ID of a user's badge for a specific type
* @param user Address of the badge holder
* @param badgeType Type of badge to look up
* @return tokenId of the badge, or 0 if not found
*/
function getUserBadgeTokenId(address user, BadgeType badgeType) public view returns (uint256) {
for (uint256 i = 1; i <= _tokenIds; i++) {
if (_exists(i) && ownerOf(i) == user && badgeTypes[i] == badgeType) {
return i;
}
}
return 0;
}

/**
Expand Down
106 changes: 105 additions & 1 deletion src/Governance.sol
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,17 @@ contract Governance is Ownable, ReentrancyGuard {

mapping(uint256 => Proposal) public proposals;
mapping(uint256 => uint256) public proposalExecutionTime;
mapping(address => address) public delegates;
mapping(address => uint256) public delegatedAmount; // Track total amount delegated to an address
mapping(address => address[]) private delegators; // Track who has delegated to an address

event ProposalCreated(
uint256 indexed proposalId, address indexed proposer, string description, uint256 startTime, uint256 endTime
);
event VoteCast(address indexed voter, uint256 indexed proposalId, bool support, uint256 weight);
event ProposalExecuted(uint256 indexed proposalId);
event DelegateChanged(address indexed delegator, address indexed fromDelegate, address indexed toDelegate);
event ProposalCancelled(uint256 indexed proposalId);

constructor(address _platformToken) Ownable() {
platformToken = IERC20(_platformToken);
Expand Down Expand Up @@ -70,6 +75,105 @@ contract Governance is Ownable, ReentrancyGuard {
emit ProposalCreated(proposalCount, msg.sender, description, startTime, endTime);
}

/**
* @dev Delegate voting power to another address
* @param delegatee Address to delegate voting power to
*/
function delegate(address delegatee) external {
require(delegatee != address(0), "Cannot delegate to zero address");
require(delegatee != msg.sender, "Cannot delegate to self");

address currentDelegate = delegates[msg.sender];
uint256 balance = platformToken.balanceOf(msg.sender);

// Remove delegation from current delegate if exists
if (currentDelegate != address(0)) {
delegatedAmount[currentDelegate] = delegatedAmount[currentDelegate] - balance;
}

// Add delegation to new delegatee
delegatedAmount[delegatee] = delegatedAmount[delegatee] + balance;
delegates[msg.sender] = delegatee;

emit DelegateChanged(msg.sender, currentDelegate, delegatee);

// Update voting power snapshots for all active proposals
for (uint256 i = 1; i <= proposalCount; i++) {
Proposal storage proposal = proposals[i];
if (block.timestamp <= proposal.endTime && !proposal.executed) {
// Update delegator's snapshot if they haven't voted
if (!proposal.hasVoted[msg.sender]) {
proposal.votingPowerSnapshot[msg.sender] = 0;
}
// Update delegatee's snapshot if they haven't voted
if (!proposal.hasVoted[delegatee]) {
proposal.votingPowerSnapshot[delegatee] = getVotingPower(delegatee);
}
if (currentDelegate != address(0) && !proposal.hasVoted[currentDelegate]) {
proposal.votingPowerSnapshot[currentDelegate] = getVotingPower(currentDelegate);
}
}
}
}

/**
* @dev Remove a delegator from a delegatee's list
* @param delegatee The address to remove the delegator from
* @param delegator The delegator to remove
*/
function _removeDelegator(address delegatee, address delegator) internal {
address[] storage dels = delegators[delegatee];
for (uint256 i = 0; i < dels.length; i++) {
if (dels[i] == delegator) {
// Move the last element to this position and pop
dels[i] = dels[dels.length - 1];
dels.pop();
break;
}
}
}

/**
* @dev Get total voting power of an address (including delegated power)
* @param account Address to check voting power for
* @return Total voting power
*/
function getVotingPower(address account) public view returns (uint256) {
// If account has delegated their power, they have 0 voting power
if (delegates[account] != address(0)) {
return 0;
}

// Get their token balance
uint256 tokenBalance = platformToken.balanceOf(account);

// Add any delegated amount
uint256 delegatedPower = delegatedAmount[account];

return tokenBalance + delegatedPower;
}

/**
* @dev Cancel a proposal (only proposer or owner can cancel)
* @param proposalId ID of the proposal to cancel
*/
function cancelProposal(uint256 proposalId) external {
Proposal storage proposal = proposals[proposalId];
require(!proposal.executed, "Proposal already executed");
require(
msg.sender == proposal.proposer || msg.sender == owner(),
"Only proposer or owner can cancel"
);
require(block.timestamp <= proposal.endTime, "Voting period ended");

// Reset voting data
proposal.forVotes = 0;
proposal.againstVotes = 0;
proposal.executed = true; // Mark as executed to prevent future voting/execution

emit ProposalCancelled(proposalId);
}

/**
* @dev Cast a vote on a proposal
* @param proposalId ID of the proposal
Expand All @@ -82,7 +186,7 @@ contract Governance is Ownable, ReentrancyGuard {

// Take snapshot of voting power if not already taken
if (proposal.votingPowerSnapshot[msg.sender] == 0) {
proposal.votingPowerSnapshot[msg.sender] = platformToken.balanceOf(msg.sender);
proposal.votingPowerSnapshot[msg.sender] = getVotingPower(msg.sender);
}

uint256 votes = proposal.votingPowerSnapshot[msg.sender];
Expand Down
Loading

0 comments on commit ecd5611

Please sign in to comment.