Leçon 2

การสร้างโทเค็นแรกของคุณด้วยมาตรฐาน FA1.2

ในบทนี้ เราจะพูดถึงกระบวนการสร้างโทเค็นที่เปลี่ยนได้โดยใช้มาตรฐาน FA1.2 บน Tezos เราจะใช้ SmartPy ออนไลน์ IDE เพื่อเขียนและปรับใช้สัญญาอัจฉริยะของเรา โปรดทราบว่ามาตรฐาน FA1.2 ส่วนใหญ่จะใช้สำหรับโทเค็นที่เปลี่ยนได้ ซึ่งหมายความว่าโทเค็นที่มีคุณสมบัติเหมือนกันและสามารถแลกเปลี่ยนได้แบบตัวต่อตัว

คำแนะนำทีละขั้นตอน

  1. การเข้าถึง SmartPy IDE

  2. ขั้นแรก เปิด SmartPy ออนไลน์ IDE ที่ https://smartpy.io/ide/ นี่คือแพลตฟอร์มที่เราจะใช้เขียน ทดสอบ และปรับใช้สัญญาอัจฉริยะของเรา

  3. การเริ่มต้นเทมเพลต FA1.2

  4. คลิกที่ “เทมเพลตตามประเภท” บนแถบด้านข้างซ้าย และเลือก “FA1.2” แท็บใหม่จะเปิดขึ้นพร้อมกับเทมเพลตสัญญา FA1.2 เป็นสัญญาพร้อมใช้ตามมาตรฐาน FA1.2

  5. ทำความเข้าใจกับเทมเพลต FA1.2

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

  7. ศึกษาเทมเพลตนี้และทำความเข้าใจฟังก์ชันต่างๆ ของเทมเพลตนี้ ไม่เป็นไรหากคุณไม่เข้าใจทุกอย่าง ณ จุดนี้ แต่พยายามทำความเข้าใจโดยทั่วไปเกี่ยวกับการดำเนินการที่สัญญานี้สามารถดำเนินการได้
    ตัวอย่างเช่น คุณสามารถคัดลอกโค้ดจากเทมเพลตบน SmartPy IDE หรือที่นี่ด้านล่าง:

Python 
 # Fungible Assets - FA12 
 # แรงบันดาลใจจาก https://gitlab.com/tzip/tzip/blob/master/A/FA1.2.md 

 นำเข้า smartpy as sp 


 # ข้อมูลเมตาด้านล่างเป็นเพียงตัวอย่างเท่านั้น ทำหน้าที่เป็นฐาน 
 # เนื้อหาใช้ในการสร้างข้อมูลเมตา JSON ที่ผู้ใช้ 
 # สามารถคัดลอกและอัปโหลดไปยัง IPFS
TZIP16_Metadata_Base = {
    "name": "SmartPy FA1.2 Token Template",
    "description": "Example Template for an FA1.2 Contract from SmartPy",
    "authors": ["SmartPy Dev Team"],
    "homepage": "https://smartpy.io",
    "interfaces": ["TZIP-007-2021-04-17", "TZIP-016-2021-04-17"],
}


