āļšāļ—āđ€āļĢāļĩāļĒāļ™āļ—āļĩāđˆ 2

Membuat Token Pertama Anda dengan Standar FA1.2

Dalam pelajaran ini, kita akan membahas proses pembuatan token yang sepadan menggunakan standar FA1.2 di Tezos. Kami akan menggunakan IDE online SmartPy untuk menulis dan menerapkan kontrak pintar kami. Perlu diingat bahwa standar FA1.2 terutama digunakan untuk token yang sepadan, yang berarti token yang memiliki properti identik dan dapat ditukar dengan basis satu-untuk-satu.

Panduan Langkah demi Langkah

  1. Mengakses IDE SmartPy

  2. Pertama, buka IDE online SmartPy di https://smartpy.io/ide/. Ini adalah platform yang akan kami gunakan untuk menulis, menguji, dan menerapkan kontrak pintar kami.

  3. Memulai Templat FA1.2

  4. Klik “Templates by Type” di sidebar kiri dan pilih “FA1.2”. Tab baru akan terbuka dengan templat kontrak FA1.2. Ini adalah kontrak siap pakai yang mengikuti standar FA1.2.

  5. Memahami Templat FA1.2

  6. Templat ini memiliki fungsi dasar untuk token yang sepadan, yang mencakup transfer token, persetujuan transfer, pengecekan saldo, dan melihat total pasokan token. Kontrak tersebut juga mencakup fungsionalitas tambahan untuk mencetak dan membakar token, serta untuk manajemen tata kelola.

  7. Pelajari template ini dan pastikan Anda memahami fungsinya. Tidak apa-apa jika Anda tidak memahami semuanya saat ini, namun cobalah memahami secara umum operasi yang dapat dilakukan kontrak ini.
    Misalnya, Anda dapat menyalin kode dari template di SmartPy IDE atau di bawah ini:

