วิธีสร้างโทเค็น ERC20 ด้วยวิธีง่ายๆ

เผยแพร่แล้ว: 2022-03-11

เป้าหมายของบทความนี้คือการสาธิตวิธีสร้างโทเค็น ERC20 ในเวลาอันสั้นที่สุด

มาเริ่มกันที่พื้นฐานกันก่อน: โทเค็น ERC20 คืออะไร?

ในช่วงไม่กี่ปีที่ผ่านมา ข้อกำหนดของโทเค็น ERC20 ได้กลายเป็นมาตรฐานสำหรับโทเค็น Ethereum กล่าวอีกนัยหนึ่งสัญญา Ethereum ส่วนใหญ่ที่มีอยู่ในปัจจุบันเป็นไปตามข้อกำหนด ERC20 บทความนี้จะอธิบายรายละเอียดเกี่ยวกับวิธีสร้างโทเค็น Ethereum ของคุณเอง แต่ก่อนที่เราจะเริ่มต้น มาดูมาตรฐาน ERC20 กันก่อนดีกว่า

ภาพประกอบโทเค็น ERC20

อะไรทำให้โทเค็น ERC20 น่าสนใจและประสบความสำเร็จ มีหลายปัจจัยในการเล่น:

  1. โทเค็น ERC20 นั้นเรียบง่ายและง่ายต่อการปรับใช้ ดังที่คุณเห็นในบทช่วยสอนนี้
  2. มาตรฐาน ERC20 แก้ปัญหาที่สำคัญ เนื่องจากตลาดซื้อขายหลักทรัพย์ที่ใช้บล็อคเชนและกระเป๋าเงินดิจิตอลเข้ารหัสต้องการชุดคำสั่งมาตรฐานชุดเดียวเพื่อสื่อสารกับช่วงของโทเค็นที่พวกเขาจัดการ ซึ่งรวมถึงกฎการโต้ตอบระหว่างโทเค็นต่างๆ ตลอดจนกฎการซื้อโทเค็น
  3. เป็นข้อกำหนดยอดนิยมรายแรกที่เสนอมาตรฐานโทเค็น Ethereum มันไม่ได้ เป็นครั้งแรก แต่ด้วยความนิยมของมัน มันจึงกลายเป็นมาตรฐานอุตสาหกรรมอย่างรวดเร็ว

เช่นเดียวกับโทเค็น Ethereum อื่น ๆ โทเค็น ERC20 ถูกนำมาใช้เป็นสัญญาอัจฉริยะและดำเนินการบน Ethereum Virtual Machine (EVM) ในลักษณะการกระจายอำนาจ

Solidity: ภาษาการเขียนโปรแกรม Smart Contract

สัญญาอัจฉริยะของ Ethereum เขียนด้วย Solidity แม้ว่าจะมีภาษาอื่น แต่แทบไม่มีใครใช้ภาษาเหล่านี้เพื่อจุดประสงค์นี้ Solidity นั้นคล้ายกับ JavaScript ดังนั้นหากคุณมีความรู้เกี่ยวกับ JavaScript หรือแม้แต่ Java และภาษา C-like อื่นๆ คุณไม่ควรมีปัญหาในการหาว่าโค้ดใน Solidity มี แม้กระทั่งก่อนที่คุณจะเชี่ยวชาญ Solidity มากพอที่จะใช้งาน มัน.

นี่คือจุดเริ่มต้นของความสนุก เพราะคุณควรจะสามารถเริ่มสร้างสัญญา ERC20 แบบง่ายๆ ได้ในเวลาไม่นาน นี่เป็นงานที่ตรงไปตรงมา ง่ายพอที่บทความนี้จะสาธิตวิธีเขียนและปรับใช้โทเค็น ERC20 ในเวลาไม่ถึงชั่วโมง

โทเค็นที่เราจะสร้างในการสาธิตนี้จะเป็นการนำ ERC20 ไปใช้โดยเปล่าประโยชน์ โดยไม่มีเสียงระฆังและนกหวีดมากเกินไป อย่างไรก็ตาม ฉันได้เห็นโทเค็นง่ายๆ ที่คล้ายกันมากมายในโลกแห่งความเป็นจริง และพวกเขามักจะทำได้ดีทีเดียว

ภาพรวมของมาตรฐานโทเค็น ERC20

ERC20 คืออะไร?