@sp.module 
 def m(): 
 คลาส AdminInterface(sp.Contract): 
 @sp.private(with_storage="read-only") 
 def is_administrator_(ตนเอง, ผู้ส่ง): 
 sp .cast(sp.ผู้ส่ง, sp.address) 
 """ไม่ใช่มาตรฐาน อาจถูกกำหนดใหม่ผ่านการสืบทอด"""
            ส่งคืน True 

 คลาส CommonInterface(AdminInterface): 
 def __init__(self): 
 AdminInterface __init__(ตนเอง)
            self.data.balances = sp.cast (
                sp.big_map(), 
 sp.big_map[ 
 sp.address, 
 sp.record(การอนุมัติ=sp.map[sp.address, sp.nat], balance=sp.nat),
                ], 
 ) 
 ตนเอง.data.total_supply = 0 
 self.data.token_metadata = sp.cast (
                sp.big_map(), 
 sp.big_map[ 
 sp.nat, 
 sp.record(token_id=sp.nat, token_info=sp.map[sp.string, sp.bytes]), 
 ], 
 ) 
 self.data.metadata = sp.cast (
                sp.big_map(), 
 sp.big_map[sp.string, sp.bytes], 
 ) 
 self.data.balances = sp.cast (
                sp.big_map(), 
 sp.big_map[ 
 sp.address, 
 sp.record(การอนุมัติ=sp.map[sp.address, sp.nat], balance=sp.nat),
                ], 
 ) 
 ตนเอง.data.total_supply = 0 
 self.data.token_metadata = sp.cast (
                sp.big_map(), 
 sp.big_map[ 
 sp.nat, 
 sp.record(token_id=sp.nat, token_info=sp.map[sp.string, sp.bytes]), 
 ], 
 ) 
 self.data.metadata = sp.cast (
                sp.big_map(), 
 sp.big_map[sp.string, sp.bytes], 
 ) 

 @sp.private(with_storage="read-only") 
 def is_paused_(self): 
 """ไม่ใช่มาตรฐาน อาจถูกกำหนดใหม่ผ่านการสืบทอด"""
            กลับเท็จ 

 คลาส Fa1_2 (CommonInterface): 
 def __init__(ตนเอง, ข้อมูลเมตา, บัญชีแยกประเภท, token_metadata): 
 """ 
 ข้อมูลจำเพาะ token_metadata: https://gitlab.com/tzip/tzip/-/blob/master/ ข้อเสนอ/tzip-12/tzip-12.md#token-ข้อมูลเมตา
            ข้อมูลเมตาเฉพาะโทเค็นจะถูกจัดเก็บ/แสดงเป็นค่าประเภท Michelson (ไบต์สตริงของแผนที่)
            คีย์บางส่วนถูกสงวนและกำหนดไว้ล่วงหน้า: 

 - "" : ควรสอดคล้องกับ TZIP-016 URI ซึ่งชี้ไปที่การแสดง JSON ของข้อมูลเมตาโทเค็น
            - "name" : ควรเป็นสตริง UTF-8 ที่ให้ "ชื่อที่แสดง" ให้กับโทเค็น
            - "สัญลักษณ์" : ควรเป็นสตริง UTF-8 สำหรับตัวระบุแบบสั้นของโทเค็น (เช่น XTZ, ยูโร, …) 
 - "ทศนิยม" : ควรเป็นจำนวนเต็ม (แปลงเป็นสตริง UTF-8 ในรูปแบบทศนิยม) 
 ซึ่งกำหนดตำแหน่งของจุดทศนิยมในยอดโทเค็นเพื่อการแสดงผล

            ข้อมูลจำเพาะของสัญญา_metadata: https://gitlab.com/tzip/tzip/-/blob/master/proposals/tzip-16/tzip-16.md 
 """ 
 CommonInterface __init__(ตนเอง)
            self.data.metadata = ข้อมูลเมตา 
 self.data.token_metadata = sp.big_map(
                {0: sp.record(token_id=0, token_info=token_metadata)}
            ) 

 สำหรับเจ้าของใน ledger.items():
                self.data.balances [owner.key] = Owner.value 
 self.data.total_supply += Owner.value.balance 

 # TODO: เปิดใช้งานเมื่อมีการใช้คุณลักษณะนี้
            # self.init_metadata("ข้อมูลเมตา", ข้อมูลเมตา) 

 @sp.entrypoint การถ่ายโอน def 
 (ตนเอง, พารามิเตอร์): 
 sp.cast ( 
 พารามิเตอร์, 
 sp.record (from_=sp.address, to_=sp.ที่อยู่ value=sp.nat).เค้าโครง(
                    ("จาก_จาก", ("ถึง_จากถึง", "ค่า")) 
 ), 
 ) 
 balance_from = self.data.balances.get(
                พารามิเตอร์.from_, ค่าเริ่มต้น = sp.record (ยอดคงเหลือ = 0, การอนุมัติ={}) 
 ) 
 balance_to = self.data.balances.get(
                param.to_, ค่าเริ่มต้น = sp.record (ยอดคงเหลือ = 0, การอนุมัติ={}) 
 ) 
 balance_from.balance = sp.as_nat(
                balance_from.balance - พารามิเตอร์ค่า error="FA1.2_InsufficientBalance" 
 ) 
 balance_to.balance += param.value 
 หากไม่ใช่ self.is_administrator_(sp.sender):
                ยืนยันว่าไม่ใช่ self.is_paused_() "FA1.2_หยุดชั่วคราว"
                ถ้า param.from_ != sp.sender: 
 balance_from.approvals[sp.sender] = sp.as_nat(
                        balance_from.approvals[sp.sender] - param.value,
                        ข้อผิดพลาด = "FA1.2_NotAllowed", 
 ) 
 self.data.balances [param.from_] = balance_จาก 
 self.data.balances[param.to_] = balance_to 

 @sp.entrypoint 
 def อนุมัติ (ตนเอง, พารามิเตอร์): 
 sp.cast ( 
 พารามิเตอร์, 
 sp.record (spender=sp.address, value=sp.nat).เค้าโครง(
                    ("spender", "value") 
 ), 
 ) 
 ยืนยันไม่ใช่ self.is_paused_(), "FA1.2_หยุดชั่วคราว"
            exper_balance = self.data.balances.get (
                sp.sender ค่าเริ่มต้น = sp.record (ยอดคงเหลือ = 0, การอนุมัติ={}) 
 ) 
 อนุมัติแล้ว = allowanceer_balance.approvals.get(param.spender, ค่าเริ่มต้น = 0) 
 ยืนยัน ( 
 อนุมัติแล้ว == 0 หรือ param.value == 0 
 ), "FA1.2_UnsafeAllowanceChange"
            exper_balance.approvals[param.spender] = param.value 
 self.data.balances[sp.sender] =สปีดเดอร์_บาลานซ์ 

 @sp.entrypoint 
 def getBalance(ตนเอง, พารามิเตอร์): 
 (ที่อยู่, โทรกลับ) = พารามิเตอร์ 
 ผลลัพธ์ = self.data.balances.get(
                ที่อยู่ ค่าเริ่มต้น = sp.record (ยอดคงเหลือ = 0, การอนุมัติ={}) 
 ).balance 
 sp.transfer(result, sp.tez(0), callback) 

 @sp.entrypoint 
 def getAllowance(self, param): 
 (args, callback) = param 
 ผลลัพธ์ = self.data.balances.get (
                args.เจ้าของ, ค่าเริ่มต้น = sp.record (ยอดคงเหลือ = 0, การอนุมัติ={}) 
 ).approvals.get(args.spender, ค่าเริ่มต้น = 0) 
 sp.transfer (ผลลัพธ์, sp.tez (0), โทรกลับ) 

 @ sp.entrypoint 
 def getTotalSupply (ตนเอง, พารามิเตอร์): 
 sp.cast (param, sp.pair [sp.unit, sp.contract[sp.nat]])
            sp.transfer (self.data.total_supply, sp.tez(0), sp.snd(param)) 

 @sp.offchain_view() 
 def token_metadata(self, token_id): 
 """ส่งคืน URI โทเค็น-เมตาดาต้าสำหรับโทเค็นที่กำหนด (token_id ต้องเป็น 0)"""
            sp.cast (token_id, sp.nat) 
 ส่งคืน self.data.token_metadata [token_id]

    ########## 
 # มิกซ์อิน # 
 ##########

    class Admin(sp.Contract):
        def __init__(self, administrator):
            self.data.administrator = administrator

        @sp.private(with_storage="read-only")
        def is_administrator_(self, sender):
            return sender == self.data.administrator

        @sp.entrypoint
        def setAdministrator(self, params):
            sp.cast(params, sp.address)
            assert self.is_administrator_(sp.sender), "Fa1.2_NotAdmin"
            self.data.administrator = params

        @sp.entrypoint()
        def getAdministrator(self, param):
            sp.cast(param, sp.pair[sp.unit, sp.contract[sp.address]])
            sp.transfer(self.data.administrator, sp.tez(0), sp.snd(param))

        @sp.onchain_view()
        def get_administrator(self):
            return self.data.administrator

    class Pause(AdminInterface):
        def __init__(self):
            AdminInterface.__init__(self)
            self.data.paused = False

        @sp.private(with_storage="read-only")
        def is_paused_(self):
            return self.data.paused

        @sp.entrypoint
        def setPause(self, param):
            sp.cast(param, sp.bool)
            assert self.is_administrator_(sp.sender), "Fa1.2_NotAdmin"
            self.data.paused = param

    class Mint(CommonInterface):
        def __init__(self):
            CommonInterface.__init__(self)

        @sp.entrypoint
        def mint(self, param):
            sp.cast(param, sp.record(address=sp.address, value=sp.nat))
            assert self.is_administrator_(sp.sender), "Fa1.2_NotAdmin"
            receiver_balance = self.data.balances.get(
                param.address, default=sp.record(balance=0, approvals={})
            )
            receiver_balance.balance += param.value
            self.data.balances[param.address] = receiver_balance
            self.data.total_supply += param.value

    class Burn(CommonInterface):
        def __init__(self):
            CommonInterface.__init__(self)

        @sp.entrypoint
        def burn(self, param):
            sp.cast(param, sp.record(address=sp.address, value=sp.nat))
            assert self.is_administrator_(sp.sender), "Fa1.2_NotAdmin"
            receiver_balance = self.data.balances.get(
                param.address, default=sp.record(balance=0, approvals={})
            )
            receiver_balance.balance = sp.as_nat(
                receiver_balance.balance - param.value,
                error="FA1.2_InsufficientBalance",
            )
            self.data.balances[param.address] = receiver_balance
            self.data.total_supply = sp.as_nat(self.data.total_supply - param.value)

    class ChangeMetadata(CommonInterface):
        def __init__(self):
            CommonInterface.__init__(self)

        @sp.entrypoint
        def update_metadata(self, key, value):
            """An entrypoint to allow the contract metadata to be updated."""
            assert self.is_administrator_(sp.sender), "Fa1.2_NotAdmin"
            self.data.metadata[key] = value

    ########## 
 # การทดสอบ # 
 ########## 

 คลาส Fa1_2TestFull (ผู้ดูแลระบบ, หยุดชั่วคราว, Fa1_2, มิ้นท์, เบิร์น, ChangeMetadata): 
 def __init__(ตนเอง, ผู้ดูแลระบบ, ข้อมูลเมตา, บัญชีแยกประเภท, token_metadata): 
 ChangeMetadata __init__(ตนเอง)
            เผา.__init__(ตนเอง)
            สะระแหน่.__init__(ตนเอง)
            ฟ้า1_2.__init__(ตนเอง ข้อมูลเมตา, บัญชีแยกประเภท, token_metadata) 
 หยุดชั่วคราว __init__(ตนเอง)
            ผู้ดูแลระบบ __init__(ตนเอง ผู้ดูแลระบบ) 

 คลาส Viewer_nat (sp.Contract): 
 def __init__(ตนเอง): 
 self.data.last = sp.cast (ไม่มี, sp.option[sp.nat])

        @ sp.entrypoint 
 def target (self, params): 
 self.data.last = sp.Some (params) 

 คลาส Viewer_address (sp.Contract): 
 def __init__(self): 
 self.data.last = sp.cast(ไม่มี, sp.option[sp.address])

        @sp.entrypoint 
 def target(self, params): 
 self.data.last = sp.Some(params) 


 ถ้า "เทมเพลต" ไม่ได้อยู่ใน __name__: 

 @sp.add_test(name="FA12") 
 การทดสอบ def (): 
 sc = sp.test_scenario (m)
        sc.h1("เทมเพลต FA1.2 - สินทรัพย์ที่สามารถทดแทนได้") 

 # sp.test_account สร้างคู่คีย์ ED25519 ตามที่กำหนด: 
 admin = sp.test_account("Administrator")
        อลิซ = sp.test_account("อลิซ")
        บ๊อบ = sp.test_account("โรเบิร์ต")

        # มาแสดงบัญชีกันดีกว่า: 
 sc.h1("Accounts") 
 sc.show([admin, alice, bob]) 

 sc.h1("Contract") 
 token_metadata = {
            "decimals": sp.utils.bytes_of_string("18"),  # Mandatory by the spec
            "name": sp.utils.bytes_of_string("My Great Token"),  # Recommended
            "symbol": sp.utils.bytes_of_string("MGT"),  # Recommended
            # Extra fields
            "icon": sp.utils.bytes_of_string(
                "https://smartpy.io/static/img/logo-only.svg"
            ),
        }
        Contract_metadata = sp.utils ข้อมูลเมตา_of_url(
            "ipfs://QmaiAUj1FFNGYTu8rLBjc3eeN9cSKwaF8EGMBNDmhzPNFd" 
 ) 

 c1 = m.Fa1_2TestFull( 
 ผู้ดูแลระบบ=admin.address, 
 metadata=contract_metadata, 
 token_metadata=token_metadata, 
 ledger={}, 
 ) 
 sc += c1 

 sc .h1("มุมมอง Offchain - token_metadata") 
 sc.verify_equal( 
 sp.View(c1, "token_metadata")(0), 
 sp.record( 
 token_id=0, 
 token_info=sp.map(
                    {
                        "decimals": sp.utils.bytes_of_string("18"),
                        "name": sp.utils.bytes_of_string("My Great Token"),
                        "symbol": sp.utils.bytes_of_string("MGT"),
                        "icon": sp.utils.bytes_of_string(
                            "https://smartpy.io/static/img/logo-only.svg"
                        ),
                    }
                ), 
 ), 
 ) 

 sc.h1("พยายามอัปเดตข้อมูลเมตา") 
 sc.verify( 
 c1.data.metadata[""]
            == sp.utils.bytes_of_string (
                "ipfs://QmaiAUj1FFNGYTu8rLBjc3eeN9cSKwaF8EGMBNDmhzPNFd" 
 ) 
 ) 
 c1.update_metadata(key="", value=sp.bytes("0x00")).run(sender=admin) 
 sc.verify(c1.data.metadata[ ""] == sp.bytes("0x00")) 

 sc.h1("จุดเข้าใช้งาน") 
 sc.h2("ผู้ดูแลระบบสร้างเหรียญสองสามเหรียญ") 
 c1.mint(address=alice.address, value=12).run (ผู้ส่ง=ผู้ดูแลระบบ)
        c1.mint(ที่อยู่=alice.address, value=3).run(ผู้ส่ง=admin)
        c1.mint(ที่อยู่=alice.address, value=3).run(ผู้ส่ง=admin)
        sc.h2("อลิซโอนไปยังบ๊อบ") 
 c1.transfer(from_=alice.address, ถึง_=bob.address, ค่า=4).รัน(ผู้ส่ง=อลิซ)
        sc.verify(c1.data.balances[alice.address].balance == 14) 
 sc.h2("Bob พยายามย้ายจาก Alice แต่เขาไม่ได้รับการอนุมัติจากเธอ") 
 c1.transfer(from_=alice.address, ถึง_=bob.address, ค่า=4).รัน(
            sender=bob, valid=False 
 ) 
 sc.h2("Alice อนุมัติการโอน Bob และ Bob") 
 c1.approve(spender=bob.address, value=5).run(sender=alice)
        c1.transfer(จาก_=อลิซ.ที่อยู่, ถึง_=bob.address, ค่า=4).รัน(ผู้ส่ง=บ๊อบ)
        sc.h2("Bob พยายามถ่ายโอนจากอลิซมากเกินไป") 
 c1.transfer(from_=alice.address, ถึง_=bob.address, ค่า=4).รัน(
            sender=bob, valid=False 
 ) 
 sc.h2("ผู้ดูแลระบบเบิร์นโทเค็น Bob") 
 c1.burn(address=bob.address, ค่า=1).รัน(ผู้ส่ง=ผู้ดูแลระบบ)
        sc.verify(c1.data.balances[alice.address].balance == 10) 
 sc.h2("อลิซพยายามเผาโทเค็น Bob") 
 c1.burn(address=bob.address, ค่า=1).รัน(ผู้ส่ง=อลิซ, valid=False) 
 sc.h2("ผู้ดูแลระบบหยุดสัญญาชั่วคราวและอลิซไม่สามารถถ่ายโอนได้อีกต่อไป") 
 c1.setPause(True).run(sender=admin)
        c1.transfer(จาก_=อลิซ.ที่อยู่, ถึง_=bob.address, ค่า=4).รัน(
            ผู้ส่ง = อลิซ ถูกต้อง = เท็จ 
 ) 
 sc.verify (c1.data.balances [alice.address].balance == 10) 
 sc.h2("ผู้ดูแลระบบถ่ายโอนขณะหยุดชั่วคราว") 
 c1.transfer(from_=alice.address, ถึง_=bob.address, ค่า=1).รัน(ผู้ส่ง=ผู้ดูแลระบบ)
        sc.h2("ผู้ดูแลระบบยกเลิกการหยุดสัญญาชั่วคราวและอนุญาตให้ถ่ายโอนได้") 
 c1.setPause(False).run(sender=admin)
        sc.verify(c1.data.balances[alice.address].balance == 9) 
 c1.transfer(from_=alice.address, ถึง_=bob.address, ค่า=1).รัน(ผู้ส่ง=อลิซ)

        sc.ตรวจสอบ(c1.data.total_supply == 17) 
 sc.verify(c1.data.balances[alice.address].balance == 8) 
 sc.verify(c1.data.balances[bob.address].balance == 9) 

 sc.h1("มุมมอง") 
 sc.h2("Balance") 
 view_balance = m.Viewer_nat() 
 sc += view_balance 
 เป้าหมาย = sp.contract(sp.TNat, view_balance.address, "เป้าหมาย").open_some() 
 c1.getBalance((alice.address, เป้าหมาย)) 
 sc.verify_equal(view_balance.data.last, sp.some(8)) 

 sc.h2("ผู้ดูแลระบบ") 
 view_administrator = m.Viewer_address() 
 sc += view_administrator 
 เป้าหมาย = sp.contract(
            sp.TAddress, view_administrator.address, "เป้าหมาย" 
 ).open_some() 
 c1.getAdministrator((sp.unit, เป้าหมาย)) 
 sc.verify_equal(view_administrator.data.last, sp.some(admin.address))

        sc.h2("อุปทานทั้งหมด") 
 view_totalSupply = m.Viewer_nat() 
 sc += view_totalSupply 
 เป้าหมาย = sp.contract(sp.TNat, view_totalSupply.address, "เป้าหมาย").open_some() 
 c1.getTotalSupply((sp.unit, เป้าหมาย)) 
 sc.verify_equal(view_totalSupply.data.last, sp.some(17)) 

 sc.h2("ค่าเผื่อ") 
 view_allowance = m.Viewer_nat() 
 sc += view_allowance 
 เป้าหมาย = sp.contract(sp.TNat, view_allowance.address, "เป้าหมาย").open_some() 
 c1.getAllowance((sp.record(owner=alice.address, allowance=bob.address), เป้าหมาย)) 
 sc.verify_equal(view_allowance.data.last, sp.บาง(1))
  1. ดำเนินการตามสัญญา คุณจะเห็นบางอย่างเช่นนี้

