Mengakses IDE SmartPy
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.
Memulai Templat FA1.2
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.
Memahami Templat FA1.2
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.
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))
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.
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!
Mengakses IDE SmartPy
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.
Memulai Templat FA1.2
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.
Memahami Templat FA1.2
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.
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))
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.
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!