พูดง่ายๆ ก็คือ มาตรฐาน ERC20 กำหนดชุดของฟังก์ชันที่จะนำไปใช้กับโทเค็น ERC20 ทั้งหมด เพื่อให้สามารถทำงานร่วมกับสัญญา กระเป๋าเงิน หรือตลาดอื่นๆ ได้ ชุดฟังก์ชันนี้ค่อนข้างสั้นและเป็นพื้นฐาน

 function totalSupply() public view returns (uint256); function balanceOf(address tokenOwner) public view returns (uint); function allowance(address tokenOwner, address spender) public view returns (uint); function transfer(address to, uint tokens) public returns (bool); function approve(address spender, uint tokens) public returns (bool); function transferFrom(address from, address to, uint tokens) public returns (bool);

ฟังก์ชัน ERC20 อนุญาตให้ผู้ใช้ภายนอก เช่น แอป crypto-wallet ค้นหายอดเงินคงเหลือของผู้ใช้และโอนเงินจากผู้ใช้รายหนึ่งไปยังอีกรายหนึ่งโดยได้รับอนุญาตอย่างถูกต้อง

สัญญาอัจฉริยะกำหนดเหตุการณ์ที่กำหนดไว้เฉพาะสองเหตุการณ์:

 event Approval(address indexed tokenOwner, address indexed spender, uint tokens); event Transfer(address indexed from, address indexed to, uint tokens);

เหตุการณ์เหล่านี้จะถูกเรียกหรือ ปล่อยออกมา เมื่อผู้ใช้ได้รับสิทธิ์ในการถอนโทเค็นออกจากบัญชี และหลังจากโอนโทเค็นจริงแล้ว

นอกเหนือจากฟังก์ชัน ERC20 มาตรฐานแล้ว โทเค็น ERC20 จำนวนมากยังมีฟิลด์เพิ่มเติม และบางส่วนได้กลายเป็นส่วนหนึ่งของมาตรฐาน ERC20 โดยพฤตินัยแล้ว หากไม่ได้เขียนเป็นลายลักษณ์อักษรในทางปฏิบัติ ต่อไปนี้คือตัวอย่างบางส่วนของฟิลด์ดังกล่าว

 string public constant name; string public constant symbol; uint8 public constant decimals;

ต่อไปนี้คือประเด็นบางประการเกี่ยวกับ ERC20 และศัพท์เฉพาะของ Solidity:

  • ฟังก์ชั่น public สามารถเข้าถึงได้นอกสัญญาเอง
  • การ view โดยทั่วไปหมายถึงค่าคงที่ กล่าวคือ สถานะภายในของสัญญาจะไม่ถูกเปลี่ยนโดยฟังก์ชัน
  • event คือวิธีการของ Solidity ในการอนุญาตให้ลูกค้าเช่นส่วนหน้าแอปพลิเคชันของคุณได้รับแจ้งเหตุการณ์เฉพาะภายในสัญญา

โครงสร้างภาษา Solidity ส่วนใหญ่ควรมีความชัดเจนหากคุณมีทักษะ Java/JavaScript ที่จำเป็นอยู่แล้ว

การเขียนโทเค็น ERC20 อย่างแข็งแกร่ง

โทเค็น ERC20 แข็งแกร่ง

ตอนนี้เราได้สรุปข้อมูลพื้นฐานและอธิบายสิ่งที่ต้องใช้เพื่อสร้างโทเค็น ERC20 แล้ว ก็ถึงเวลาที่จะเริ่มเขียนตรรกะบางอย่าง

อันดับแรก เราต้องกำหนดวัตถุการทำแผนที่สองรายการ นี่คือแนวคิด Solidity สำหรับอาร์เรย์ที่เชื่อมโยงหรือคีย์/ค่า:

 mapping(address => uint256) balances; mapping(address => mapping (address => uint256)) allowed;

การ mapping(address => uint256) กำหนดอาเรย์ที่เชื่อมโยงซึ่งมีคีย์เป็น address ประเภท — ตัวเลขที่ใช้เพื่อระบุที่อยู่บัญชี และมีค่าเป็นประเภท uint256 — โดยทั่วไปแล้วจะเป็นจำนวนเต็ม 256 บิตเพื่อเก็บยอดคงเหลือโทเค็น

ออบเจ็กต์การแมปแรก balances จะเก็บยอดคงเหลือโทเค็นของบัญชีเจ้าของแต่ละบัญชี