สัญญา FA1.2 ดั้งเดิมมีฟังก์ชันพื้นฐาน เช่น การโอนโทเค็น การอนุมัติการโอน การตรวจสอบยอดคงเหลือ และการดูอุปทานทั้งหมดของโทเค็น ตอนนี้เราจะปรับปรุงฟังก์ชันนี้

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

ยินดีด้วย! คุณได้สร้างโทเค็นที่ใช้งานได้เป็นครั้งแรกบน Tezos โดยใช้มาตรฐาน FA1.2!

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

Clause de non-responsabilité
* Les investissements en cryptomonnaies comportent des risques importants. Veuillez faire preuve de prudence. Le cours n'est pas destiné à fournir des conseils en investissement.
* Ce cours a été créé par l'auteur qui a rejoint Gate Learn. Toute opinion partagée par l'auteur ne représente pas Gate Learn.
Catalogue
Leçon 2

การสร้างโทเค็นแรกของคุณด้วยมาตรฐาน FA1.2

ในบทนี้ เราจะพูดถึงกระบวนการสร้างโทเค็นที่เปลี่ยนได้โดยใช้มาตรฐาน FA1.2 บน Tezos เราจะใช้ SmartPy ออนไลน์ IDE เพื่อเขียนและปรับใช้สัญญาอัจฉริยะของเรา โปรดทราบว่ามาตรฐาน FA1.2 ส่วนใหญ่จะใช้สำหรับโทเค็นที่เปลี่ยนได้ ซึ่งหมายความว่าโทเค็นที่มีคุณสมบัติเหมือนกันและสามารถแลกเปลี่ยนได้แบบตัวต่อตัว

