Cover photo

BaseCamp NFT - Deploy contract ภาค 2

เก็บ NFT จนครบ 13ชิ้น

บทความนี้ outdated แล้ว

อ่านบทความล่าสุดที่

https://web3.gadgeteer.in.th/learning-deploy-contract-base-camp-nft-full

ทุกครั้งที่ลอกการบ้าน อย่าลืมตั้ง version complier ให้ตรงกับโค้ดทุกครั้งนะครับ

การบ้าน 2

ตั้งชื่อไฟล์ว่า ControlStructures.sol

solidity 0.8.14

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.14;

contract ControlStructures {
    // Create a custom error for AfterHours
    error AfterHours(uint256 time);

    function fizzBuzz(uint256 _number) public pure returns (string memory) {
        if (_number % 3 == 0 && _number % 5 == 0) {
            return "FizzBuzz";
        } else if (_number % 3 == 0) {
            return "Fizz";
        } else if (_number % 5 == 0) {
            return "Buzz";
        } else {
            return "Splat";
        }
    }

    function doNotDisturb(uint256 _time) public pure returns (string memory) {
        // If _time is greater than or equal to 2400, trigger a panic
        assert(_time < 2400);

        // If _time is greater than 2200 or less than 800, revert with a custom error of AfterHours
        if (_time > 2200 || _time < 800) {
            revert AfterHours(_time);
        } else if (_time >= 1200 && _time <= 1259) {
            revert("At lunch!");
        } else if (_time >= 800 && _time < 1200) {
            return "Morning!";
        } else if (_time >= 1300 && _time <= 1799) {
            return "Afternoon!";
        } else if (_time >= 1800 && _time <= 2199) {
            return "Evening!";
        } else {
            revert("Invalid time");
        }
    }
}

จากนั้น compile > deploy > เก็บ contract address ที่ได้ > claim NFT https: docs.base.org base-camp docs control-structures control-structures-exercise

Deploy บน Base เสร็จ ก็ลง testnet chain อื่นๆ ไปด้วยเช่นเคย

  • Base Goerli

  • Scroll alpha testnet

  • Linea

  • polygon zkEVM testnet

วันไหน gas ถูกก็จัด mainnet ต่างๆ เช่น polygon zkEVM, zkSync

ลงมันทุก EVM ที่นึกได้ ถ้าไม่เปลือง gas


การบ้าน 3

เหมือนเดิม แค่ใช้โค้ดใหม่

claim https: docs.base.org base-camp docs storage storage-exercise

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

contract EmployeeStorage {
    uint256 public idNumber;
    uint32 private salary;
    uint16 private shares;
    string public name;

    constructor() {
        shares = 1000;
        name = "Pat";
        salary = 50000;
        idNumber = 112358132134;
    }

    function viewSalary() public view returns(uint32) {
        return salary;
    }

    function viewShares() public view returns(uint16) {
        return shares;
    }

    function grantShares(uint16 _newShares) public {
        uint16 newTotalShares = shares + _newShares;

        if (_newShares > 5000 || newTotalShares > 5000) {
            revert("Too many shares");
        }

        shares = newTotalShares;
    }

    function checkForPacking(uint _slot) public view returns (uint r) {
        assembly {
            r := sload (_slot)
        }
    }

    function debugResetShares() public {
        shares = 1000;
    }
}

การบ้าน 4

ตั้งชื่อไฟล์ว่า ArraysExercise.sol

solidity version 0.8.17

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

contract ArraysExercise {
    uint[] public numbers = [1,2,3,4,5,6,7,8,9,10];
    address[] public senders;
    uint[] public timestamps;

    // Return a Complete Array
    function getNumbers() public view returns (uint[] memory) {
        return numbers;
    }

    // Reset Numbers
    function resetNumbers() public {
        numbers = [1,2,3,4,5,6,7,8,9,10];
    }

    // Append to an Existing Array
    function appendToNumbers(uint[] calldata _toAppend) public {
        uint originalLength = numbers.length;
        uint toAppendLength = _toAppend.length;

        uint[] memory newNumbers = new uint[](originalLength + toAppendLength);

        for (uint i = 0; i < originalLength; i++) {
            newNumbers[i] = numbers[i];
        }

        for (uint j = 0; j < toAppendLength; j++) {
            newNumbers[originalLength + j] = _toAppend[j];
        }

        numbers = newNumbers;
    }

    // Timestamp Saving
    function saveTimestamp(uint _unixTimestamp) public {
        senders.push(msg.sender);
        timestamps.push(_unixTimestamp);
    }

    // Timestamp Filtering
    function afterY2K() public view returns (uint[] memory, address[] memory) {
        uint counter = 0;
        for (uint i = 0; i < timestamps.length; i++) {
            if (timestamps[i] > 946702800) {
                counter++;
            }
        }

        uint[] memory postY2KTimestamps = new uint[](counter);
        address[] memory postY2KSenders = new address[](counter);

        counter = 0;
        for (uint i = 0; i < timestamps.length; i++) {
            if (timestamps[i] > 946702800) {
                postY2KTimestamps[counter] = timestamps[i];
                postY2KSenders[counter] = senders[i];
                counter++;
            }
        }

        return (postY2KTimestamps, postY2KSenders);
    }

    // Resets
    function resetSenders() public {
        delete senders;
    }

    function resetTimestamps() public {
        delete timestamps;
    }
}