ออบเจ็กต์การทำแผนที่ที่สอง ที่ allowed จะรวมบัญชีทั้งหมดที่ได้รับการอนุมัติให้ถอนออกจากบัญชีที่กำหนดพร้อมกับผลรวมการถอนที่อนุญาตสำหรับแต่ละบัญชี

อย่างที่คุณเห็น ฟิลด์ค่าของการทำแผนที่ที่อนุญาตนั้นเป็นที่อยู่ของบัญชีการวางแผนการทำแผนที่กับผลรวมการถอนที่ได้รับอนุมัติ

การแมปเหล่านี้ร่วมกับฟิลด์สัญญาอื่น ๆ ทั้งหมดจะถูกเก็บไว้ในบล็อคเชนและจะถูก ขุด ส่งผลให้การเปลี่ยนแปลงถูกเผยแพร่ไปยังโหนดผู้ใช้เครือข่ายทั้งหมด

ที่เก็บข้อมูลบล็อกเชนมีราคาแพงและผู้ใช้สัญญาของคุณต้องจ่ายเงินไม่ทางใดก็ทางหนึ่ง ดังนั้นคุณควรพยายามลดขนาดพื้นที่เก็บข้อมูลให้น้อยที่สุดและเขียนลงในบล็อคเชน

ตอนนี้ เรามีโครงสร้างข้อมูลที่จำเป็นแล้ว เราสามารถเริ่มเขียนตรรกะ ERC20 ลงในฟังก์ชันที่เหมาะสมได้

การตั้งค่าจำนวนโทเค็น ICO

เราจะกำหนดจำนวนโทเค็น ICO ได้อย่างไร? มีหลายวิธีในการกำหนดจำนวนโทเค็น ICO สูงสุด และเรื่องนี้อาจคุ้มค่ากับการอภิปรายด้วยตัวเอง

สำหรับความต้องการของบทช่วยสอน ECR20 ของเรา เราจะใช้แนวทางที่ง่ายที่สุด: กำหนดจำนวนโทเค็นทั้งหมด ณ เวลาที่สร้างสัญญา และเริ่มต้นมอบหมายทั้งหมดให้กับ “เจ้าของสัญญา” เช่น บัญชีที่ปรับใช้สัญญาอัจฉริยะ:

 uint256 totalSupply_; constructor(uint256 total) public { totalSupply_ = total; balances[msg.sender] = _totalSupply; }

คอนสตรัคเตอร์เป็นฟังก์ชันพิเศษที่ Ethereum เรียกโดยอัตโนมัติทันทีหลังจากใช้งานสัญญา โดยทั่วไปจะใช้เพื่อเริ่มต้นสถานะของโทเค็นโดยใช้พารามิเตอร์ที่ส่งผ่านโดยบัญชีการปรับใช้ของสัญญา

msg เป็นตัวแปรส่วนกลางที่ประกาศและบรรจุโดย Ethereum เอง มีข้อมูลสำคัญสำหรับการทำสัญญา ฟิลด์ที่เราใช้อยู่ที่นี่: msg.sender มีบัญชี Ethereum ที่ใช้ฟังก์ชันสัญญาปัจจุบัน

เฉพาะบัญชีที่ใช้งานเท่านั้นที่สามารถเข้าสู่ตัวสร้างสัญญาได้ เมื่อสัญญาเริ่มต้นขึ้น ฟังก์ชันนี้จะจัดสรรโทเค็นที่มีให้กับบัญชี 'เจ้าของสัญญา'

รับ Token Supply ทั้งหมด

 function totalSupply() public view returns (uint256) { return totalSupply_; }

ฟังก์ชันนี้จะคืนค่าจำนวนของโทเค็นทั้งหมดที่จัดสรรโดยสัญญานี้โดยไม่คำนึงถึงเจ้าของ

รับโทเค็นยอดคงเหลือของเจ้าของ

 function balanceOf(address tokenOwner) public view returns (uint) { return balances[tokenOwner]; }

balanceOf จะส่งคืนยอดคงเหลือโทเค็นปัจจุบันของบัญชีซึ่งระบุโดยที่อยู่ของเจ้าของ