คำแนะนำทีละขั้นตอน

  1. การเข้าถึง SmartPy IDE

  2. ขั้นแรก เปิด SmartPy ออนไลน์ IDE ที่ https://smartpy.io/ide/ นี่คือแพลตฟอร์มที่เราจะใช้เขียน ทดสอบ และปรับใช้สัญญาอัจฉริยะของเรา

  3. การเริ่มต้นเทมเพลต FA1.2

  4. คลิกที่ “เทมเพลตตามประเภท” บนแถบด้านข้างซ้าย และเลือก “FA1.2” แท็บใหม่จะเปิดขึ้นพร้อมกับเทมเพลตสัญญา FA1.2 เป็นสัญญาพร้อมใช้ตามมาตรฐาน FA1.2

  5. ทำความเข้าใจกับเทมเพลต FA1.2

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

  7. ศึกษาเทมเพลตนี้และทำความเข้าใจฟังก์ชันต่างๆ ของเทมเพลตนี้ ไม่เป็นไรหากคุณไม่เข้าใจทุกอย่าง ณ จุดนี้ แต่พยายามทำความเข้าใจโดยทั่วไปเกี่ยวกับการดำเนินการที่สัญญานี้สามารถดำเนินการได้
    ตัวอย่างเช่น คุณสามารถคัดลอกโค้ดจากเทมเพลตบน SmartPy IDE หรือที่นี่ด้านล่าง:

