# Base Learn Roles - 5 > Base Learn Supreme Role Contract Codes **Published by:** [timurlenk](https://paragraph.com/@timurlenk/) **Published on:** 2025-09-21 **Categories:** base **URL:** https://paragraph.com/@timurlenk/base-learn-roles-5 ## Content SCD ERC721 Pin NFT Contract Code: // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.23; import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; contract Submission is ERC721 { struct Haiku { address author; string line1; string line2; string line3; } uint public counter; mapping(uint256 => Haiku) private haikus; mapping(bytes32 => bool) private line1Hashes; mapping(address => uint256[]) private sharedHaikus; error HaikuNotUnique(); error NotYourHaiku(uint256 tokenId); error NoHaikusShared(); constructor() ERC721("HaikuNFT", "HNK") {} function mintHaiku( string memory _line1, string memory _line2, string memory _line3 ) external { bytes32 line1Hash = keccak256(abi.encodePacked(_line1)); if (line1Hashes[line1Hash]) { revert HaikuNotUnique(); } line1Hashes[line1Hash] = true; uint256 id = counter; haikus[id] = Haiku(msg.sender, _line1, _line2, _line3); _mint(msg.sender, id); counter++; } function shareHaiku(uint256 _id, address _to) external { if (ownerOf(_id) != msg.sender) { revert NotYourHaiku(_id); } sharedHaikus[_to].push(_id); } function getMySharedHaikus() external view returns (Haiku[] memory) { uint256[] memory ids = sharedHaikus[msg.sender]; if (ids.length == 0) { revert NoHaikusShared(); } Haiku[] memory result = new Haiku[](ids.length); for (uint256 i = 0; i < ids.length; i++) { result[i] = haikus[ids[i]]; } return result; } } Minimal Token Pin NFT Contract Code: // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.23; contract Submission { mapping(address => uint256) public balances; mapping(address => bool) private hasClaimed; uint256 public totalClaimed; error TokensClaimed(); error UnsafeTransfer(address to); error InsufficientBalance(uint256 available, uint256 required); function claim() external { if (hasClaimed[msg.sender]) { revert TokensClaimed(); } hasClaimed[msg.sender] = true; balances[msg.sender] += 1000; totalClaimed += 1000; } function safeTransfer(address _to, uint256 _amount) external { if (_to == address(0)) { revert UnsafeTransfer(_to); } if (_to.code.length > 0) { if (_to.balance == 0) { revert UnsafeTransfer(_to); } } if (balances[msg.sender] < _amount) { revert InsufficientBalance(balances[msg.sender], _amount); } balances[msg.sender] -= _amount; balances[_to] += _amount; } function totalSupply() external view returns (uint256) { return totalClaimed; } } ERC20 Pin NFT Contract Code: // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.23; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; contract Submission is ERC20 { using EnumerableSet for EnumerableSet.AddressSet; struct Issue { EnumerableSet.AddressSet voters; string issueDesc; uint256 votesFor; uint256 votesAgainst; uint256 votesAbstain; uint256 totalVotes; uint256 quorum; bool passed; bool closed; } struct ReturnableIssue { address[] voters; string issueDesc; uint256 votesFor; uint256 votesAgainst; uint256 votesAbstain; uint256 totalVotes; uint256 quorum; bool passed; bool closed; } enum Vote { AGAINST, FOR, ABSTAIN } mapping(address => bool) private hasClaimed; Issue[] private issues; error TokensClaimed(); error NoTokensHeld(); error AlreadyVoted(); constructor() ERC20("VoteToken", "VTK") {} function claim() external { if (hasClaimed[msg.sender]) { revert TokensClaimed(); } hasClaimed[msg.sender] = true; _mint(msg.sender, 100); } function createIssue( string memory _issueDesc, uint256 _quorum ) external returns (uint256) { if (balanceOf(msg.sender) == 0) { revert NoTokensHeld(); } Issue storage newIssue = issues.push(); newIssue.issueDesc = _issueDesc; newIssue.quorum = _quorum; return issues.length - 1; } function getIssue( uint256 _id ) external view returns (ReturnableIssue memory) { Issue storage issue = issues[_id]; return ReturnableIssue( issue.voters.values(), issue.issueDesc, issue.votesFor, issue.votesAgainst, issue.votesAbstain, issue.totalVotes, issue.quorum, issue.passed, issue.closed ); } function vote(uint256 _issueId, Vote _vote) external { Issue storage issueToVote = issues[_issueId]; if (issueToVote.voters.contains(msg.sender)) { revert AlreadyVoted(); } uint256 voteWeight = balanceOf(msg.sender); issueToVote.voters.add(msg.sender); if (_vote == Vote.FOR) { issueToVote.votesFor += voteWeight; } else if (_vote == Vote.AGAINST) { issueToVote.votesAgainst += voteWeight; } else { issueToVote.votesAbstain += voteWeight; } issueToVote.totalVotes += voteWeight; if (issueToVote.totalVotes >= issueToVote.quorum) { issueToVote.closed = true; if (issueToVote.votesFor > issueToVote.votesAgainst) { issueToVote.passed = true; } } } } ## Publication Information - [timurlenk](https://paragraph.com/@timurlenk/): Publication homepage - [All Posts](https://paragraph.com/@timurlenk/): More posts from this publication - [RSS Feed](https://api.paragraph.com/blogs/rss/@timurlenk): Subscribe to updates - [Twitter](https://twitter.com/timurwp1): Follow on Twitter