โอนโทเค็นไปยังบัญชีอื่น

 function transfer(address receiver, uint numTokens) public returns (bool) { require(numTokens <= balances[msg.sender]); balances[msg.sender] = balances[msg.sender] — numTokens; balances[receiver] = balances[receiver] + numTokens; emit Transfer(msg.sender, receiver, numTokens); return true; }

ตามชื่อของมัน ฟังก์ชันการ transfer ใช้เพื่อย้ายจำนวนโทเค็น numTokens จากยอดคงเหลือของเจ้าของไปยังผู้ใช้อื่น หรือ receiver เจ้าของการโอนคือ msg.sender นั่นคือผู้ดำเนินการฟังก์ชัน ซึ่งหมายความว่ามีเพียงเจ้าของโทเค็นเท่านั้นที่สามารถโอนให้ผู้อื่นได้

ต้องการวิธีการยืนยันเพรดิเคตของ require ในกรณีนี้บัญชีที่โอนมียอดคงเหลือเพียงพอในการดำเนินการโอน หากคำสั่ง require ล้มเหลว ธุรกรรมจะถูกย้อนกลับทันทีโดยไม่มีการเปลี่ยนแปลงในบล็อกเชน

ก่อนออกจากฟังก์ชันจะเริ่มต้น Transfer เหตุการณ์ ERC20 เพื่อให้ผู้ฟังที่ลงทะเบียนตอบสนองต่อการเสร็จสิ้น

อนุมัติผู้รับมอบสิทธิ์เพื่อถอนโทเค็น

ฟังก์ชันนี้มักใช้ในสถานการณ์ตลาดโทเค็น

 function approve(address delegate, uint numTokens) public returns (bool) { allowed[msg.sender][delegate] = numTokens; emit Approval(msg.sender, delegate, numTokens); return true; }

สิ่งที่การ approve ทำคือการอนุญาตให้เจ้าของ เช่น msg.sender อนุมัติบัญชีผู้รับมอบสิทธิ์ — อาจเป็นตลาดซื้อขายเอง — เพื่อถอนโทเค็นจากบัญชีของเขาและโอนไปยังบัญชีอื่น

อย่างที่คุณเห็น ฟังก์ชันนี้ใช้สำหรับสถานการณ์ที่เจ้าของเสนอโทเค็นในตลาดซื้อขาย อนุญาตให้ตลาดทำธุรกรรมให้เสร็จสิ้นโดยไม่ต้องรอการอนุมัติล่วงหน้า

เมื่อสิ้นสุดการดำเนินการ ฟังก์ชันนี้จะเริ่มต้นเหตุการณ์ Approval

รับจำนวนโทเค็นที่อนุมัติสำหรับการถอน

 function allowance(address owner, address delegate) public view returns (uint) { return allowed[owner][delegate]; }

ฟังก์ชันนี้จะคืนค่าจำนวนโทเค็นที่ได้รับอนุมัติในปัจจุบันโดยเจ้าของให้กับผู้รับมอบสิทธิ์เฉพาะ ตามที่กำหนดไว้ในฟังก์ชันการ approve

โอนโทเค็นโดยผู้รับมอบสิทธิ์

ฟังก์ชัน transferFrom เป็นฟังก์ชันเพียร์ของฟังก์ชันการ approve ซึ่งเราได้กล่าวถึงก่อนหน้านี้ อนุญาตให้ตัวแทนที่ได้รับอนุมัติให้ถอนเงินเพื่อโอนเงินของเจ้าของไปยังบัญชีบุคคลที่สาม

 function transferFrom(address owner, address buyer, uint numTokens) public returns (bool) { require(numTokens <= balances[owner]); require(numTokens <= allowed[owner][msg.sender]); balances[owner] = balances[owner] — numTokens; allowed[owner][msg.sender] = allowed[from][msg.sender] — numTokens; balances[buyer] = balances[buyer] + numTokens; Transfer(owner, buyer, numTokens); return true; }

คำสั่งสองข้อที่ require ในการเริ่มการทำงานคือการตรวจสอบว่าธุรกรรมนั้นถูกต้องหรือไม่ นั่นคือเจ้าของมีโทเค็นเพียงพอที่จะโอนและผู้รับมอบสิทธิ์ได้รับการอนุมัติ (อย่างน้อย) numTokens เพื่อถอนออก