Python 
 # Fungible Assets - FA12 
 # แรงบันดาลใจจาก https://gitlab.com/tzip/tzip/blob/master/A/FA1.2.md 

 นำเข้า smartpy as sp 


 # ข้อมูลเมตาด้านล่างเป็นเพียงตัวอย่างเท่านั้น ทำหน้าที่เป็นฐาน 
 # เนื้อหาใช้ในการสร้างข้อมูลเมตา JSON ที่ผู้ใช้ 
 # สามารถคัดลอกและอัปโหลดไปยัง IPFS
TZIP16_Metadata_Base = {
    "name": "SmartPy FA1.2 Token Template",
    "description": "Example Template for an FA1.2 Contract from SmartPy",
    "authors": ["SmartPy Dev Team"],
    "homepage": "https://smartpy.io",
    "interfaces": ["TZIP-007-2021-04-17", "TZIP-016-2021-04-17"],
}


@sp.module 
 def m(): 
 คลาส AdminInterface(sp.Contract): 
 @sp.private(with_storage="read-only") 
 def is_administrator_(ตนเอง, ผู้ส่ง): 
 sp .cast(sp.ผู้ส่ง, sp.address) 
 """ไม่ใช่มาตรฐาน อาจถูกกำหนดใหม่ผ่านการสืบทอด"""
            ส่งคืน True 

 คลาส CommonInterface(AdminInterface): 
 def __init__(self): 
 AdminInterface __init__(ตนเอง)
            self.data.balances = sp.cast (
                sp.big_map(), 
 sp.big_map[ 
 sp.address, 
 sp.record(การอนุมัติ=sp.map[sp.address, sp.nat], balance=sp.nat),
                ], 
 ) 
 ตนเอง.data.total_supply = 0 
 self.data.token_metadata = sp.cast (
                sp.big_map(), 
 sp.big_map[ 
 sp.nat, 
 sp.record(token_id=sp.nat, token_info=sp.map[sp.string, sp.bytes]), 
 ], 
 ) 
 self.data.metadata = sp.cast (
                sp.big_map(), 
 sp.big_map[sp.string, sp.bytes], 
 ) 
 self.data.balances = sp.cast (
                sp.big_map(), 
 sp.big_map[ 
 sp.address, 
 sp.record(การอนุมัติ=sp.map[sp.address, sp.nat], balance=sp.nat),
                ], 
 ) 
 ตนเอง.data.total_supply = 0 
 self.data.token_metadata = sp.cast (
                sp.big_map(), 
 sp.big_map[ 
 sp.nat, 
 sp.record(token_id=sp.nat, token_info=sp.map[sp.string, sp.bytes]), 
 ], 
 ) 
 self.data.metadata = sp.cast (
                sp.big_map(), 
 sp.big_map[sp.string, sp.bytes], 
 ) 

 @sp.private(with_storage="read-only") 
 def is_paused_(self): 
 """ไม่ใช่มาตรฐาน อาจถูกกำหนดใหม่ผ่านการสืบทอด"""
            กลับเท็จ 

 คลาส Fa1_2 (CommonInterface): 
 def __init__(ตนเอง, ข้อมูลเมตา, บัญชีแยกประเภท, token_metadata): 
 """ 
 ข้อมูลจำเพาะ token_metadata: https://gitlab.com/tzip/tzip/-/blob/master/ ข้อเสนอ/tzip-12/tzip-12.md#token-ข้อมูลเมตา
            ข้อมูลเมตาเฉพาะโทเค็นจะถูกจัดเก็บ/แสดงเป็นค่าประเภท Michelson (ไบต์สตริงของแผนที่)
            คีย์บางส่วนถูกสงวนและกำหนดไว้ล่วงหน้า: 

 - "" : ควรสอดคล้องกับ TZIP-016 URI ซึ่งชี้ไปที่การแสดง JSON ของข้อมูลเมตาโทเค็น
            - "name" : ควรเป็นสตริง UTF-8 ที่ให้ "ชื่อที่แสดง" ให้กับโทเค็น
            - "สัญลักษณ์" : ควรเป็นสตริง UTF-8 สำหรับตัวระบุแบบสั้นของโทเค็น (เช่น XTZ, ยูโร, …) 
 - "ทศนิยม" : ควรเป็นจำนวนเต็ม (แปลงเป็นสตริง UTF-8 ในรูปแบบทศนิยม) 
 ซึ่งกำหนดตำแหน่งของจุดทศนิยมในยอดโทเค็นเพื่อการแสดงผล

            ข้อมูลจำเพาะของสัญญา_metadata: https://gitlab.com/tzip/tzip/-/blob/master/proposals/tzip-16/tzip-16.md 
 """ 
 CommonInterface __init__(ตนเอง)
            self.data.metadata = ข้อมูลเมตา 
 self.data.token_metadata = sp.big_map(
                {0: sp.record(token_id=0, token_info=token_metadata)}
            ) 

 สำหรับเจ้าของใน ledger.items():
                self.data.balances [owner.key] = Owner.value 
 self.data.total_supply += Owner.value.balance 

 # TODO: เปิดใช้งานเมื่อมีการใช้คุณลักษณะนี้
            # self.init_metadata("ข้อมูลเมตา", ข้อมูลเมตา) 

 @sp.entrypoint การถ่ายโอน def 
 (ตนเอง, พารามิเตอร์): 
 sp.cast ( 
 พารามิเตอร์, 
 sp.record (from_=sp.address, to_=sp.ที่อยู่ value=sp.nat).เค้าโครง(
                    ("จาก_จาก", ("ถึง_จากถึง", "ค่า")) 
 ), 
 ) 
 balance_from = self.data.balances.get(
                พารามิเตอร์.from_, ค่าเริ่มต้น = sp.record (ยอดคงเหลือ = 0, การอนุมัติ={}) 
 ) 
 balance_to = self.data.balances.get(
                param.to_, ค่าเริ่มต้น = sp.record (ยอดคงเหลือ = 0, การอนุมัติ={}) 
 ) 
 balance_from.balance = sp.as_nat(
                balance_from.balance - พารามิเตอร์ค่า error="FA1.2_InsufficientBalance" 
 ) 
 balance_to.balance += param.value 
 หากไม่ใช่ self.is_administrator_(sp.sender):
                ยืนยันว่าไม่ใช่ self.is_paused_() "FA1.2_หยุดชั่วคราว"
                ถ้า param.from_ != sp.sender: 
 balance_from.approvals[sp.sender] = sp.as_nat(
                        balance_from.approvals[sp.sender] - param.value,
                        ข้อผิดพลาด = "FA1.2_NotAllowed", 
 ) 
 self.data.balances [param.from_] = balance_จาก 
 self.data.balances[param.to_] = balance_to 

 @sp.entrypoint 
 def อนุมัติ (ตนเอง, พารามิเตอร์): 
 sp.cast ( 
 พารามิเตอร์, 
 sp.record (spender=sp.address, value=sp.nat).เค้าโครง(
                    ("spender", "value") 
 ), 
 ) 
 ยืนยันไม่ใช่ self.is_paused_(), "FA1.2_หยุดชั่วคราว"
            exper_balance = self.data.balances.get (
                sp.sender ค่าเริ่มต้น = sp.record (ยอดคงเหลือ = 0, การอนุมัติ={}) 
 ) 
 อนุมัติแล้ว = allowanceer_balance.approvals.get(param.spender, ค่าเริ่มต้น = 0) 
 ยืนยัน ( 
 อนุมัติแล้ว == 0 หรือ param.value == 0 
 ), "FA1.2_UnsafeAllowanceChange"
            exper_balance.approvals[param.spender] = param.value 
 self.data.balances[sp.sender] =สปีดเดอร์_บาลานซ์ 

 @sp.entrypoint 
 def getBalance(ตนเอง, พารามิเตอร์): 
 (ที่อยู่, โทรกลับ) = พารามิเตอร์ 
 ผลลัพธ์ = self.data.balances.get(
                ที่อยู่ ค่าเริ่มต้น = sp.record (ยอดคงเหลือ = 0, การอนุมัติ={}) 
 ).balance 
 sp.transfer(result, sp.tez(0), callback) 

 @sp.entrypoint 
 def getAllowance(self, param): 
 (args, callback) = param 
 ผลลัพธ์ = self.data.balances.get (
                args.เจ้าของ, ค่าเริ่มต้น = sp.record (ยอดคงเหลือ = 0, การอนุมัติ={}) 
 ).approvals.get(args.spender, ค่าเริ่มต้น = 0) 
 sp.transfer (ผลลัพธ์, sp.tez (0), โทรกลับ) 

 @ sp.entrypoint 
 def getTotalSupply (ตนเอง, พารามิเตอร์): 
 sp.cast (param, sp.pair [sp.unit, sp.contract[sp.nat]])
            sp.transfer (self.data.total_supply, sp.tez(0), sp.snd(param)) 

 @sp.offchain_view() 
 def token_metadata(self, token_id): 
 """ส่งคืน URI โทเค็น-เมตาดาต้าสำหรับโทเค็นที่กำหนด (token_id ต้องเป็น 0)"""
            sp.cast (token_id, sp.nat) 
 ส่งคืน self.data.token_metadata [token_id]

    ########## 
 # มิกซ์อิน # 
 ##########

    class Admin(sp.Contract):
        def __init__(self, administrator):
            self.data.administrator = administrator

        @sp.private(with_storage="read-only")
        def is_administrator_(self, sender):
            return sender == self.data.administrator

        @sp.entrypoint
        def setAdministrator(self, params):
            sp.cast(params, sp.address)
            assert self.is_administrator_(sp.sender), "Fa1.2_NotAdmin"
            self.data.administrator = params

        @sp.entrypoint()
        def getAdministrator(self, param):
            sp.cast(param, sp.pair[sp.unit, sp.contract[sp.address]])
            sp.transfer(self.data.administrator, sp.tez(0), sp.snd(param))

        @sp.onchain_view()
        def get_administrator(self):
            return self.data.administrator

    class Pause(AdminInterface):
        def __init__(self):
            AdminInterface.__init__(self)
            self.data.paused = False

        @sp.private(with_storage="read-only")
        def is_paused_(self):
            return self.data.paused

        @sp.entrypoint
        def setPause(self, param):
            sp.cast(param, sp.bool)
            assert self.is_administrator_(sp.sender), "Fa1.2_NotAdmin"
            self.data.paused = param

    class Mint(CommonInterface):
        def __init__(self):
            CommonInterface.__init__(self)

        @sp.entrypoint
        def mint(self, param):
            sp.cast(param, sp.record(address=sp.address, value=sp.nat))
            assert self.is_administrator_(sp.sender), "Fa1.2_NotAdmin"
            receiver_balance = self.data.balances.get(
                param.address, default=sp.record(balance=0, approvals={})
            )
            receiver_balance.balance += param.value
            self.data.balances[param.address] = receiver_balance
            self.data.total_supply += param.value

    class Burn(CommonInterface):
        def __init__(self):
            CommonInterface.__init__(self)

        @sp.entrypoint
        def burn(self, param):
            sp.cast(param, sp.record(address=sp.address, value=sp.nat))
            assert self.is_administrator_(sp.sender), "Fa1.2_NotAdmin"
            receiver_balance = self.data.balances.get(
                param.address, default=sp.record(balance=0, approvals={})
            )
            receiver_balance.balance = sp.as_nat(
                receiver_balance.balance - param.value,
                error="FA1.2_InsufficientBalance",
            )
            self.data.balances[param.address] = receiver_balance
            self.data.total_supply = sp.as_nat(self.data.total_supply - param.value)

    class ChangeMetadata(CommonInterface):
        def __init__(self):
            CommonInterface.__init__(self)

        @sp.entrypoint
        def update_metadata(self, key, value):
            """An entrypoint to allow the contract metadata to be updated."""
            assert self.is_administrator_(sp.sender), "Fa1.2_NotAdmin"
            self.data.metadata[key] = value

    ########## 
 # การทดสอบ # 
 ########## 

 คลาส Fa1_2TestFull (ผู้ดูแลระบบ, หยุดชั่วคราว, Fa1_2, มิ้นท์, เบิร์น, ChangeMetadata): 
 def __init__(ตนเอง, ผู้ดูแลระบบ, ข้อมูลเมตา, บัญชีแยกประเภท, token_metadata): 
 ChangeMetadata __init__(ตนเอง)
            เผา.__init__(ตนเอง)
            สะระแหน่.__init__(ตนเอง)
            ฟ้า1_2.__init__(ตนเอง ข้อมูลเมตา, บัญชีแยกประเภท, token_metadata) 
 หยุดชั่วคราว __init__(ตนเอง)
            ผู้ดูแลระบบ __init__(ตนเอง ผู้ดูแลระบบ) 

 คลาส Viewer_nat (sp.Contract): 
 def __init__(ตนเอง): 
 self.data.last = sp.cast (ไม่มี, sp.option[sp.nat])

        @ sp.entrypoint 
 def target (self, params): 
 self.data.last = sp.Some (params) 

 คลาส Viewer_address (sp.Contract): 
 def __init__(self): 
 self.data.last = sp.cast(ไม่มี, sp.option[sp.address])

        @sp.entrypoint 
 def target(self, params): 
 self.data.last = sp.Some(params) 


 ถ้า "เทมเพลต" ไม่ได้อยู่ใน __name__: 

 @sp.add_test(name="FA12") 
 การทดสอบ def (): 
 sc = sp.test_scenario (m)
        sc.h1("เทมเพลต FA1.2 - สินทรัพย์ที่สามารถทดแทนได้") 

 # sp.test_account สร้างคู่คีย์ ED25519 ตามที่กำหนด: 
 admin = sp.test_account("Administrator")
        อลิซ = sp.test_account("อลิซ")
        บ๊อบ = sp.test_account("โรเบิร์ต")

        # มาแสดงบัญชีกันดีกว่า: 
 sc.h1("Accounts") 
 sc.show([admin, alice, bob]) 

 sc.h1("Contract") 
 token_metadata = {
            "decimals": sp.utils.bytes_of_string("18"),  # Mandatory by the spec
            "name": sp.utils.bytes_of_string("My Great Token"),  # Recommended
            "symbol": sp.utils.bytes_of_string("MGT"),  # Recommended
            # Extra fields
            "icon": sp.utils.bytes_of_string(
                "https://smartpy.io/static/img/logo-only.svg"
            ),
        }
        Contract_metadata = sp.utils ข้อมูลเมตา_of_url(
            "ipfs://QmaiAUj1FFNGYTu8rLBjc3eeN9cSKwaF8EGMBNDmhzPNFd" 
 ) 

 c1 = m.Fa1_2TestFull( 
 ผู้ดูแลระบบ=admin.address, 
 metadata=contract_metadata, 
 token_metadata=token_metadata, 
 ledger={}, 
 ) 
 sc += c1 

 sc .h1("มุมมอง Offchain - token_metadata") 
 sc.verify_equal( 
 sp.View(c1, "token_metadata")(0), 
 sp.record( 
 token_id=0, 
 token_info=sp.map(
                    {
                        "decimals": sp.utils.bytes_of_string("18"),
                        "name": sp.utils.bytes_of_string("My Great Token"),
                        "symbol": sp.utils.bytes_of_string("MGT"),
                        "icon": sp.utils.bytes_of_string(
                            "https://smartpy.io/static/img/logo-only.svg"
                        ),
                    }
                ), 
 ), 
 ) 

 sc.h1("พยายามอัปเดตข้อมูลเมตา") 
 sc.verify( 
 c1.data.metadata[""]
            == sp.utils.bytes_of_string (
                "ipfs://QmaiAUj1FFNGYTu8rLBjc3eeN9cSKwaF8EGMBNDmhzPNFd" 
 ) 
 ) 
 c1.update_metadata(key="", value=sp.bytes("0x00")).run(sender=admin) 
 sc.verify(c1.data.metadata[ ""] == sp.bytes("0x00")) 

 sc.h1("จุดเข้าใช้งาน") 
 sc.h2("ผู้ดูแลระบบสร้างเหรียญสองสามเหรียญ") 
 c1.mint(address=alice.address, value=12).run (ผู้ส่ง=ผู้ดูแลระบบ)
        c1.mint(ที่อยู่=alice.address, value=3).run(ผู้ส่ง=admin)
        c1.mint(ที่อยู่=alice.address, value=3).run(ผู้ส่ง=admin)
        sc.h2("อลิซโอนไปยังบ๊อบ") 
 c1.transfer(from_=alice.address, ถึง_=bob.address, ค่า=4).รัน(ผู้ส่ง=อลิซ)
        sc.verify(c1.data.balances[alice.address].balance == 14) 
 sc.h2("Bob พยายามย้ายจาก Alice แต่เขาไม่ได้รับการอนุมัติจากเธอ") 
 c1.transfer(from_=alice.address, ถึง_=bob.address, ค่า=4).รัน(
            sender=bob, valid=False 
 ) 
 sc.h2("Alice อนุมัติการโอน Bob และ Bob") 
 c1.approve(spender=bob.address, value=5).run(sender=alice)
        c1.transfer(จาก_=อลิซ.ที่อยู่, ถึง_=bob.address, ค่า=4).รัน(ผู้ส่ง=บ๊อบ)
        sc.h2("Bob พยายามถ่ายโอนจากอลิซมากเกินไป") 
 c1.transfer(from_=alice.address, ถึง_=bob.address, ค่า=4).รัน(
            sender=bob, valid=False 
 ) 
 sc.h2("ผู้ดูแลระบบเบิร์นโทเค็น Bob") 
 c1.burn(address=bob.address, ค่า=1).รัน(ผู้ส่ง=ผู้ดูแลระบบ)
        sc.verify(c1.data.balances[alice.address].balance == 10) 
 sc.h2("อลิซพยายามเผาโทเค็น Bob") 
 c1.burn(address=bob.address, ค่า=1).รัน(ผู้ส่ง=อลิซ, valid=False) 
 sc.h2("ผู้ดูแลระบบหยุดสัญญาชั่วคราวและอลิซไม่สามารถถ่ายโอนได้อีกต่อไป") 
 c1.setPause(True).run(sender=admin)
        c1.transfer(จาก_=อลิซ.ที่อยู่, ถึง_=bob.address, ค่า=4).รัน(
            ผู้ส่ง = อลิซ ถูกต้อง = เท็จ 
 ) 
 sc.verify (c1.data.balances [alice.address].balance == 10) 
 sc.h2("ผู้ดูแลระบบถ่ายโอนขณะหยุดชั่วคราว") 
 c1.transfer(from_=alice.address, ถึง_=bob.address, ค่า=1).รัน(ผู้ส่ง=ผู้ดูแลระบบ)
        sc.h2("ผู้ดูแลระบบยกเลิกการหยุดสัญญาชั่วคราวและอนุญาตให้ถ่ายโอนได้") 
 c1.setPause(False).run(sender=admin)
        sc.verify(c1.data.balances[alice.address].balance == 9) 
 c1.transfer(from_=alice.address, ถึง_=bob.address, ค่า=1).รัน(ผู้ส่ง=อลิซ)

        sc.ตรวจสอบ(c1.data.total_supply == 17) 
 sc.verify(c1.data.balances[alice.address].balance == 8) 
 sc.verify(c1.data.balances[bob.address].balance == 9) 

 sc.h1("มุมมอง") 
 sc.h2("Balance") 
 view_balance = m.Viewer_nat() 
 sc += view_balance 
 เป้าหมาย = sp.contract(sp.TNat, view_balance.address, "เป้าหมาย").open_some() 
 c1.getBalance((alice.address, เป้าหมาย)) 
 sc.verify_equal(view_balance.data.last, sp.some(8)) 

 sc.h2("ผู้ดูแลระบบ") 
 view_administrator = m.Viewer_address() 
 sc += view_administrator 
 เป้าหมาย = sp.contract(
            sp.TAddress, view_administrator.address, "เป้าหมาย" 
 ).open_some() 
 c1.getAdministrator((sp.unit, เป้าหมาย)) 
 sc.verify_equal(view_administrator.data.last, sp.some(admin.address))

        sc.h2("อุปทานทั้งหมด") 
 view_totalSupply = m.Viewer_nat() 
 sc += view_totalSupply 
 เป้าหมาย = sp.contract(sp.TNat, view_totalSupply.address, "เป้าหมาย").open_some() 
 c1.getTotalSupply((sp.unit, เป้าหมาย)) 
 sc.verify_equal(view_totalSupply.data.last, sp.some(17)) 

 sc.h2("ค่าเผื่อ") 
 view_allowance = m.Viewer_nat() 
 sc += view_allowance 
 เป้าหมาย = sp.contract(sp.TNat, view_allowance.address, "เป้าหมาย").open_some() 
 c1.getAllowance((sp.record(owner=alice.address, allowance=bob.address), เป้าหมาย)) 
 sc.verify_equal(view_allowance.data.last, sp.บาง(1))
  1. ดำเนินการตามสัญญา คุณจะเห็นบางอย่างเช่นนี้

สัญญา FA1.2 ดั้งเดิมมีฟังก์ชันพื้นฐาน เช่น การโอนโทเค็น การอนุมัติการโอน การตรวจสอบยอดคงเหลือ และการดูอุปทานทั้งหมดของโทเค็น ตอนนี้เราจะปรับปรุงฟังก์ชันนี้

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

ยินดีด้วย! คุณได้สร้างโทเค็นที่ใช้งานได้เป็นครั้งแรกบน Tezos โดยใช้มาตรฐาน FA1.2!

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

Clause de non-responsabilité
* Les investissements en cryptomonnaies comportent des risques importants. Veuillez faire preuve de prudence. Le cours n'est pas destiné à fournir des conseils en investissement.
* Ce cours a été créé par l'auteur qui a rejoint Gate Learn. Toute opinion partagée par l'auteur ne représente pas Gate Learn.