Python 
 # Aset Fungible - FA12 
 # Terinspirasi oleh https://gitlab.com/tzip/tzip/blob/master/A/FA1.2.md 

 import smartpy as sp 


 # Metadata di bawah hanyalah sebuah contoh saja berfungsi sebagai basis, 
 # isinya digunakan untuk membangun metadata JSON yang dapat disalin dan diunggah oleh pengguna 
 # ke 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(): 
 kelas AdminInterface(sp.Contract): 
 @sp.private(with_storage="read-only") 
 def is_administrator_(mandiri, pengirim): 
 sp .cast(sp.pengirim, sp.address) 
 """Tidak standar, dapat didefinisikan ulang melalui pewarisan."""
            kembalikan Benar 

 kelas CommonInterface (AdminInterface): 
 def __init__(mandiri): 
 AdminInterface.__init__(diri sendiri)
            diri.data.saldo = sp.cast(
                sp.big_map(), 
 sp.big_map[ 
 sp.alamat, 
 sp.record(approvals=sp.map[sp.address, sp.nat], saldo=sp.nat),
                ], 
 ) 
 mandiri.data.total_supply = 0 
 diri.data.token_metadata = sp.pemeran(
                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.alamat, 
 sp.record(approvals=sp.map[sp.address, sp.nat], saldo=sp.nat),
                ], 
 ) 
 mandiri.data.total_supply = 0 
 diri.data.token_metadata = sp.pemeran(
                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): 
 """Tidak standar, dapat didefinisikan ulang melalui pewarisan."""
            return False 

 class Fa1_2(CommonInterface): 
 def __init__(mandiri, metadata, buku besar, token_metadata): 
 """ 
 spesifikasi token_metadata: https://gitlab.com/tzip/tzip/-/blob/master/ proposal/tzip-12/tzip-12.md#token-metadata
            Metadata khusus token disimpan/disajikan sebagai tipe nilai Michelson (byte string peta).
            Beberapa kunci telah dicadangkan dan ditentukan sebelumnya: 

 - "" : Harus sesuai dengan URI TZIP-016 yang menunjuk ke representasi JSON dari metadata token.
            - "nama" : Harus berupa string UTF-8 yang memberikan "nama tampilan" pada token.
            - "simbol" : Harus berupa string UTF-8 untuk pengidentifikasi singkat token (mis XTZ, EUR, â€Ķ). 
 - "desimal" : Harus berupa bilangan bulat (dikonversi ke string UTF-8 dalam desimal) 
 yang menentukan posisi titik desimal dalam saldo token untuk tujuan tampilan.

            spesifikasi contract_metadata: https://gitlab.com/tzip/tzip/-/blob/master/proposals/tzip-16/tzip-16.md 
 """ 
 CommonInterface.__init__(mandiri)
            self.data.metadata = metadata 
 self.data.token_metadata = sp.peta_besar(
                {0: sp.record(token_id=0, token_info=token_metadata)}
            ) 

 untuk pemilik di ledger.items():
                self.data.balances[pemilik.kunci] = pemilik.nilai 
 diri.data.total_supply += owner.value.balance 

 # TODO: Aktifkan saat fitur ini diterapkan.
            # diri.init_metadata("metadata", metadata) 

 @sp.entrypoint 
 def transfer(self, param): 
 sp.cast( 
 param, 
 sp.record(from_=sp.address, ke_=sp.alamat, nilai=sp.nat).tata letak(
                    ("dari_ sebagai dari", ("ke_ sebagai", "nilai")) 
 ), 
 ) 
 balance_from = self.data.balances.get(
                param.dari_, default=sp.record(saldo=0, persetujuan={}) 
 ) 
 balance_to = self.data.balances.get(
                param.to_, default=sp.record(saldo=0, persetujuan={}) 
 ) 
 balance_from.balance = sp.as_nat(
                balance_from.balance - param.nilai, error="FA1.2_InsufficientBalance" 
 ) 
 balance_to.balance += param.value 
 jika bukan self.is_administrator_(sp.sender):
                tegaskan bukan diri.is_paused_(), "FA1.2_Dijeda"
                jika param.dari_ != sp.pengirim: 
 saldo_dari.persetujuan[sp.pengirim] = sp.as_nat(
                        balance_from.approvals[sp.sender] - param.nilai,
                        error="FA1.2_NotAllowed", 
 ) 
 self.data.balances[param.from_] = saldo_dari 
 data.diri.saldo[param.to_] = balance_to 

 @sp.entrypoint 
 def menyetujui(self, param): 
 sp.cast( 
 param, 
 sp.record(spender=sp.address, nilai=sp.nat).tata letak(
                    ("pembelanja", "nilai") 
 ), 
 ) 
 menegaskan bukan diri.is_paused_(), "FA1.2_Dijeda"
            pembelanja_saldo = diri.data.saldo.mendapatkan(
                sp.pengirim, default=sp.record(saldo=0, persetujuan={}) 
 ) 
 sudahDisetujui = pembelanja_keseimbangan.persetujuan.get(param.spender, default=0) 
 menegaskan ( 
 sudah Disetujui == 0 atau param.value == 0 
 ), "FA1.2_UnsafeAllowanceChange"
            pembelanja_saldo.persetujuan[param.pembelanja] = param.value 
 self.data.balances[sp.sender] = pembelanja_saldo 

 @sp.entrypoint 
 def getBalance(self, param): 
 (alamat, panggilan balik) = param 
 hasil = self.data.balances.get(
                alamat, default=sp.record(saldo=0, persetujuan={}) 
 ).balance 
 sp.transfer(hasil, sp.tez(0), callback) 

 @sp.entrypoint 
 def getAllowance(self, param): 
 (args, callback) = param 
 hasil = diri.data.saldo.dapatkan(
                args.pemilik, default=sp.record(saldo=0, persetujuan={}) 
 ).approvals.get(args.spender, default=0) 
 sp.transfer(hasil, sp.tez(0), callback) 

 @sp.entrypoint 
 def getTotalSupply(self, param): 
 sp.cast(param, sp.pair[sp.unit, sp.kontrak[sp.nat]])
            sp.transfer(mandiri.data.total_supply, sp.tez(0), sp.snd(param)) 

 @sp.offchain_view() 
 def token_metadata(self, token_id): 
 """Kembalikan URI token-metadata untuk token yang diberikan. (token_id harus 0)."""
            sp.cast(token_id, sp.nat) 
 mengembalikan self.data.token_metadata[token_id]

    ########## 
 # Mixin # 
 ##########

    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

    ########## 
 # Tes # 
 ########## 

 kelas Fa1_2TestFull(Admin, Jeda, Fa1_2, Mint, Bakar, ChangeMetadata): 
 def __init__(mandiri, administrator, metadata, buku besar, token_metadata): 
 ChangeMetadata.__init__(diri sendiri)
            Membakar.__init__(diri sendiri)
            Daun mint.__init__(diri sendiri)
            Fa1_2.__init__(diri sendiri, metadata, buku besar, token_metadata) 
 Jeda.__init__(diri sendiri)
            Admin.__init__(diri sendiri, administrator) 

 kelas Viewer_nat(sp.Contract): 
 def __init__(mandiri): 
 self.data.last = sp.cast(Tidak ada, sp.pilihan[sp.nat])

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

 class Viewer_address(sp.Contract): 
 def __init__(self): 
 self.data.last = sp.cast(Tidak ada, sp.pilihan[sp.alamat])

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


 jika "template" tidak ada di __name__: 

 @sp.add_test(name="FA12") 
 def tes(): 
 sc = sp.test_scenario(m)
        sc.h1("Template FA1.2 - Aset yang dapat dipertukarkan") 

 # sp.test_account menghasilkan pasangan kunci ED25519 secara deterministik: 
 admin = sp.test_account("Administrator")
        alice = sp.test_account("Alice")
        bob = sp.test_account("Robert")

        # Mari kita tampilkan akun-akunnya: 
 sc.h1("Akun") 
 sc.show([admin, alice, bob]) 

 sc.h1("Kontrak") 
 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. metadata_of_url(
            "ipfs://QmaiAUj1FFNGYTu8rLBjc3eeN9cSKwaF8EGMBNDmhzPNFd" 
 ) 

 c1 = m.Fa1_2TestFull( 
 administrator=admin.address, 
 metadata=contract_metadata, 
 token_metadata=token_metadata, 
 ledger={}, 
 ) 
 sc += c1 

 sc .h1("Tampilan 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("Mencoba memperbarui metadata") 
 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("Entrypoints") 
 sc.h2("Admin mencetak beberapa koin") 
 c1.mint(address=alice.address, value=12).run (pengirim=admin)
        c1.mint(alamat=alice.alamat, nilai=3).run(pengirim=admin)
        c1.mint(alamat=alice.alamat, nilai=3).run(pengirim=admin)
        sc.h2("Alice mentransfer ke Bob") 
 c1.transfer(from_=alice.alamat, ke_=bob.alamat, nilai=4).menjalankan(pengirim=alice)
        sc.verify(c1.data.balances[alice.address].balance == 14) 
 sc.h2("Bob mencoba mentransfer dari Alice tetapi dia tidak mendapat persetujuannya") 
 c1.transfer(from_=alice.address, ke_=bob.alamat, nilai=4).jalankan(
            sender=bob, valid=False 
 ) 
 sc.h2("Alice menyetujui transfer Bob dan Bob") 
 c1.approve(spender=bob.address, value=5).run(sender=alice)
        c1.transfer(dari_=alamat.alice, ke_=bob.alamat, nilai=4).jalankan(pengirim=bob)
        sc.h2("Bob mencoba melakukan transfer berlebih dari Alice") 
 c1.transfer(from_=alice.address, ke_=bob.alamat, nilai=4).jalankan(
            sender=bob, valid=False 
 ) 
 sc.h2("Admin membakar token Bob") 
 c1.burn(address=bob.address, nilai=1).jalankan(pengirim=admin)
        sc.verify(c1.data.balances[alice.address].balance == 10) 
 sc.h2("Alice mencoba membakar token Bob") 
 c1.burn(address=bob.address, nilai=1).menjalankan(pengirim=alice, valid=False) 
 sc.h2("Admin menjeda kontrak dan Alice tidak dapat mentransfer lagi") 
 c1.setPause(True).run(sender=admin)
        c1.transfer(dari_=alamat.alice, ke_=bob.alamat, nilai=4).jalankan(
            pengirim=alice, valid=False 
 ) 
 sc.verify(c1.data.balances[alice.address].balance == 10) 
 sc.h2("Admin mentransfer saat jeda") 
 c1.transfer(from_=alice.address, ke_=bob.alamat, nilai=1).jalankan(pengirim=admin)
        sc.h2("Admin membatalkan jeda kontrak dan transfer diperbolehkan") 
 c1.setPause(False).run(sender=admin)
        sc.verify(c1.data.balances[alice.address].balance == 9) 
 c1.transfer(dari_=alamat.alice, ke_=bob.alamat, nilai=1).jalankan(pengirim=alice)

        sc.verify(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("Tampilan") 
 sc.h2("Saldo") 
 view_balance = m.Viewer_nat() 
 sc += view_balance 
 target = sp.contract(sp.TNat, view_balance.address, "target").open_some() 
 c1.getBalance((alice.alamat, target)) 
 sc.verify_equal(view_balance.data.last, sp.some(8)) 

 sc.h2("Administrator") 
 view_administrator = m.Viewer_address() 
 sc += view_administrator 
 target = sp.kontrak(
            sp.TAddress, view_administrator.address, "target" 
 ).open_some() 
 c1.getAdministrator((sp.unit, target)) 
 sc.verify_equal(view_administrator.data.last, sp.some(admin.alamat))

        sc.h2("Total Persediaan") 
 view_totalSupply = m.Viewer_nat() 
 sc += view_totalSupply 
 target = sp.contract(sp.TNat, view_totalSupply.address, "target").open_some() 
 c1.getTotalSupply((sp.unit, target)) 
 sc.verify_equal(view_totalSupply.data.last, sp.some(17)) 

 sc.h2("Allowance") 
 view_allowance = m.Viewer_nat() 
 sc += view_allowance 
 target = sp.contract(sp.TNat, view_allowance.address, "target").open_some() 
 c1.getAllowance((sp.record(pemilik=alice.alamat, pembelanja=bob.alamat), target)) 
 sc.verify_equal(view_allowance.data.last, sp.beberapa(1))
  1. Jalankan Kontrak. Anda akan melihat sesuatu seperti ini

Kontrak FA1.2 asli memiliki fungsi dasar seperti mentransfer token, menyetujui transfer, memeriksa saldo, dan melihat total pasokan token. Sekarang kami akan meningkatkan fungsi ini.

  • Admin: Kami akan memperkenalkan kontrak yang memungkinkan administrator melakukan tindakan tertentu, seperti menjeda kontrak, dan mencegah akun lain menggunakan fungsi ini.
  • Jeda: Fitur ini memungkinkan kami untuk menjeda dan membatalkan jeda kontrak. Ketika kontrak dijeda, tidak ada yang bisa menggunakannya kecuali administrator.
  • Mint: Fitur kontrak ini memungkinkan administrator membuat token baru.
  • Bakar: Fitur kontrak ini memungkinkan administrator untuk menghancurkan token.
  • ChangeMetadata: Fitur ini memungkinkan administrator memperbarui metadata kontrak.
    Masing-masing fungsi ini didefinisikan dalam kelas terpisah.

Selamat! Anda telah membuat token sepadan pertama Anda di Tezos menggunakan standar FA1.2!

Pada pelajaran berikutnya, kita akan mempelajari cara berinteraksi dengan kontrak token yang baru saja kita buat. Ini termasuk mentransfer token, menyetujui transfer token, dan memeriksa saldo token dan total pasokan. Pantau terus!

āļ‚āđ‰āļ­āļˆāļģāļāļąāļ”āļ„āļ§āļēāļĄāļĢāļąāļšāļœāļīāļ”
* āļāļēāļĢāļĨāļ‡āļ—āļļāļ™āļ„āļĢāļīāļ›āđ‚āļ•āļĄāļĩāļ„āļ§āļēāļĄāđ€āļŠāļĩāđˆāļĒāļ‡āļŠāļđāļ‡ āđ‚āļ›āļĢāļ”āļ”āļģāđ€āļ™āļīāļ™āļāļēāļĢāļ”āđ‰āļ§āļĒāļ„āļ§āļēāļĄāļĢāļ°āļĄāļąāļ”āļĢāļ°āļ§āļąāļ‡ āļŦāļĨāļąāļāļŠāļđāļ•āļĢāļ™āļĩāđ‰āđ„āļĄāđˆāđ„āļ”āđ‰āļĄāļĩāđ„āļ§āđ‰āđ€āļžāļ·āđˆāļ­āđ€āļ›āđ‡āļ™āļ„āļģāđāļ™āļ°āļ™āļģāđƒāļ™āļāļēāļĢāļĨāļ‡āļ—āļļāļ™
* āļŦāļĨāļąāļāļŠāļđāļ•āļĢāļ™āļĩāđ‰āļŠāļĢāđ‰āļēāļ‡āļ‚āļķāđ‰āļ™āđ‚āļ”āļĒāļœāļđāđ‰āđ€āļ‚āļĩāļĒāļ™āļ—āļĩāđˆāđ„āļ”āđ‰āđ€āļ‚āđ‰āļēāļĢāđˆāļ§āļĄ Gate Learn āļ„āļ§āļēāļĄāļ„āļīāļ”āđ€āļŦāđ‡āļ™āļ‚āļ­āļ‡āļœāļđāđ‰āđ€āļ‚āļĩāļĒāļ™āđ„āļĄāđˆāđ„āļ”āđ‰āļĄāļēāļˆāļēāļ Gate Learn
āđāļ„āļ•āļ•āļēāļĨāđ‡āļ­āļ
āļšāļ—āđ€āļĢāļĩāļĒāļ™āļ—āļĩāđˆ 2

Membuat Token Pertama Anda dengan Standar FA1.2

Dalam pelajaran ini, kita akan membahas proses pembuatan token yang sepadan menggunakan standar FA1.2 di Tezos. Kami akan menggunakan IDE online SmartPy untuk menulis dan menerapkan kontrak pintar kami. Perlu diingat bahwa standar FA1.2 terutama digunakan untuk token yang sepadan, yang berarti token yang memiliki properti identik dan dapat ditukar dengan basis satu-untuk-satu.

Panduan Langkah demi Langkah

  1. Mengakses IDE SmartPy

  2. Pertama, buka IDE online SmartPy di https://smartpy.io/ide/. Ini adalah platform yang akan kami gunakan untuk menulis, menguji, dan menerapkan kontrak pintar kami.

  3. Memulai Templat FA1.2

  4. Klik “Templates by Type” di sidebar kiri dan pilih “FA1.2”. Tab baru akan terbuka dengan templat kontrak FA1.2. Ini adalah kontrak siap pakai yang mengikuti standar FA1.2.

  5. Memahami Templat FA1.2

  6. Templat ini memiliki fungsi dasar untuk token yang sepadan, yang mencakup transfer token, persetujuan transfer, pengecekan saldo, dan melihat total pasokan token. Kontrak tersebut juga mencakup fungsionalitas tambahan untuk mencetak dan membakar token, serta untuk manajemen tata kelola.

  7. Pelajari template ini dan pastikan Anda memahami fungsinya. Tidak apa-apa jika Anda tidak memahami semuanya saat ini, namun cobalah memahami secara umum operasi yang dapat dilakukan kontrak ini.
    Misalnya, Anda dapat menyalin kode dari template di SmartPy IDE atau di bawah ini:

Python 
 # Aset Fungible - FA12 
 # Terinspirasi oleh https://gitlab.com/tzip/tzip/blob/master/A/FA1.2.md 

 import smartpy as sp 


 # Metadata di bawah hanyalah sebuah contoh saja berfungsi sebagai basis, 
 # isinya digunakan untuk membangun metadata JSON yang dapat disalin dan diunggah oleh pengguna 
 # ke 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(): 
 kelas AdminInterface(sp.Contract): 
 @sp.private(with_storage="read-only") 
 def is_administrator_(mandiri, pengirim): 
 sp .cast(sp.pengirim, sp.address) 
 """Tidak standar, dapat didefinisikan ulang melalui pewarisan."""
            kembalikan Benar 

 kelas CommonInterface (AdminInterface): 
 def __init__(mandiri): 
 AdminInterface.__init__(diri sendiri)
            diri.data.saldo = sp.cast(
                sp.big_map(), 
 sp.big_map[ 
 sp.alamat, 
 sp.record(approvals=sp.map[sp.address, sp.nat], saldo=sp.nat),
                ], 
 ) 
 mandiri.data.total_supply = 0 
 diri.data.token_metadata = sp.pemeran(
                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.alamat, 
 sp.record(approvals=sp.map[sp.address, sp.nat], saldo=sp.nat),
                ], 
 ) 
 mandiri.data.total_supply = 0 
 diri.data.token_metadata = sp.pemeran(
                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): 
 """Tidak standar, dapat didefinisikan ulang melalui pewarisan."""
            return False 

 class Fa1_2(CommonInterface): 
 def __init__(mandiri, metadata, buku besar, token_metadata): 
 """ 
 spesifikasi token_metadata: https://gitlab.com/tzip/tzip/-/blob/master/ proposal/tzip-12/tzip-12.md#token-metadata
            Metadata khusus token disimpan/disajikan sebagai tipe nilai Michelson (byte string peta).
            Beberapa kunci telah dicadangkan dan ditentukan sebelumnya: 

 - "" : Harus sesuai dengan URI TZIP-016 yang menunjuk ke representasi JSON dari metadata token.
            - "nama" : Harus berupa string UTF-8 yang memberikan "nama tampilan" pada token.
            - "simbol" : Harus berupa string UTF-8 untuk pengidentifikasi singkat token (mis XTZ, EUR, â€Ķ). 
 - "desimal" : Harus berupa bilangan bulat (dikonversi ke string UTF-8 dalam desimal) 
 yang menentukan posisi titik desimal dalam saldo token untuk tujuan tampilan.

            spesifikasi contract_metadata: https://gitlab.com/tzip/tzip/-/blob/master/proposals/tzip-16/tzip-16.md 
 """ 
 CommonInterface.__init__(mandiri)
            self.data.metadata = metadata 
 self.data.token_metadata = sp.peta_besar(
                {0: sp.record(token_id=0, token_info=token_metadata)}
            ) 

 untuk pemilik di ledger.items():
                self.data.balances[pemilik.kunci] = pemilik.nilai 
 diri.data.total_supply += owner.value.balance 

 # TODO: Aktifkan saat fitur ini diterapkan.
            # diri.init_metadata("metadata", metadata) 

 @sp.entrypoint 
 def transfer(self, param): 
 sp.cast( 
 param, 
 sp.record(from_=sp.address, ke_=sp.alamat, nilai=sp.nat).tata letak(
                    ("dari_ sebagai dari", ("ke_ sebagai", "nilai")) 
 ), 
 ) 
 balance_from = self.data.balances.get(
                param.dari_, default=sp.record(saldo=0, persetujuan={}) 
 ) 
 balance_to = self.data.balances.get(
                param.to_, default=sp.record(saldo=0, persetujuan={}) 
 ) 
 balance_from.balance = sp.as_nat(
                balance_from.balance - param.nilai, error="FA1.2_InsufficientBalance" 
 ) 
 balance_to.balance += param.value 
 jika bukan self.is_administrator_(sp.sender):
                tegaskan bukan diri.is_paused_(), "FA1.2_Dijeda"
                jika param.dari_ != sp.pengirim: 
 saldo_dari.persetujuan[sp.pengirim] = sp.as_nat(
                        balance_from.approvals[sp.sender] - param.nilai,
                        error="FA1.2_NotAllowed", 
 ) 
 self.data.balances[param.from_] = saldo_dari 
 data.diri.saldo[param.to_] = balance_to 

 @sp.entrypoint 
 def menyetujui(self, param): 
 sp.cast( 
 param, 
 sp.record(spender=sp.address, nilai=sp.nat).tata letak(
                    ("pembelanja", "nilai") 
 ), 
 ) 
 menegaskan bukan diri.is_paused_(), "FA1.2_Dijeda"
            pembelanja_saldo = diri.data.saldo.mendapatkan(
                sp.pengirim, default=sp.record(saldo=0, persetujuan={}) 
 ) 
 sudahDisetujui = pembelanja_keseimbangan.persetujuan.get(param.spender, default=0) 
 menegaskan ( 
 sudah Disetujui == 0 atau param.value == 0 
 ), "FA1.2_UnsafeAllowanceChange"
            pembelanja_saldo.persetujuan[param.pembelanja] = param.value 
 self.data.balances[sp.sender] = pembelanja_saldo 

 @sp.entrypoint 
 def getBalance(self, param): 
 (alamat, panggilan balik) = param 
 hasil = self.data.balances.get(
                alamat, default=sp.record(saldo=0, persetujuan={}) 
 ).balance 
 sp.transfer(hasil, sp.tez(0), callback) 

 @sp.entrypoint 
 def getAllowance(self, param): 
 (args, callback) = param 
 hasil = diri.data.saldo.dapatkan(
                args.pemilik, default=sp.record(saldo=0, persetujuan={}) 
 ).approvals.get(args.spender, default=0) 
 sp.transfer(hasil, sp.tez(0), callback) 

 @sp.entrypoint 
 def getTotalSupply(self, param): 
 sp.cast(param, sp.pair[sp.unit, sp.kontrak[sp.nat]])
            sp.transfer(mandiri.data.total_supply, sp.tez(0), sp.snd(param)) 

 @sp.offchain_view() 
 def token_metadata(self, token_id): 
 """Kembalikan URI token-metadata untuk token yang diberikan. (token_id harus 0)."""
            sp.cast(token_id, sp.nat) 
 mengembalikan self.data.token_metadata[token_id]

    ########## 
 # Mixin # 
 ##########

    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

    ########## 
 # Tes # 
 ########## 

 kelas Fa1_2TestFull(Admin, Jeda, Fa1_2, Mint, Bakar, ChangeMetadata): 
 def __init__(mandiri, administrator, metadata, buku besar, token_metadata): 
 ChangeMetadata.__init__(diri sendiri)
            Membakar.__init__(diri sendiri)
            Daun mint.__init__(diri sendiri)
            Fa1_2.__init__(diri sendiri, metadata, buku besar, token_metadata) 
 Jeda.__init__(diri sendiri)
            Admin.__init__(diri sendiri, administrator) 

 kelas Viewer_nat(sp.Contract): 
 def __init__(mandiri): 
 self.data.last = sp.cast(Tidak ada, sp.pilihan[sp.nat])

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

 class Viewer_address(sp.Contract): 
 def __init__(self): 
 self.data.last = sp.cast(Tidak ada, sp.pilihan[sp.alamat])

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


 jika "template" tidak ada di __name__: 

 @sp.add_test(name="FA12") 
 def tes(): 
 sc = sp.test_scenario(m)
        sc.h1("Template FA1.2 - Aset yang dapat dipertukarkan") 

 # sp.test_account menghasilkan pasangan kunci ED25519 secara deterministik: 
 admin = sp.test_account("Administrator")
        alice = sp.test_account("Alice")
        bob = sp.test_account("Robert")

        # Mari kita tampilkan akun-akunnya: 
 sc.h1("Akun") 
 sc.show([admin, alice, bob]) 

 sc.h1("Kontrak") 
 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. metadata_of_url(
            "ipfs://QmaiAUj1FFNGYTu8rLBjc3eeN9cSKwaF8EGMBNDmhzPNFd" 
 ) 

 c1 = m.Fa1_2TestFull( 
 administrator=admin.address, 
 metadata=contract_metadata, 
 token_metadata=token_metadata, 
 ledger={}, 
 ) 
 sc += c1 

 sc .h1("Tampilan 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("Mencoba memperbarui metadata") 
 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("Entrypoints") 
 sc.h2("Admin mencetak beberapa koin") 
 c1.mint(address=alice.address, value=12).run (pengirim=admin)
        c1.mint(alamat=alice.alamat, nilai=3).run(pengirim=admin)
        c1.mint(alamat=alice.alamat, nilai=3).run(pengirim=admin)
        sc.h2("Alice mentransfer ke Bob") 
 c1.transfer(from_=alice.alamat, ke_=bob.alamat, nilai=4).menjalankan(pengirim=alice)
        sc.verify(c1.data.balances[alice.address].balance == 14) 
 sc.h2("Bob mencoba mentransfer dari Alice tetapi dia tidak mendapat persetujuannya") 
 c1.transfer(from_=alice.address, ke_=bob.alamat, nilai=4).jalankan(
            sender=bob, valid=False 
 ) 
 sc.h2("Alice menyetujui transfer Bob dan Bob") 
 c1.approve(spender=bob.address, value=5).run(sender=alice)
        c1.transfer(dari_=alamat.alice, ke_=bob.alamat, nilai=4).jalankan(pengirim=bob)
        sc.h2("Bob mencoba melakukan transfer berlebih dari Alice") 
 c1.transfer(from_=alice.address, ke_=bob.alamat, nilai=4).jalankan(
            sender=bob, valid=False 
 ) 
 sc.h2("Admin membakar token Bob") 
 c1.burn(address=bob.address, nilai=1).jalankan(pengirim=admin)
        sc.verify(c1.data.balances[alice.address].balance == 10) 
 sc.h2("Alice mencoba membakar token Bob") 
 c1.burn(address=bob.address, nilai=1).menjalankan(pengirim=alice, valid=False) 
 sc.h2("Admin menjeda kontrak dan Alice tidak dapat mentransfer lagi") 
 c1.setPause(True).run(sender=admin)
        c1.transfer(dari_=alamat.alice, ke_=bob.alamat, nilai=4).jalankan(
            pengirim=alice, valid=False 
 ) 
 sc.verify(c1.data.balances[alice.address].balance == 10) 
 sc.h2("Admin mentransfer saat jeda") 
 c1.transfer(from_=alice.address, ke_=bob.alamat, nilai=1).jalankan(pengirim=admin)
        sc.h2("Admin membatalkan jeda kontrak dan transfer diperbolehkan") 
 c1.setPause(False).run(sender=admin)
        sc.verify(c1.data.balances[alice.address].balance == 9) 
 c1.transfer(dari_=alamat.alice, ke_=bob.alamat, nilai=1).jalankan(pengirim=alice)

        sc.verify(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("Tampilan") 
 sc.h2("Saldo") 
 view_balance = m.Viewer_nat() 
 sc += view_balance 
 target = sp.contract(sp.TNat, view_balance.address, "target").open_some() 
 c1.getBalance((alice.alamat, target)) 
 sc.verify_equal(view_balance.data.last, sp.some(8)) 

 sc.h2("Administrator") 
 view_administrator = m.Viewer_address() 
 sc += view_administrator 
 target = sp.kontrak(
            sp.TAddress, view_administrator.address, "target" 
 ).open_some() 
 c1.getAdministrator((sp.unit, target)) 
 sc.verify_equal(view_administrator.data.last, sp.some(admin.alamat))

        sc.h2("Total Persediaan") 
 view_totalSupply = m.Viewer_nat() 
 sc += view_totalSupply 
 target = sp.contract(sp.TNat, view_totalSupply.address, "target").open_some() 
 c1.getTotalSupply((sp.unit, target)) 
 sc.verify_equal(view_totalSupply.data.last, sp.some(17)) 

 sc.h2("Allowance") 
 view_allowance = m.Viewer_nat() 
 sc += view_allowance 
 target = sp.contract(sp.TNat, view_allowance.address, "target").open_some() 
 c1.getAllowance((sp.record(pemilik=alice.alamat, pembelanja=bob.alamat), target)) 
 sc.verify_equal(view_allowance.data.last, sp.beberapa(1))
  1. Jalankan Kontrak. Anda akan melihat sesuatu seperti ini

Kontrak FA1.2 asli memiliki fungsi dasar seperti mentransfer token, menyetujui transfer, memeriksa saldo, dan melihat total pasokan token. Sekarang kami akan meningkatkan fungsi ini.

  • Admin: Kami akan memperkenalkan kontrak yang memungkinkan administrator melakukan tindakan tertentu, seperti menjeda kontrak, dan mencegah akun lain menggunakan fungsi ini.
  • Jeda: Fitur ini memungkinkan kami untuk menjeda dan membatalkan jeda kontrak. Ketika kontrak dijeda, tidak ada yang bisa menggunakannya kecuali administrator.
  • Mint: Fitur kontrak ini memungkinkan administrator membuat token baru.
  • Bakar: Fitur kontrak ini memungkinkan administrator untuk menghancurkan token.
  • ChangeMetadata: Fitur ini memungkinkan administrator memperbarui metadata kontrak.
    Masing-masing fungsi ini didefinisikan dalam kelas terpisah.

Selamat! Anda telah membuat token sepadan pertama Anda di Tezos menggunakan standar FA1.2!

Pada pelajaran berikutnya, kita akan mempelajari cara berinteraksi dengan kontrak token yang baru saja kita buat. Ini termasuk mentransfer token, menyetujui transfer token, dan memeriksa saldo token dan total pasokan. Pantau terus!

āļ‚āđ‰āļ­āļˆāļģāļāļąāļ”āļ„āļ§āļēāļĄāļĢāļąāļšāļœāļīāļ”
* āļāļēāļĢāļĨāļ‡āļ—āļļāļ™āļ„āļĢāļīāļ›āđ‚āļ•āļĄāļĩāļ„āļ§āļēāļĄāđ€āļŠāļĩāđˆāļĒāļ‡āļŠāļđāļ‡ āđ‚āļ›āļĢāļ”āļ”āļģāđ€āļ™āļīāļ™āļāļēāļĢāļ”āđ‰āļ§āļĒāļ„āļ§āļēāļĄāļĢāļ°āļĄāļąāļ”āļĢāļ°āļ§āļąāļ‡ āļŦāļĨāļąāļāļŠāļđāļ•āļĢāļ™āļĩāđ‰āđ„āļĄāđˆāđ„āļ”āđ‰āļĄāļĩāđ„āļ§āđ‰āđ€āļžāļ·āđˆāļ­āđ€āļ›āđ‡āļ™āļ„āļģāđāļ™āļ°āļ™āļģāđƒāļ™āļāļēāļĢāļĨāļ‡āļ—āļļāļ™
* āļŦāļĨāļąāļāļŠāļđāļ•āļĢāļ™āļĩāđ‰āļŠāļĢāđ‰āļēāļ‡āļ‚āļķāđ‰āļ™āđ‚āļ”āļĒāļœāļđāđ‰āđ€āļ‚āļĩāļĒāļ™āļ—āļĩāđˆāđ„āļ”āđ‰āđ€āļ‚āđ‰āļēāļĢāđˆāļ§āļĄ Gate Learn āļ„āļ§āļēāļĄāļ„āļīāļ”āđ€āļŦāđ‡āļ™āļ‚āļ­āļ‡āļœāļđāđ‰āđ€āļ‚āļĩāļĒāļ™āđ„āļĄāđˆāđ„āļ”āđ‰āļĄāļēāļˆāļēāļ Gate Learn