นอกเหนือจากการโอนจำนวน numTokens จากเจ้าของไปยังผู้ซื้อแล้ว ฟังก์ชันนี้ยังลบ numTokens ออกจากค่าเผื่อของผู้รับมอบสิทธิ์ด้วย โดยพื้นฐานแล้วจะช่วยให้ผู้รับมอบสิทธิ์ที่ได้รับอนุญาตสามารถแบ่งการถอนออกได้หลายแบบ ซึ่งเป็นพฤติกรรมทั่วไปของตลาด

เราสามารถหยุดที่นี่และใช้ ERC20 ที่ถูกต้องได้ อย่างไรก็ตาม เราต้องการก้าวไปอีกขั้น เนื่องจากเราต้องการโทเค็นความแข็งแกร่งของอุตสาหกรรม สิ่งนี้ต้องการให้เราสร้างรหัสให้ปลอดภัยขึ้นเล็กน้อย แม้ว่าเราจะยังสามารถทำให้โทเค็นค่อนข้างง่ายได้ หากไม่ใช่แบบพื้นฐาน

SafeMath Solidity Library

SafeMath เป็นห้องสมุด Solidity ที่มุ่งจัดการกับแฮ็กเกอร์ทางเดียวที่ทราบว่าสามารถทำลายสัญญา: การโจมตีด้วยจำนวนเต็มล้น ในการโจมตีดังกล่าว แฮ็กเกอร์บังคับให้สัญญาใช้ค่าตัวเลขที่ไม่ถูกต้องโดยส่งพารามิเตอร์ที่จะนำจำนวนเต็มที่เกี่ยวข้อง ผ่าน ค่าสูงสุด

ห้องสมุด Safemath ใน Solidity: ภาพประกอบ

SafeMath ป้องกันสิ่งนี้โดยการทดสอบโอเวอร์โฟลว์ก่อนดำเนินการคำนวณ จึงขจัดอันตรายจากการโจมตีโอเวอร์โฟลว์ ห้องสมุดมีขนาดเล็กมากจนส่งผลกระทบต่อขนาดสัญญาเพียงเล็กน้อย ไม่ก่อให้เกิดประสิทธิภาพใด ๆ และมีค่าปรับเพียงเล็กน้อยจากต้นทุนการจัดเก็บ