claim NFT https: docs.base.org base-camp docs arrays arrays-exercise


การบ้าน 5

FavoriteRecords.sol

solidity version 0.8.19

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

contract FavoriteRecords {
    
    // Error type for records not being approved
    error NotApproved(string record);
    
    // State variables
    mapping(string => bool) public approvedRecords;
    mapping(address => mapping(string => bool)) private userFavorites;
    string[] private approvedRecordNames;
    
    constructor() {
        // Load approved records
        string[9] memory records = [
            "Thriller",
            "Back in Black",
            "The Bodyguard",
            "The Dark Side of the Moon",
            "Their Greatest Hits (1971-1975)",
            "Hotel California",
            "Come On Over",
            "Rumours",
            "Saturday Night Fever"
        ];
        for(uint i=0; i<records.length; i++) {
            approvedRecords[records[i]] = true;
            approvedRecordNames.push(records[i]);
        }
    }
    
    // Get approved records
    function getApprovedRecords() public view returns(string[] memory) {
        return approvedRecordNames;
    }
    
    // Add record to favorites
    function addRecord(string memory record) public {
        if(approvedRecords[record]) {
            userFavorites[msg.sender][record] = true;
        } else {
            revert NotApproved(record);
        }
    }
    
    // Get user favorites
    function getUserFavorites(address user) public view returns(string[] memory) {
        uint count = 0;
        for(uint i=0; i<approvedRecordNames.length; i++) {
            if(userFavorites[user][approvedRecordNames[i]]) {
                count++;
            }
        }
        
        string[] memory favorites = new string[](count);
        uint index = 0;
        for(uint i=0; i<approvedRecordNames.length; i++) {
            if(userFavorites[user][approvedRecordNames[i]]) {
                favorites[index] = approvedRecordNames[i];
                index++;
            }
        }
        return favorites;
    }
    
    // Reset user favorites
    function resetUserFavorites() public {
        for(uint i=0; i<approvedRecordNames.length; i++) {
            userFavorites[msg.sender][approvedRecordNames[i]] = false;
        }
    }
}

claim NFT https: docs.base.org base-camp docs mappings mappings-exercise


การบ้าน 6

GarageManager.sol
solidity 0.8.19

Structs NFT Badge
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

contract GarageManager {
    // Car struct definition
    struct Car {
        string make;
        string model;
        string color;
        uint numberOfDoors;
    }

    // mapping to store a list of cars, indexed by address
    mapping(address => Car[]) public garage;

    // function to add a car to the user's collection in the garage
    function addCar(string memory _make, string memory _model, string memory _color, uint _numberOfDoors) public {
        Car memory newCar = Car({
            make: _make,
            model: _model,
            color: _color,
            numberOfDoors: _numberOfDoors
        });
        garage[msg.sender].push(newCar);
    }

    // function to return all cars owned by the calling user
    function getMyCars() public view returns (Car[] memory) {
        return garage[msg.sender];
    }

    // function to return all cars owned by a specific user
    function getUserCars(address user) public view returns (Car[] memory) {
        return garage[user];
    }

    // function to update a car of the calling user
    function updateCar(uint index, string memory _make, string memory _model, string memory _color, uint _numberOfDoors) public {
        if (index >= garage[msg.sender].length) {
            revert("BadCarIndex");
        }

        garage[msg.sender][index] = Car({
            make: _make,
            model: _model,
            color: _color,
            numberOfDoors: _numberOfDoors
        });
    }

    // function to delete the entry in garage for the sender
    function resetMyGarage() public {
        delete garage[msg.sender];
    }
}

claim nft https: docs.base.org base-camp docs structs structs-exercise


การบ้าน 7

