The BadgeMarketplace contract enables users to trade their achievement badges and supports time-limited badge features. It provides a secure way to list, purchase, and manage badge transfers on the platform.
- Badge Listing Management
- Secure Purchase System
- Time-Limited Badge Support
- Active Listings Query
To list a badge for sale, users must own the badge and approve the marketplace:
// First approve the marketplace
badge.approve(marketplaceAddress, tokenId);
// Then list the badge
marketplace.listBadge(
tokenId, // Badge token ID
price, // Price in wei
expiry // 0 for permanent, timestamp for time-limited
);
Important Notes:
- Only badge owners can list
- Marketplace must be approved for transfer
- Expiry of 0 means permanent badge
- Price is in wei (1 ether = 1e18 wei)
Users can purchase listed badges by sending the required payment:
// Purchase a badge
marketplace.purchaseBadge{value: price}(tokenId);
Purchase Process:
- Validates listing exists and payment sufficient
- Checks badge hasn't expired (for time-limited badges)
- Transfers badge ownership
- Sends payment to seller
- Refunds excess payment if any
Sellers can remove their badges from the marketplace:
marketplace.unlistBadge(tokenId);
The marketplace supports time-limited badges that expire after a certain timestamp:
// Check if a badge has expired
bool expired = marketplace.isBadgeExpired(tokenId);
Time-Limited Features:
- Set expiry when listing
- Cannot purchase expired badges
- Expiry status publicly queryable
- Permanent badges use expiry = 0
Get all active (non-expired) listings:
(
uint256[] memory tokenIds,
uint256[] memory prices,
address[] memory sellers,
uint256[] memory expiries
) = marketplace.getActiveListings();
Here's a complete example of integrating the marketplace:
contract MarketplaceUI {
BadgeMarketplace public marketplace;
Badge public badge;
function listMyBadge(
uint256 tokenId,
uint256 price,
uint256 expiry
) external {
// First approve marketplace
badge.approve(address(marketplace), tokenId);
// Then list badge
marketplace.listBadge(tokenId, price, expiry);
}
function buyBadge(uint256 tokenId) external payable {
// Get listing details
(address seller, uint256 price, uint256 expiry) = marketplace.listings(tokenId);
// Verify badge isn't expired
require(!marketplace.isBadgeExpired(tokenId), "Badge expired");
// Purchase badge
marketplace.purchaseBadge{value: price}(tokenId);
}
function displayActiveListings() external view returns (
uint256[] memory tokens,
uint256[] memory prices,
address[] memory sellers,
uint256[] memory expiries
) {
return marketplace.getActiveListings();
}
}
Monitor marketplace activity through these events:
event BadgeListed(uint256 indexed tokenId, address indexed seller, uint256 price, uint256 expiry);
event BadgeSold(uint256 indexed tokenId, address indexed seller, address indexed buyer, uint256 price);
event BadgeUnlisted(uint256 indexed tokenId);
event BadgeExpired(uint256 indexed tokenId);
-
Listing Management
- Verify badge ownership before listing
- Set reasonable prices
- Consider time-limited use cases
- Monitor BadgeListed events
-
Purchase Handling
- Check badge validity before purchase
- Verify price and expiry
- Handle failed transactions
- Monitor BadgeSold events
-
Time-Limited Badges
- Set appropriate expiry times
- Check expiry before interactions
- Monitor BadgeExpired events
- Consider timezone implications
-
Market Monitoring
- Track active listings
- Monitor price trends
- Watch for unusual activity
- Use events for updates
Here's how to test the marketplace functionality:
contract MarketplaceTest is Test {
BadgeMarketplace public marketplace;
Badge public badge;
address seller = address(0x1);
address buyer = address(0x2);
function setUp() public {
badge = new Badge();
marketplace = new BadgeMarketplace(address(badge));
// Mint a badge to seller
badge.awardBadge(
seller,
Badge.BadgeType.POWER_BACKER,
"ipfs://test"
);
}
function testBadgeSale() public {
vm.startPrank(seller);
// Approve and list badge
uint256 tokenId = 1;
uint256 price = 1 ether;
badge.approve(address(marketplace), tokenId);
marketplace.listBadge(tokenId, price, 0);
vm.stopPrank();
// Purchase badge
vm.startPrank(buyer);
vm.deal(buyer, 1 ether);
marketplace.purchaseBadge{value: price}(tokenId);
// Verify ownership transferred
assertEq(badge.ownerOf(tokenId), buyer);
}
}
-
Reentrancy Protection
- Uses OpenZeppelin's ReentrancyGuard
- Clears listing before transfer
- Handles payments last
-
Access Control
- Only owners can list/unlist
- Requires marketplace approval
- Ownership verified on listing
-
Payment Handling
- Secure payment transfers
- Excess payment refunding
- Failed transfer handling
-
Time Management
- Secure expiry checking
- Block timestamp usage
- Expired badge protection