# Base Learn

*Guild：https://guild.xyz/base/base-learn
REMIX ：https://remix.ethereum.org/*

By [Biewen](https://paragraph.com/@biewenw) · 2025-09-21

base, baselearn

---

  

**Base Learn Newcomer**

![](https://storage.googleapis.com/papyrus_images/2833910e33589ba88b72335d716f9cbc4d4192a9ad27b4a8b83cb6ff8e6ce5cd.png)

1、打开 [https://remix.ethereum.org/](https://remix.ethereum.org/) ，1.点击“prettierrc.json。2.点击左上角”创建文件夹。3.文件夹名字可以设置为Basic Contracts，然后回车保存

Open [https://remix.ethereum.org/](https://remix.ethereum.org/),

1.  Click on “prettierrc.json”.
    
2.  Click the top-left corner to create a folder.
    
3.  Name the folder “Basic Contracts” and press Enter to save.
    

![](https://storage.googleapis.com/papyrus_images/6b9689bc86413cc259859f44ce8df764e064d3b8ffe19c2eeaf70aea9a7ba081.png)

2、复制代码

2.  Copy the code.
    

    // SPDX-License-Identifier: UNLICENSED
    pragma solidity ^0.8.23;
    
    interface IBasicContractTest {
        function adder(
            uint _a,
            uint _b
        ) external returns (uint result, bool success);
    
        function subtractor(
            uint _a,
            uint _b
        ) external returns (uint result, bool success);
    }
    
    contract SafeMathContract is IBasicContractTest {
        function adder(
            uint _a,
            uint _b
        ) external pure override returns (uint result, bool success) {
            if (type(uint).max - _a < _b) {
                return (0, true);
            } else {
                result = _a + _b;
                return (result, false);
            }
        }
    
        function subtractor(
            uint _a,
            uint _b
        ) external pure override returns (uint result, bool success) {
            if (_b > _a) {
                return (0, true);
            } else {
                result = _a - _b;
                return (result, false);
            }
        }
    }
    

3、将复制的代码粘贴到创建的文件夹中，然后关闭提示。 然后Ctrl+S 保存，保存后你会在左侧看到“打钩”的绿色标记

3.  Paste the copied code into the folder you created, then close the prompt. Press Ctrl+S to save, and after saving, you will see a green checkmark on the left.
    

![](https://storage.googleapis.com/papyrus_images/f73d7e151e225dd5cd3f909eff5e8ed10c93a6b91774abdacd7ad66243eabef4.png)

![](https://storage.googleapis.com/papyrus_images/81ebc860bfeafa5a9d1e8b7b8dcfe94141800e484facade94e4f9962a8f0bfd7.png)

4、打开左侧的Deploy & run transactions，点击remix VM 链接你的钱包，如果你的钱包在上面没有，你可以根据下方图片的步骤来启用你的钱包，如果已经有metamask，可以直接链接

4.  Open **Deploy & Run Transactions** on the left, click **Remix VM** to connect your wallet. If your wallet isn’t listed, you can follow the steps in the image below to enable it. If you already have MetaMask, you can connect it directly.
    

![](https://storage.googleapis.com/papyrus_images/0160718159747aeded293c6ad78130e87eceae66b5562f26c3c4fdf7f60aa3cf.png)

  

5、在钱包中选择Base sepolia网络，必须是手动选择

5.  In your wallet, select the **Base Sepolia** network. It must be selected manually.
    

![](https://storage.googleapis.com/papyrus_images/9bcacf9d0cee4ae100f0ab7f785efd3e674fa0a8bdb78102b44768a1cc4c3177.png)

6、在Contract中选择图中这个，然后点击“Deploy”，需要在base sepolia网络支付gas fee。

6.  In **Contract**, select the one shown in the image, then click **Deploy**. You will need to pay a gas fee on the Base Sepolia network.
    

![](https://storage.googleapis.com/papyrus_images/c7a3ae97f75d707d35d1736e52454216b5f583829c32ee07f5ba9bf903d4206e.png)

7、交易成功后，你可以在tx里面复制 合约地址。

7.  After the transaction is successful, you can copy the contract address from **tx**.
    

![](https://storage.googleapis.com/papyrus_images/373c42ede1f83475fbde6991e272f6425cf72b0eff5f80788df61b34e565c7f0.png)

  

8、回到guild页面点击NFT的合约，然后到basescan中，验证你的合约。交易成功后回到页面guild页面刷新就行了，完成后我们可以在remix中把之前的创建的文件夹删了。后面的步骤都差不多

8.  Go back to the guild page and click on the NFT contract, then verify your contract on BaseScan. After the transaction is successful, return to the guild page and refresh. Once done, you can delete the folder you created in Remix. The following steps are similar.
    

![](https://storage.googleapis.com/papyrus_images/6e697f7347ae4c324b3717cfdd56e5a7156fcd65eddbdd960d76c175ea57d1c8.png)

![](https://storage.googleapis.com/papyrus_images/af4d9b9ad7b1a04d224a37789189b50f5e30b2bcf7857ff80fa55272aac4fa9a.png)

![](https://storage.googleapis.com/papyrus_images/af24120109c0be10ee47afe8704630f8af0ddee05c14198eeda5cf41ff613cc2.png)

  

### **Base Learn Acolyte**

**  
1、Own a(n) Storage Pin NFT**

    // SPDX-License-Identifier: UNLICENSED
    pragma solidity ^0.8.23;
    
    contract EmployeeStorage {
        string public name;
        uint public idNumber;
    
        uint24 private salary;
        uint16 private shares;
    
        constructor() {
            name = "Pat";
            idNumber = 112358132134;
            salary = 50000;
            shares = 1000;
        }
    
        function grantShares(uint16 _newShares) external {
            require(_newShares <= 5000, "Too many shares");
            shares += _newShares;
        }
    
        function checkForPacking(uint _slot) external view returns (uint result) {
            assembly {
                result := sload(_slot)
            }
        }
    
        function viewShares() external view returns (uint16) {
            return shares;
        }
    
        function viewSalary() external view returns (uint24) {
            return salary;
        }
    
        function debugResetShares() external {
            shares = 1000;
        }
    }
    

  

**2、Control Structures**

    // SPDX-License-Identifier: UNLICENSED
    pragma solidity ^0.8.23;
    
    contract ControlStructures {
        error AfterHours(uint256 time);
    
        function fizzBuzz(uint256 _number) external pure returns (string memory) {
            if (_number % 15 == 0) {
                return "FizzBuzz";
            } else if (_number % 3 == 0) {
                return "Fizz";
            } else if (_number % 5 == 0) {
                return "Buzz";
            } else {
                return "Splat";
            }
        }
    
        function doNotDisturb(uint256 _time) external pure returns (string memory) {
            assert(_time <= 2400);
    
            if (_time <= 800 || _time > 2200) {
                revert AfterHours(_time);
            }
    
            if (_time > 800 && _time <= 1200) {
                return "Morning!";
            } else if (_time > 1200 && _time <= 1800) {
                return "Afternoon!";
            } else {
                return "Evening!";
            }
        }
    }
    

  

**3、Arrays**

    // SPDX-License-Identifier: UNLICENSED
    pragma solidity ^0.8.23;
    
    contract Submission {
        uint[] private numbers;
        uint[] private timestamps;
        address[] private senders;
    
        constructor() {
            resetNumbers();
        }
    
        function resetNumbers() public {
            delete numbers;
            for (uint i = 1; i <= 10; i++) {
                numbers.push(i);
            }
        }
    
        function appendToNumbers(uint[] calldata _toAppend) external {
            for (uint i = 0; i < _toAppend.length; i++) {
                numbers.push(_toAppend[i]);
            }
        }
    
        function getNumbers() external view returns (uint[] memory) {
            return numbers;
        }
    
        function saveTimestamp(uint _unixTimestamp) external {
            timestamps.push(_unixTimestamp);
            senders.push(msg.sender);
        }
    
        function resetTimestamps() external {
            delete timestamps;
        }
    
        function resetSenders() external {
            delete senders;
        }
    
        function afterY2K() external view returns (uint[] memory, address[] memory) {
            uint count = 0;
            for (uint i = 0; i < timestamps.length; i++) {
                if (timestamps[i] >= 946702900) {
                    count++;
                }
            }
    
            uint[] memory filteredTimestamps = new uint[](count);
            address[] memory filteredAddresses = new address[](count);
            uint counter = 0;
    
            for (uint i = 0; i < timestamps.length; i++) {
                if (timestamps[i] >= 946702900) {
                    filteredTimestamps[counter] = timestamps[i];
                    filteredAddresses[counter] = senders[i];
                    counter++;
                }
            }
    
            return (filteredTimestamps, filteredAddresses);
        }
    }
    

  
  

### **Base Learn Consul**

**  
1、Mappings**

    // SPDX-License-Identifier: UNLICENSED
    pragma solidity ^0.8.23;
    
    interface ISubmission {
        function getApprovedRecords() external view returns (string[] memory);
    
        function addRecord(string memory _albumName) external;
    
        function getUserFavorites(
            address _address
        ) external view returns (string[] memory);
    
        function resetUserFavorites() external;
    }
    
    contract Submission is ISubmission {
        mapping(address => string[]) public userFavorites;
    
        function getApprovedRecords()
            external
            pure
            override
            returns (string[] memory)
        {
            string ;
            approved[0] = "Thriller";
            approved[1] = "Back in Black";
            approved[2] = "The Bodyguard";
            approved[3] = "The Dark Side of the Moon";
            approved[4] = "Their Greatest Hits (1971-1975)";
            approved[5] = "Hotel California";
            approved[6] = "Come On Over";
            approved[7] = "Rumours";
            approved[8] = "Saturday Night Fever";
            return approved;
        }
    
        function addRecord(string memory _albumName) external override {
            userFavorites[msg.sender].push(_albumName);
        }
    
        function getUserFavorites(
            address _address
        ) external view override returns (string[] memory) {
            return userFavorites[_address];
        }
    
        function resetUserFavorites() external override {
            delete userFavorites[msg.sender];
        }
    }
    

  

**2、Inheritance**

    // SPDX-License-Identifier: UNLICENSED
    pragma solidity ^0.8.23;
    
    interface IEmployee {
        function idNumber() external returns (uint);
        function managerId() external returns (uint);
    }
    
    interface ISalaried is IEmployee {
        function annualSalary() external returns (uint);
    }
    
    interface IHourly is IEmployee {
        function hourlyRate() external returns (uint);
    }
    
    interface ISalesPerson is IHourly {}
    
    interface IEngineeringManager is ISalaried {}
    
    interface IInheritanceSubmission {
        function salesPerson() external returns (address);
        function engineeringManager() external returns (address);
    }
    
    contract SalesPerson is ISalesPerson {
        function hourlyRate() external pure override returns (uint) {
            return 20;
        }
    
        function idNumber() external pure override returns (uint) {
            return 55555;
        }
    
        function managerId() external pure override returns (uint) {
            return 0;
        }
    }
    
    contract EngineeringManager is IEngineeringManager {
        function annualSalary() external pure override returns (uint) {
            return 200_000;
        }
    
        function managerId() external pure override returns (uint) {
            return 11111;
        }
    
        function idNumber() external pure override returns (uint) {
            return 0;
        }
    }
    
    contract InheritanceSubmission is IInheritanceSubmission {
        SalesPerson private _salesPerson;
        EngineeringManager private _engineeringManager;
    
        constructor() {
            _salesPerson = new SalesPerson();
            _engineeringManager = new EngineeringManager();
        }
    
        function salesPerson() external view override returns (address) {
            return address(_salesPerson);
        }
    
        function engineeringManager() external view override returns (address) {
            return address(_engineeringManager);
        }
    }
    

  

**3、Structs**

    // SPDX-License-Identifier: UNLICENSED
    pragma solidity ^0.8.23;
    
    interface ISubmission {
        function getApprovedRecords() external view returns (string[] memory);
    
        function addRecord(string memory _albumName) external;
    
        function getUserFavorites(
            address _address
        ) external view returns (string[] memory);
    
        function resetUserFavorites() external;
    }
    
    contract Submission is ISubmission {
        mapping(address => string[]) public userFavorites;
    
        function getApprovedRecords()
            external
            pure
            override
            returns (string[] memory)
        {
            string ;
            approved[0] = "Thriller";
            approved[1] = "Back in Black";
            approved[2] = "The Bodyguard";
            approved[3] = "The Dark Side of the Moon";
            approved[4] = "Their Greatest Hits (1971-1975)";
            approved[5] = "Hotel California";
            approved[6] = "Come On Over";
            approved[7] = "Rumours";
            approved[8] = "Saturday Night Fever";
            return approved;
        }
    
        function addRecord(string memory _albumName) external override {
            userFavorites[msg.sender].push(_albumName);
        }
    
        function getUserFavorites(
            address _address
        ) external view override returns (string[] memory) {
            return userFavorites[_address];
        }
    
        function resetUserFavorites() external override {
            delete userFavorites[msg.sender];
        }
    }
    

  

### **Base Learn Prefect**

**  
1、Error Triage**

    // SPDX-License-Identifier: MIT
    
    pragma solidity ^0.8.17;
    
    contract ErrorTriageExercise {
        /**
         * @dev Finds the difference between each uint with its neighbor (a to b, b to c, etc.)
         * and returns a uint array with the absolute integer difference of each pairing.
         * 
         * @param _a The first unsigned integer.
         * @param _b The second unsigned integer.
         * @param _c The third unsigned integer.
         * @param _d The fourth unsigned integer.
         * 
         * @return results An array containing the absolute differences between each pair of integers.
         */
        function diffWithNeighbor(
            uint _a,
            uint _b,
            uint _c,
            uint _d
        ) public pure returns (uint[] memory) {
            // Initialize an array to store the differences
            uint ;
    
            // Calculate the absolute difference between each pair of integers and store it in the results array
            results[0] = _a > _b ? _a - _b : _b - _a;
            results[1] = _b > _c ? _b - _c : _c - _b;
            results[2] = _c > _d ? _c - _d : _d - _c;
    
            // Return the array of differences
            return results;
        }
    
        /**
         * @dev Changes the base by the value of the modifier. Base is always >= 1000. Modifiers can be
         * between positive and negative 100.
         * 
         * @param _base The base value to be modified.
         * @param _modifier The value by which the base should be modified.
         * 
         * @return returnValue The modified value of the base.
         */
        function applyModifier(
            uint _base,
            int _modifier
        ) public pure returns (uint returnValue) {
            // Apply the modifier to the base value
            if(_modifier > 0) {
                return _base + uint(_modifier);
            }
            return _base - uint(-_modifier);
        }
    
    
        uint[] arr;
    
        function popWithReturn() public returns (uint returnNum) {
            if(arr.length > 0) {
                uint result = arr[arr.length - 1];
                arr.pop();
                return result;
            }
        }
    
        // The utility functions below are working as expected
        function addToArr(uint _num) public {
            arr.push(_num);
        }
    
        function getArr() public view returns (uint[] memory) {
            return arr;
        }
    
        function resetArr() public {
            delete arr;
        }
    }
    

  

**2、New Keyword：这个需要先部署两个合约，然后在Deploy的时候选择“other contract.sol”**

2.  **New Keyword**: You need to deploy two contracts first, then select **“other contract.sol”** when deploying.
    

AddressBook.sol

    // SPDX-License-Identifier: GPL-3.0
    pragma solidity ^0.8.8;
    
    import "@openzeppelin/contracts/access/Ownable.sol";
    
    contract AddressBook is Ownable {
        // Define a private salt value for internal use
        string private salt = "value"; 
    
        // Define a struct to represent a contact
        struct Contact {
            uint id; // Unique identifier for the contact
            string firstName; // First name of the contact
            string lastName; // Last name of the contact
            uint[] phoneNumbers; // Array to store multiple phone numbers for the contact
        }
    
        // Array to store all contacts
        Contact[] private contacts;
    
        // Mapping to store the index of each contact in the contacts array using its ID
        mapping(uint => uint) private idToIndex;
    
        // Variable to keep track of the ID for the next contact
        uint private nextId = 1;
    
        // Custom error for when a contact is not found
        error ContactNotFound(uint id);
    
        // 🔧 FIX: Constructor eklendi - Ownable için gerekli
        constructor() Ownable(msg.sender) {}
    
        // Function to add a new contact
        function addContact(string calldata firstName, string calldata lastName, uint[] calldata phoneNumbers) external onlyOwner {
            // Create a new contact with the provided details and add it to the contacts array
            contacts.push(Contact(nextId, firstName, lastName, phoneNumbers));
            // Map the ID of the new contact to its index in the array
            idToIndex[nextId] = contacts.length - 1;
            // Increment the nextId for the next contact
            nextId++;
        }
    
        // Function to delete a contact by its ID
        function deleteContact(uint id) external onlyOwner {
            // Get the index of the contact to be deleted
            uint index = idToIndex[id];
            // Check if the index is valid and if the contact with the provided ID exists
            if (index >= contacts.length || contacts[index].id != id) revert ContactNotFound(id);
    
            // Replace the contact to be deleted with the last contact in the array
            contacts[index] = contacts[contacts.length - 1];
            // Update the index mapping for the moved contact
            idToIndex[contacts[index].id] = index;
            // Remove the last contact from the array
            contacts.pop();
            // Delete the mapping entry for the deleted contact ID
            delete idToIndex[id];
        }
    
        // Function to retrieve a contact by its ID
        function getContact(uint id) external view returns (Contact memory) {
            // Get the index of the contact
            uint index = idToIndex[id];
            // Check if the index is valid and if the contact with the provided ID exists
            if (index >= contacts.length || contacts[index].id != id) revert ContactNotFound(id);
            // Return the contact details
            return contacts[index];
        }
    
        // Function to retrieve all contacts
        function getAllContacts() external view returns (Contact[] memory) {
            // Return the array of all contacts
            return contacts;
        }
    }
    

  

other contract.sol

    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.8;
    
    // Import the AddressBook contract to interact with it
    import "./AddressBook.sol";
    
    // Contract for creating new instances of AddressBook
    contract AddressBookFactory {
        // Define a private salt value for internal use
        string private salt = "value";
    
        // Function to deploy a new instance of AddressBook
        function deploy() external returns (AddressBook) {
            // Create a new instance of AddressBook
            AddressBook newAddressBook = new AddressBook();
    
            // Transfer ownership of the new AddressBook contract to the caller of this function
            newAddressBook.transferOwnership(msg.sender);
    
            // Return the newly created AddressBook contract
            return newAddressBook;
        }
    }
    

  

**3、Imports：这个需要先部署两个合约，然后在Deploy的时候选择“imports.sol”**

3.  **Imports**: You need to deploy two contracts first, then select **“imports.sol”** when deploying.
    

SillyStringUtils.sol

    // SPDX-License-Identifier: MIT
    
    pragma solidity ^0.8.17;
    
    library SillyStringUtils {
    
        struct Haiku {
            string line1;
            string line2;
            string line3;
        }
    
        function shruggie(string memory _input) internal pure returns (string memory) {
            return string.concat(_input, unicode" 🤷");
        }
    }
    

imports.sol

    // SPDX-License-Identifier: MIT
    
    // Importing the SillyStringUtils library
    import "./SillyStringUtils.sol";
    
    pragma solidity 0.8.17;
    
    contract ImportsExercise {
        // Using the SillyStringUtils library for string manipulation
        using SillyStringUtils for string;
    
        // Declaring a public variable to store a Haiku
        SillyStringUtils.Haiku public haiku;
    
        // Function to save a Haiku
        function saveHaiku(string memory _line1, string memory _line2, string memory _line3) public {
            haiku.line1 = _line1;
            haiku.line2 = _line2;
            haiku.line3 = _line3;
        }
    
        // Function to retrieve the saved Haiku
        function getHaiku() public view returns (SillyStringUtils.Haiku memory) {
            return haiku;
        }
    
        // Function to append a shrugging emoji to the third line of the Haiku
        function shruggieHaiku() public view returns (SillyStringUtils.Haiku memory) {
            // Creating a copy of the Haiku
            SillyStringUtils.Haiku memory newHaiku = haiku;
            // Appending the shrugging emoji to the third line using the shruggie function from the SillyStringUtils library
            newHaiku.line3 = newHaiku.line3.shruggie();
            return newHaiku;
        }
    }
    

**  
Base Learn Supreme**

**  
1、SCD ERC721**

    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    
    // Importing OpenZeppelin ERC721 contract
    import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol";
    
    // Interface for interacting with a submission contract
    interface ISubmission {
        // Struct representing a haiku
        struct Haiku {
            address author; // Address of the haiku author
            string line1; // First line of the haiku
            string line2; // Second line of the haiku
            string line3; // Third line of the haiku
        }
    
        // Function to mint a new haiku
        function mintHaiku(
            string memory _line1,
            string memory _line2,
            string memory _line3
        ) external;
    
        // Function to get the total number of haikus
        function counter() external view returns (uint256);
    
        // Function to share a haiku with another address
        function shareHaiku(uint256 _id, address _to) external;
    
        // Function to get haikus shared with the caller
        function getMySharedHaikus() external view returns (Haiku[] memory);
    }
    
    // Contract for managing Haiku NFTs
    contract HaikuNFT is ERC721, ISubmission {
        Haiku[] public haikus; // Array to store haikus
        mapping(address => mapping(uint256 => bool)) public sharedHaikus; // Mapping to track shared haikus
        uint256 public haikuCounter; // Counter for total haikus minted
    
        // Constructor to initialize the ERC721 contract
        constructor() ERC721("HaikuNFT", "HAIKU") {
            haikuCounter = 1; // Initialize haiku counter
        }
    
        string salt = "value"; // A private string variable
    
        // Function to get the total number of haikus
        function counter() external view override returns (uint256) {
            return haikuCounter;
        }
    
        // Function to mint a new haiku
        function mintHaiku(
            string memory _line1,
            string memory _line2,
            string memory _line3
        ) external override {
            // Check if the haiku is unique
            string[3] memory haikusStrings = [_line1, _line2, _line3];
            for (uint256 li = 0; li < haikusStrings.length; li++) {
                string memory newLine = haikusStrings[li];
                for (uint256 i = 0; i < haikus.length; i++) {
                    Haiku memory existingHaiku = haikus[i];
                    string[3] memory existingHaikuStrings = [
                        existingHaiku.line1,
                        existingHaiku.line2,
                        existingHaiku.line3
                    ];
                    for (uint256 eHsi = 0; eHsi < 3; eHsi++) {
                        string memory existingHaikuString = existingHaikuStrings[
                            eHsi
                        ];
                        if (
                            keccak256(abi.encodePacked(existingHaikuString)) ==
                            keccak256(abi.encodePacked(newLine))
                        ) {
                            revert HaikuNotUnique();
                        }
                    }
                }
            }
    
            // Mint the haiku NFT
            _safeMint(msg.sender, haikuCounter);
            haikus.push(Haiku(msg.sender, _line1, _line2, _line3));
            haikuCounter++;
        }
    
        // Function to share a haiku with another address
        function shareHaiku(uint256 _id, address _to) external override {
            require(_id > 0 && _id <= haikuCounter, "Invalid haiku ID");
    
            Haiku memory haikuToShare = haikus[_id - 1];
            require(haikuToShare.author == msg.sender, "NotYourHaiku");
    
            sharedHaikus[_to][_id] = true;
        }
    
        // Function to get haikus shared with the caller
        function getMySharedHaikus()
            external
            view
            override
            returns (Haiku[] memory)
        {
            uint256 sharedHaikuCount;
            for (uint256 i = 0; i < haikus.length; i++) {
                if (sharedHaikus[msg.sender][i + 1]) {
                    sharedHaikuCount++;
                }
            }
    
            Haiku[] memory result = new Haiku[](sharedHaikuCount);
            uint256 currentIndex;
            for (uint256 i = 0; i < haikus.length; i++) {
                if (sharedHaikus[msg.sender][i + 1]) {
                    result[currentIndex] = haikus[i];
                    currentIndex++;
                }
            }
    
            if (sharedHaikuCount == 0) {
                revert NoHaikusShared();
            }
    
            return result;
        }
    
        // Custom errors
        error HaikuNotUnique(); // Error for attempting to mint a non-unique haiku
        error NotYourHaiku(); // Error for attempting to share a haiku not owned by the caller
        error NoHaikusShared(); // Error for no haikus shared with the caller
    }
    

**  
2、Minimal Token**

    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    
    // Contract for an unburnable token
    contract UnburnableToken {
        string private salt = "value"; // A private string variable
    
        // Mapping to track token balances of addresses
        mapping(address => uint256) public balances;
    
        uint256 public totalSupply; // Total supply of tokens
        uint256 public totalClaimed; // Total number of tokens claimed
        mapping(address => bool) private claimed; // Mapping to track whether an address has claimed tokens
    
        // Custom errors
        error TokensClaimed(); // Error for attempting to claim tokens again
        error AllTokensClaimed(); // Error for attempting to claim tokens when all are already claimed
        error UnsafeTransfer(address _to); // Error for unsafe token transfer
    
        // Constructor to set the total supply of tokens
        constructor() {
            totalSupply = 100000000; // Set the total supply of tokens
        }
    
        // Public function to claim tokens
        function claim() public {
            // Check if all tokens have been claimed
            if (totalClaimed >= totalSupply) revert AllTokensClaimed();
            
            // Check if the caller has already claimed tokens
            if (claimed[msg.sender]) revert TokensClaimed();
    
            // Update balances and claimed status
            balances[msg.sender] += 1000;
            totalClaimed += 1000;
            claimed[msg.sender] = true;
        }
    
        // Public function for safe token transfer
        function safeTransfer(address _to, uint256 _amount) public {
            // Check for unsafe transfer conditions, including if the target address has a non-zero ether balance
            if (_to == address(0) || _to.balance == 0) revert UnsafeTransfer(_to);
    
            // Ensure the sender has enough balance to transfer
            require(balances[msg.sender] >= _amount, "Insufficient balance");
    
            // Perform the transfer
            balances[msg.sender] -= _amount;
            balances[_to] += _amount;
        }
    }
    

  

**3、ERC20**

    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.17;
    
    // Importing OpenZeppelin contracts for ERC20 and EnumerableSet functionalities
    import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
    import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
    
    // Contract for weighted voting using ERC20 token
    contract WeightedVoting is ERC20 {
        string private salt = "value"; // A private string variable
        using EnumerableSet for EnumerableSet.AddressSet; // Importing EnumerableSet for address set functionality
    
        // Custom errors
        error TokensClaimed(); // Error for attempting to claim tokens again
        error AllTokensClaimed(); // Error for attempting to claim tokens when all are already claimed
        error NoTokensHeld(); // Error for attempting to perform an action without holding tokens
        error QuorumTooHigh(); // Error for setting a quorum higher than total supply
        error AlreadyVoted(); // Error for attempting to vote more than once
        error VotingClosed(); // Error for attempting to vote on a closed issue
    
        // Struct to represent an issue
        struct Issue {
            EnumerableSet.AddressSet voters; // Set of voters
            string issueDesc; // Description of the issue
            uint256 quorum; // Quorum required to close the issue
            uint256 totalVotes; // Total number of votes casted
            uint256 votesFor; // Total number of votes in favor
            uint256 votesAgainst; // Total number of votes against
            uint256 votesAbstain; // Total number of abstained votes
            bool passed; // Flag indicating if the issue passed
            bool closed; // Flag indicating if the issue is closed
        }
    
        // Struct to represent a serialized issue
        struct SerializedIssue {
            address[] voters; // Array of voters
            string issueDesc; // Description of the issue
            uint256 quorum; // Quorum required to close the issue
            uint256 totalVotes; // Total number of votes casted
            uint256 votesFor; // Total number of votes in favor
            uint256 votesAgainst; // Total number of votes against
            uint256 votesAbstain; // Total number of abstained votes
            bool passed; // Flag indicating if the issue passed
            bool closed; // Flag indicating if the issue is closed
        }
    
        // Enum to represent different vote options
        enum Vote {
            AGAINST,
            FOR,
            ABSTAIN
        }
    
        // Array to store all issues
        Issue[] internal issues;
    
        // Mapping to track if tokens are claimed by an address
        mapping(address => bool) public tokensClaimed;
    
        uint256 public maxSupply = 1000000; // Maximum supply of tokens
        uint256 public claimAmount = 100; // Amount of tokens to be claimed
    
        string saltt = "any"; // Another string variable
    
        // Constructor to initialize ERC20 token with a name and symbol
        constructor(string memory _name, string memory _symbol)
            ERC20(_name, _symbol)
        {
            issues.push(); // Pushing an empty issue to start from index 1
        }
    
        // Function to claim tokens
        function claim() public {
            // Check if all tokens have been claimed
            if (totalSupply() + claimAmount > maxSupply) {
                revert AllTokensClaimed();
            }
            // Check if the caller has already claimed tokens
            if (tokensClaimed[msg.sender]) {
                revert TokensClaimed();
            }
            // Mint tokens to the caller
            _mint(msg.sender, claimAmount);
            tokensClaimed[msg.sender] = true; // Mark tokens as claimed
        }
    
        // Function to create a new voting issue
        function createIssue(string calldata _issueDesc, uint256 _quorum)
            external
            returns (uint256)
        {
            // Check if the caller holds any tokens
            if (balanceOf(msg.sender) == 0) {
                revert NoTokensHeld();
            }
            // Check if the specified quorum is higher than total supply
            if (_quorum > totalSupply()) {
                revert QuorumTooHigh();
            }
            // Create a new issue and return its index
            Issue storage _issue = issues.push();
            _issue.issueDesc = _issueDesc;
            _issue.quorum = _quorum;
            return issues.length - 1;
        }
    
        // Function to get details of a voting issue
        function getIssue(uint256 _issueId)
            external
            view
            returns (SerializedIssue memory)
        {
            Issue storage _issue = issues[_issueId];
            return
                SerializedIssue({
                    voters: _issue.voters.values(),
                    issueDesc: _issue.issueDesc,
                    quorum: _issue.quorum,
                    totalVotes: _issue.totalVotes,
                    votesFor: _issue.votesFor,
                    votesAgainst: _issue.votesAgainst,
                    votesAbstain: _issue.votesAbstain,
                    passed: _issue.passed,
                    closed: _issue.closed
                });
        }
    
        // Function to cast a vote on a voting issue
        function vote(uint256 _issueId, Vote _vote) public {
            Issue storage _issue = issues[_issueId];
    
            // Check if the issue is closed
            if (_issue.closed) {
                revert VotingClosed();
            }
            // Check if the caller has already voted
            if (_issue.voters.contains(msg.sender)) {
                revert AlreadyVoted();
            }
    
            uint256 nTokens = balanceOf(msg.sender);
            // Check if the caller holds any tokens
            if (nTokens == 0) {
                revert NoTokensHeld();
            }
    
            // Update vote counts based on the vote option
            if (_vote == Vote.AGAINST) {
                _issue.votesAgainst += nTokens;
            } else if (_vote == Vote.FOR) {
                _issue.votesFor += nTokens;
            } else {
                _issue.votesAbstain += nTokens;
            }
    
            // Add the caller to the list of voters and update total votes count
            _issue.voters.add(msg.sender);
            _issue.totalVotes += nTokens;
    
            // Close the issue if quorum is reached and determine if it passed
            if (_issue.totalVotes >= _issue.quorum) {
                _issue.closed = true;
                if (_issue.votesFor > _issue.votesAgainst) {
                    _issue.passed = true;
                }
            }
        }
    }
    

  

到此就结束了

That’s the end.

  

本文参考Web3村长youtube，大家可以订阅一下！

[![](https://paragraph.com/editor/youtube/play.png)](https://www.youtube.com/watch?v=CvpZ50K-XxQ)

---

*Originally published on [Biewen](https://paragraph.com/@biewenw/base-learn)*