อันนี้อยู่ๆก็อัพเวล เพราะเราจะ deploy หลาย contract ในการบ้านนี้
แอดนาโนจะจับมือไปทีละขั้นตอน อิอิ

1) สร้าง ไฟล์ Employee.sol
ใส่โค้ด

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

abstract contract Employee {
    uint public idNumber;
    uint public managerId;

    constructor(uint _idNumber, uint _managerId) {
        idNumber = _idNumber;
        managerId = _managerId;
    }

    function getAnnualCost() public virtual view returns(uint);
}

contract Salaried is Employee {
    uint public annualSalary;

    constructor(uint _idNumber, uint _managerId, uint _annualSalary) Employee(_idNumber, _managerId) {
        annualSalary = _annualSalary;
    }

    function getAnnualCost() public override view returns(uint) {
        return annualSalary;
    }
}

contract Hourly is Employee {
    uint public hourlyRate;

    constructor(uint _idNumber, uint _managerId, uint _hourlyRate) Employee(_idNumber, _managerId) {
        hourlyRate = _hourlyRate;
    }

    function getAnnualCost() public override view returns(uint) {
        return hourlyRate * 2080;
    }
}

contract Manager {
    uint[] public employeeIds;

    function addReport(uint idNumber) public {
        employeeIds.push(idNumber);
    }

    function resetReports() public {
        delete employeeIds;
    }
}

contract Salesperson is Hourly {
    constructor(uint _idNumber, uint _managerId, uint _hourlyRate) Hourly(_idNumber, _managerId, _hourlyRate) {}
}

contract EngineeringManager is Salaried, Manager {
    constructor(uint _idNumber, uint _managerId, uint _annualSalary) Salaried(_idNumber, _managerId, _annualSalary) {}
}

2) เริ่ม Deploy เลือก contract "Engineer manager" ใส่ 54321, 11111, 200000

3)Deploy เลือก contract "Salesperson"
ใส่ 55555,12345,20

4) สร้าง InheritanceSubmission.sol

// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;

contract InheritanceSubmission {
    address public salesPerson;
    address public engineeringManager;

    constructor(address _salesPerson, address _engineeringManager) {
        salesPerson = _salesPerson;
        engineeringManager = _engineeringManager;
    }
}

deploy ตามด้านล่าง
- ก็อบ contract address ของ saleperson ใส่ก่อน
- ตามด้วย comma (,)
- จากนั้นใส่ contract address ของ engineer
- กด deploy

ก็อบ contract address ของ inheritancesubmission (ลูกศรเขียว)
ไปใช้เคลม NFT reward

claim nft https: docs.base.org base-camp docs inheritance inheritance-exercise


การบ้าน 8

กลับมาง่ายเหมือนเดิมแล้ว

Silly.sol
Solidity 0.8.17

// 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(abi.encodePacked(_input, " \xF0\x9F\xA4\xB7")); // Adding space before the emoji
    }
}

contract ImportsExercise {
    using SillyStringUtils for string;

    SillyStringUtils.Haiku public haiku;

    function saveHaiku(string memory _line1, string memory _line2, string memory _line3) public {
        haiku = SillyStringUtils.Haiku({
            line1: _line1,
            line2: _line2,
            line3: _line3
        });
    }

    function getHaiku() public view returns (SillyStringUtils.Haiku memory) {
        return haiku;
    }

    function shruggieHaiku() public view returns (SillyStringUtils.Haiku memory) {
        SillyStringUtils.Haiku memory newHaiku = haiku;
        newHaiku.line3 = newHaiku.line3.shruggie();
        return newHaiku;
    }
}

claim NFT https: docs.base.org base-camp docs imports imports-exercise


การบ้าน 9

errorFix.sol
0.8.17

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.17;

contract ErrorTriageExercise {
    /**
     * Finds the difference between each uint with it's neighbor (a to b, b to c, etc.)
     * and returns a uint array with the absolute integer difference of each pairing.
     */
    function diffWithNeighbor(
        uint _a,
        uint _b,
        uint _c,
        uint _d
    ) public pure returns (uint[] memory) {
        uint[] memory results = new uint[](3);

        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 results;
    }

    /**
     * Changes the _base by the value of _modifier.  Base is always > 1000.  Modifiers can be
     * between positive and negative 100;
     */
    function applyModifier(
        uint _base,
        int _modifier
    ) public pure returns (uint) {
        require(_modifier >= -100 && _modifier <= 100, "Modifier should be between -100 and 100");
        return uint(int(_base) + _modifier);
    }

    /**
     * Pop the last element from the supplied array, and return the modified array and the popped
     * value (unlike the built-in function)
     */
    uint[] arr;

    function popWithReturn() public returns (uint) {
        require(arr.length > 0, "Array is empty");
        uint index = arr.length - 1;
        uint value = arr[index];
        arr.pop();
        return value;
    }

    // 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;
    }
}

