เหมือนบทความเดิมนะ ใครเคยทำแล้ว ต่อข้อ 11 ได้เลย
เพิ่มข้อ 11-13 ให้ครบ
ใครเคยติดข้อ 10 อันนี้น่าจะทำตามง่ายขึ้นนะ 😀
เรียงให้ตามง่ายขึ้น
deploy contract ทำ ทำไม
โพสนี้เริ่มจาก Base มีหลักสูตรให้เรียนและเคลม NFT ทุกๆบท เมื่อผมเรียนจบก็เห็นว่ามันลอกการบ้านง่ายนะ เลยคิดว่ามาแนะนำเพื่อนๆดีกว่า
✅ จะได้รู้ว่า contract มันทำยังไง
✅ ง่ายกว่าที่คิด
✅ ไม่ต้องติดตั้งแอปลงเครื่อง
✅ ใช้เวลาไม่มาก(?)
deploy contract บน chain อื่นๆ เช่น Scroll, Linea, zkSync, polygon zkEVM และ EVM อื่นๆได้ทั้งหมด เทสเน็ตเรา deploy ได้รัวๆ ส่วน mainnet ใช้เงินจริงนะเอ้อ
การ deploy contract เป็นการสร้างกิจกรรมบน blockchain ซึ่งปกติมีแต่ dev ที่ทำ user ไม่ค่อยทำ ถ้า เชนไหน reward dev ก็น่าจะส่งผลกับ airdrop ที่มากขึ้น ดังนั้นผมคิดว่าทำไว้ไม่เสียหาย โดยเฉพาะเทสเน็ตอย่าง Scroll testnet, Linea testnet ไม่ควรพลาด
สำหรับ NFT ที่เราจะเก็บกันอยู่นี้ จะมีผลกับ Base mainnet ในสิ้นปี 2024 นี้ด้วย
เตรียมการกันก่อน
แอด chain ต่างๆได้ที่ https: chainlist.org
เข้าไปแล้ว connect wallet แอดเชน testnet ให้หมด โดยเฉพาะ Base sepoliaเตรียม เหรียญเทส ETH ขุดเอาได้ไว https: sepolia-faucet.pk910.de
บริดเหรียญเทสไปเชนที่ต้องการ https: rinkeby.orbiter.finance
*เสริม กรณีบริด mainnet https: www.orbiter.finance
พร้อมแล้วก็ลุยต่อกันเลย
เริ่มdeploy contract และ เคลม NFT กันได้เลย
เราจะเขียน Contract และ Deploy ด้วย Remix
https: remix.ethereum.org < เพื่อความปลอดภัยเช็ค URL ให้ถูกต้องทุกครั้ง
เมื่อเข้าไป ให้กด tab - file explorer บนสุด เราจะเห็นว่ามีไฟล์อยู่แล้ว ลบให้หมด เหลือแค่ folder contract
จากนั้นคลิกขวา folder > new file > ตั้งชื่อไฟล์ใหม่ BasicMath.sol ก็ได้
จากนั้นดูด้านขวา ก็อบโค้ดการบ้านนี้วาง (ตอนวางจะมีเตือน เราว่าระวังอย่าใส่โค้ดหลอกมั่วๆทางเน็ต ไม่ต้องตกใจ)
การก็อบโค้ดทุกครั้งมีความเสี่ยง เช่นถ้าเราก็อบ contract rug rug มา ก็จะไม่ดี
ไม่ควรเชื่อเว็บไซต์ทั่วไป
โค้ดการบ้านข้อ 1 contract ที่ช่วยเราบวกลบเลข👇
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
contract BasicMath {
uint constant MAX_UINT = type(uint).max;
function adder(uint _a, uint _b) public pure returns (uint, bool) {
if(_a > MAX_UINT - _b) {
return (0, true); // Overflow would occur, return error
}
return (_a + _b, false); // No overflow
}
function subtractor(uint _a, uint _b) public pure returns (uint, bool) {
if(_b > _a) {
return (0, true); // Underflow would occur, return error
}
return (_a - _b, false); // No underflow
}
}
จากนั้นไป tab - Solidity compiler
เลือก version ให้ตรงกับในโค้ด แล้วกดปุ่ม compile
Compile เรียบร้อย ต่อไปเรา deploy กันเลย
กด tab - deploy and run
เลือก injected - wallet จะมีต่อ metamask
ตั้ง metamask เป็น Base Sepolia network ให้ถูกต้อง
สังเกตมีเลขระบุ network 84532 = base Sepolia networkกดปุ่ม deploy และ sign ให้เรียบร้อย
ด้านล่างสุด เราจะเห็น contract ที่เพิ่ง deploy มาได้
copy address เอาไว้สำหรับเคลม NFT
เท่านี้เพื่อนๆก็ deploy contract บน Base Sepolia เรียบร้อยกันแล้วนะครับ
เคลม NFT กัน
เรียบร้อย จบไปแล้วสำหรับการบ้าน 1
เก่งมากครับทุกคนนนน 🧡
เพื่อนๆทำเป็นแล้ว contract การบ้าน ต่อๆไปก็น่าจะประมาณนี้แหละ ครับ
ปล. ไหนๆก็ทำแล้ว เชียร์ deploy บน testnet อื่นๆเช่น Scroll, Linea ไปด้วยเลยแม้จะไม่มี NFT เป็นรางวัล
กดเปลี่ยน network แล้ว deploy รัวๆ
Scroll Alpha testnet (check network id 534353)
Linea testnet (check network id 59140)
Taiko
การ Deploy บน mainnet ต่างๆก็น่าสนใจเช่นกัน (ต้องระวังเรื่อง gas)
Polygon zkEVM 1101
zkSync
Arb Nova 42170และอื่นๆ บน metamask ใช้วิธีนี้ได้ทั้งหมด
โดย contract นี้มีขนาดเล็ก ใช้ gas น้อย ปลอดภัย(ทำได้แค่บวกลบเลข)
Verify contract
▼ต่อจาก deploy เราสามารถ verify contract ได้นะ (ไม่จำเป็น)
การ verify EVM contract ที่ deploy ไป
ไปที่ block explorer - etherscan/ basescan
คลิก verify contract
จากนั้นกรอกข้อมูลให้เรียบร้อย เลข contract address และ version solidity
ดู version ตรงนี้
license ตามที่ผมเคยสอน ส่วนใหญ่จะเป็น MIT
ใส่โค้ดที่เคยใช้เข้าไป แล้ว sign
แค่นี้การ verify ก็เรียบร้อย
การบ้าน 2-13
การบ้าน 1 ไปเรียบร้อย
ตอนนี้เพื่อนๆก็ทำเป็นแล้ว ต่อๆไป จะเป็นการก็อบโค้ดและทำซ้ำอย่างเดียวครับ :)
หากจะลุยต่อ อยากให้ลอง collect โพสด้วยปุ่มด้านล่าง 0.001 matic + gas
ทุกครั้งที่ลอกการบ้าน อย่าลืมตั้ง 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 Sepolia
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
// 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 sepolia ได้ที่ (อันที่เคยทำบน goerli จะไม่โชว์ แต่ก็นับ) https://docs.base.org/base-camp/progress/
NFT ต่างๆที่เราได้มาจะมีการโอนไป Base mainnet ในช่วงส้ินปีนี้ด้วย
และ contract ต่างๆที่เราได้เรียนรู้
สามารถนำไปdeploy บน testnet อื่นๆได้สบาย
เมื่อทำครบหมดให้เข้า guild เพื่อรับ discord role ได้เลย
https://guild.xyz/base/base-camp
ถ้าถูกใจสามารถช่วยเลี้ยงกาแฟ nanobro ได้โดยการ collect โพสด้านล่างคร้าบ ขอบคุณมากๆครับ🙏