มาเพิ่ม SafeMath ให้กับโค้ดของเรากันเถอะ:

 library SafeMath { // Only relevant functions function sub(uint256 a, uint256 b) internal pure returns (uint256) { assert(b <= a); return a — b; } function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; assert(c >= a); return c; } }

SafeMath ใช้คำสั่ง assert เพื่อตรวจสอบความถูกต้องของพารามิเตอร์ที่ส่งผ่าน หากการ assert ล้มเหลว การเรียกใช้ฟังก์ชันจะหยุดทันทีและการเปลี่ยนแปลงบล็อคเชนทั้งหมดจะถูกย้อนกลับ

ต่อไป ให้เราเพิ่มคำสั่งต่อไปนี้เพื่อแนะนำไลบรารีให้กับคอมไพเลอร์ Solidity:

using SafeMath for uint256;

จากนั้น เราจะแทนที่เลขคณิตไร้เดียงสาที่เราใช้ตอนต้นด้วยฟังก์ชัน SafeMath:

 balances[msg.sender] = balances[msg.sender].sub(numTokens); balances[receiver] = balances[receiver].add(numTokens); balances[buyer] = balances[buyer].add(numTokens); balances[owner] = balances[owner].sub(numTokens);

แพ็คคู่กันไปเลย

ใน Solidity ฟังก์ชันและเหตุการณ์ของสัญญาอัจฉริยะถูกรวมไว้ในเอนทิตีที่เรียกว่า สัญญา ซึ่งคุณสามารถแปลเป็น “คลาสบล็อคเชน” ได้ ด้านล่างนี้คือสัญญาที่เข้ากันได้กับ ERC20 ที่เราสร้างขึ้น รวมถึงส่วนสำคัญของรหัสของเรา ฟิลด์ชื่อและสัญลักษณ์สามารถเปลี่ยนแปลงได้ตามต้องการ โทเค็นส่วนใหญ่เก็บค่าทศนิยมไว้ที่ 18 ดังนั้นเราจะทำเช่นเดียวกัน

การปรับใช้สัญญา Ethereum

ถึงเวลาปรับใช้สัญญาของเรากับบล็อคเชนแล้ว หลังจากการปรับใช้ สัญญาของเราจะถูกโอนไปยังโหนดทั้งหมดที่เข้าร่วมในเครือข่าย การเปลี่ยนแปลงใดๆ และทั้งหมดที่ทำกับสัญญาจะเผยแพร่ไปยังโหนดที่เข้าร่วมทั้งหมด

นักพัฒนา Ethereum มักจะใช้เครื่องมือการปรับใช้ เช่น ทรัฟเฟิล แม้แต่ทรัฟเฟิลก็ยังเกินความสามารถสำหรับความต้องการที่จำกัดของบทความนี้ และเครื่องมือออนไลน์ง่ายๆ ที่เรียกว่า Remix ก็เพียงพอแล้ว

ในการใช้งาน คุณจะต้องติดตั้งปลั๊กอิน MetaMask บนเบราว์เซอร์ของคุณและบัญชี Rinkeby (เครือข่ายทดสอบ Ethereum) ที่มี Rinkeby Ether อย่างน้อยบางส่วน นี่เป็นขั้นตอนที่ค่อนข้างง่าย ดังนั้นเราจะไม่ลงรายละเอียด

ในกรณีที่คุณไม่มี ให้ไปที่ MetaMask และ Rinkeby เพื่อดูลิงก์ดาวน์โหลดและรับคำแนะนำในการติดตั้งและการใช้งานที่ชัดเจน

ตอนนี้เรามีพื้นฐานครบถ้วนแล้ว เราจะไปที่ Remix และวางโค้ดด้านบน ซึ่งรวมถึงบรรทัด Pragma และไลบรารี SafeMath ลงในเครื่องมือแก้ไขออนไลน์

จากนั้น เราจะข้ามไปที่แท็บที่สองทางด้านขวาที่เรียกว่า " เรียกใช้ " และคลิก " ปรับใช้ " ป๊อปอัป MetaMask จะปรากฏขึ้นเพื่อขอให้เรายืนยันธุรกรรม แน่นอน เราจะอนุมัติ

ข้อความแสดงแทนรูปภาพ

  • กล่องสีเขียว: ตรวจสอบให้แน่ใจว่าคุณอยู่ใน Rinkeby
  • กล่องสีน้ำเงิน: ตั้งค่าปริมาณโทเค็นทั้งหมดของคุณ
  • กล่องสีแดง: ปรับใช้!

สรุป : https://gist.github.com/giladHaimov/8e81dbde10c9aeff69a1d683ed6870be#file-basicerc20-sol

ยินดีด้วย! คุณเพิ่งปรับใช้โทเค็น ERC20 แรกของคุณ เหมือนมืออาชีพ Ethereum ตัวจริง ตามที่สัญญาไว้ โทเค็นนั้นเรียบง่ายและน้ำหนักเบา แต่ทำงานได้อย่างสมบูรณ์ เป็นไปตามมาตรฐาน ERC20 และปลอดภัยด้วย MathSafe พร้อมที่จะซื้อ ชำระเงิน และโอนผ่านบล็อกเชน

นั่นคือทั้งหมดที่มีในสัญญาอัจฉริยะหรือไม่?

ไม่ ไม่ได้ใกล้เคียงด้วยซ้ำ เนื่องจากการสาธิตสั้นๆ ของเราแทบไม่มีรอยขีดข่วนบนพื้นผิว และเกี่ยวข้องกับการพัฒนาสัญญาอัจฉริยะเพียงด้านเดียว

สัญญาอัจฉริยะอาจซับซ้อนกว่ามากขึ้นอยู่กับตรรกะทางธุรกิจของคุณ การสร้างแบบจำลองของการโต้ตอบกับผู้ใช้ ไม่ว่าคุณจะอนุญาตให้สร้างและเบิร์นโทเค็นหรือไม่ก็ตาม การเปลี่ยนแปลงวงจรชีวิตที่คุณแนะนำในสัญญา ความต้องการความสามารถระดับผู้ดูแลระบบซึ่งมักจะมาพร้อมกับ ชุดฟังก์ชันที่ได้รับอนุญาตจากผู้ดูแลระบบ และอื่นๆ คุณได้รับภาพ

อย่างไรก็ตาม หากคุณสามารถทำซ้ำสิ่งที่เราทำที่นี่ได้ นั่นเป็นรากฐานที่มั่นคงในการขยายความรู้ของคุณและไปสู่สัญญาที่ซับซ้อนมากขึ้นเมื่อจำเป็น