claim NFT https: docs.base.org base-camp docs error-triage error-triage-exercise


การบ้าน 10

คนติดข้อนี้เยอะมาก แอดเคยทำได้ ตอนนี้ลองใหม่ก็ติด คิดว่าเป็นเพราะ remix

กลับมายากนิดหน่อยกันอีกครั้ง เพราะงวดนี้เราจะสร้าง contractFactory ที่ช่วยเราสร้าง contract อีกที (งงดิ)

Contact.sol solidity 0.8.17
ก็อบโค้ดนี้เข้า remix จากนั้นอ่านวิธีต่อด้านล่าง

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.17;

import "@openzeppelin/contracts/access/Ownable.sol";

contract AddressBook is Ownable {

    struct Contact {
        uint id;
        string firstName;
        string lastName;
        uint[] phoneNumbers;
    }

    mapping (uint => Contact) private contacts;
    uint private contactCounter;

    function addContact(
        string memory _firstName,
        string memory _lastName,
        uint[] memory _phoneNumbers
    ) public onlyOwner returns (uint) {
        contacts[contactCounter] = Contact(contactCounter, _firstName, _lastName, _phoneNumbers);
        return contactCounter++;
    }

    function deleteContact(uint _id) public onlyOwner {
        require(contacts[_id].id == _id, "ContactNotFound");
        delete contacts[_id];
    }

    function getContact(uint _id) public view returns (Contact memory) {
        require(contacts[_id].id == _id, "ContactNotFound");
        return contacts[_id];
    }

    function getAllContacts() public view returns (Contact[] memory) {
        Contact[] memory contactArray = new Contact[](contactCounter);
        for (uint i = 0; i < contactCounter; i++) {
            if (contacts[i].id == i) {
                contactArray[i] = contacts[i];
            }
        }
        return contactArray;
    }
}

contract AddressBookFactory {
    function deploy() public returns (address) {
        AddressBook addrBook = new AddressBook();
        addrBook.transferOwnership(msg.sender);
        return address(addrBook);
    }
}

เมื่อก็อบโค้ดวางเรียบร้อย ให้เลือก contract AddressBookFactory แล้วกด deploy

จากนั้นให้เข้า metamask ดู transaction ล่าสุด ก็อบ contract address มาตามปกติ

เอา contract address ที่เพิ่งได้ไปใส่ใน remix ใต้ปุ่ม deploy
ตรงปุ่ม at Address
จากนั้นกดปุ่ม deploy จาก contract

เรียบร้อย เราจะได้ contract ใหม่อีกตัว
ก็อบเลข contract อันเดิม (ในตัวอย่างก็ 0x755 ไปใช้เคลม NFT)

claim NFT https: docs.base.org base-camp docs new-keyword new-keyword-exercise


11-13 solutions from
https://github.com/harsharock/BaseCamp/tree/main thank you

การบ้าน 11

minimol.sol
0.8.0

// 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;
    }
}

claim NFT https: docs.base.org base-camp docs minimal-tokens minimal-tokens-exercise


การบ้าน 12

erc20.sol
0.8.17

ใส่ชื่ออะไรก็ได้ตรงปุ่ม deploy

// SPDX-License-Identifier: UNLICENSED
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;
            }
        }
    }
}

claim NFT https://docs.base.org/base-camp/docs/erc-20-token/erc-20-exercise


การบ้าน 13

erc721.sol
0.8.26 ได้

// 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
}

claim NFT https://docs.base.org/base-camp/docs/erc-721-token/erc-721-exercise


สำหรับเพื่อนๆที่ทำถึงตรงนี้ยินดีด้วยครับ คุณได้สร้าง 13 NFT สำหรับ BaseCamp
NFT ต่างๆที่เราได้มาจะมีการโอนไป Base mainnet ในช่วงส้ินปีนี้ด้วย

และ contract ต่างๆที่เราได้เรียนรู้
สามารถนำไปdeploy บน testnet อื่นๆได้สบาย

ถ้าถูกใจสามารถช่วยเลี้ยงกาแฟ nanobro ได้โดยการ collect โพสด้านล่างคร้าบ ขอบคุณมากๆครับ🙏

Loading...
highlight
Collect this post to permanently own it.
nanobro.eth logo
Subscribe to nanobro.eth and never miss a post.
#l2#base#tips#tools#nft#scroll#linea