SmartPy IDE へのアクセス
まず、SmartPy オンライン IDE を開きます https://smartpy.io/ide/
。 これは、スマートコントラクトの作成、テスト、デプロイに使用するプラットフォームです。
FA1.2 テンプレートの開始
左サイドバーの「Templates by Type」をクリックし、「FA1.2」を選択します。 新しいタブが開き、FA1.2 契約テンプレートが表示されます。 これは、FA1.2 標準に準拠したすぐに使用できる契約です。
FA1.2 テンプレートについて
このテンプレートには、トークンの転送、転送の承認、残高の確認、トークンの総供給量の表示など、代替可能なトークンの基本機能があります。 このコントラクトには、トークンの鋳造と燃焼、およびガバナンス管理のための追加機能も含まれています。
このテンプレートを研究し、その機能を理解していることを確認してください。 この時点ですべてを理解していなくても問題ありませんが、このコントラクトで実行できる操作の一般的な感覚をつかむようにしてください。
たとえば、SmartPy IDEまたは以下のテンプレートからコードをコピーできます。
ニシキヘビ
# 代替可能な資産 - FA12
# https://gitlab.com/tzip/tzip/blob/master/A/FA1.2.md に触発されて
SmartPy を SP としてインポートする
#以下のメタデータは単なる例であり、ベースとして機能します。
#コンテンツは、ユーザーが
#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.モジュール
デフm():
class AdminInterface(sp.契約):
@sp.private(with_storage="読み取り専用")
def is_administrator_(self, sender):
sp.cast(sp.sender, sp.addressなど)
"""標準ではない。継承によって再定義される可能性がある。""" True を返す
class CommonInterface(AdminInterface):
デフ __init__(セルフ):
AdminInterfaceです。__init__(自己) self.data.balances = sp.cast( sp.big_map()、
sp.big_map[
sp.address、
sp.record(approvals=sp.map[sp.address, sp.nat]、balance=sp.nat)、 ],
)
self.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(approvals=sp.map[sp.address, sp.nat]、balance=sp.nat)、 ],
)
self.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="読み取り専用")
デフis_paused_(自己):
"""標準ではない。継承によって再定義される可能性がある。""" Falseを返す
クラスFa1_2(CommonInterface):
デフ __init__(自己、メタデータ、台帳、token_metadata):
"""
token_metadata仕様:https://gitlab.com/tzip/tzip/-/blob/master/proposals/tzip-12/tzip-12.md#token-metadata トークン固有のメタデータは、型 (マップ文字列バイト) の Michelson 値として格納/提示されます。 いくつかのキーは予約済みで、事前定義されています。
- "" : トークンメタデータの JSON 表現を指す TZIP-016 URI に対応する必要があります。 - "name" :トークンに「表示名」を与えるUTF-8文字列である必要があります。 - "symbol" :トークンの短い識別子のUTF-8文字列である必要があります(例: XTZ、EUR、...)。
- "decimals" : 整数 (10 進数の UTF-8 文字列に変換) である必要があります。
これは、表示目的でトークン残高の小数点の位置を定義します。 contract_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()のownerの場合: self.data.balances[所有者.キー] = owner.value (所有者.値)
self.data.total_supply += owner.value.balance (所有者.値.残高)
# TODO:この機能が実装されたときにアクティブ化します。 # self.init_metadata("メタデータ", メタデータ)
@sp.エントリポイント
def transfer(self, param):
sp.cast(
paramや
sp.record(from_=sp.address, to_=sp.address、 value=sp.nat).layout( ("from_ as from", ("to_ as to", "value"))
),
)
balance_from = self.data.balances.get( param.from_、 デフォルト=sp.record(balance=0, approvals={})
)
balance_to = self.data.balances.get( param.to_、 デフォルト=sp.record(balance=0, approvals={})
)
balance_from.残高 = sp.as_nat( balance_from.balance - param.value、 エラー="FA1.2_InsufficientBalance"
)
balance_to.balance += param.value
self.is_administrator_(sp.sender)でない場合: assert not self.is_paused_()、 「FA1.2_Paused」 param.from_場合 != sp.sender:
balance_from.承認[sp.sender] = sp.as_nat( balance_from.approvals[sp.sender] - param.value、 エラー="FA1.2_NotAllowed"、
)
self.data.balances[param.from_] = balance_from
self.data.balances[param.to_] = balance_to
@sp.エントリポイント
def approve(self, param):
sp.cast(
paramや
sp.record(spender=sp.address, value=sp.nat).layout( (「浪費者」、「価値」)
),
)
assert not self.is_paused_()、 「FA1.2_Paused」 spender_balance = self.data.balances.get( sp.sender、デフォルト= sp.record(balance=0、 approvals={})
)
alreadyApproved = spender_balance.approvals.get(param.spender, デフォルト = 0)
アサート (
alreadyApproved == 0 または param.value == 0
)、「FA1.2_UnsafeAllowanceChange」 spender_balance.承認[param.spender] = param.value
self.data.balances[sp.sender] = spender_balance
@sp.エントリポイント
デフgetBalance(自己、パラメータ):
(アドレス、コールバック) = param
結果= self.data.balances.get( アドレス、デフォルト= sp.record(balance=0、 approvals={})
).バランス
sp.transfer(結果, sp.tez(0), コールバック)
@sp.エントリポイント
デフgetAllowance(自己、パラメータ):
(引数、コールバック)= param
結果= self.data.balances.get( args.ownerや デフォルト=sp.record(balance=0, approvals={})
).approvals.get(args.spender, デフォルト = 0)
sp.transfer(結果, sp.tez(0), コールバック)
@sp.エントリポイント
デフ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(Admin、Pause、Fa1_2、Mint、Burn、ChangeMetadata):
デフ __init__(自己、管理者、メタデータ、台帳、token_metadata):
ChangeMetadataです。__init__(自己) 燃える。__init__(自己) ミント。__init__(自己) Fa1_2。__init__(self, メタデータ、台帳、token_metadata)
休止。__init__(自己) 管理者__init__(self、 管理者)
クラスViewer_nat(sp.契約):
デフ __init__(セルフ):
self.data.last = sp.cast(なし、 sp.option[sp.nat]) です。 @sp.エントリポイント
def target(self, params):
self.data.last = sp です。一部(パラメータ)
クラスViewer_address(sp.契約):
デフ __init__(セルフ):
self.data.last = sp.cast(なし、 sp.option[sp.address]) @sp.エントリポイント
def target(self, params):
self.data.last = sp です。一部(パラメータ)
「テンプレート」が __name__にない場合:
@sp.add_test(名前= "FA12")
def test()を使用します。
sc = sp.test_scenario(m) sc.h1("FA1.2 テンプレート - 代替可能な資産")
# sp.test_account はED25519キーペアを確定的に生成します。
admin = sp.test_account("管理者") alice = sp.test_account("アリス") bob = sp.test_account("ロバート") # アカウントを表示しましょう。
sc.h1("アカウント")
sc.show([管理者、アリス、ボブ])
sc.h1("契約")
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("オフチェーンビュー - token_metadata")
sc.verify_equal(
SPの。ビュー(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(アドレス=アリス.アドレス、値=12).run(送信者=管理者) c1.mint(アドレス=アリス.アドレス、値=3).run(送信者=管理者) c1.mint(アドレス=アリス.アドレス、値=3).run(送信者=管理者) sc.h2("アリスはボブに転送します")
c1.transfer(from_=alice.address, to_=bob.address、 値=4).run(送信者=アリス) sc.verify(c1.data.balances[alice.address].balance == 14)
sc.h2("ボブはアリスから転勤しようとするが、アリスの承認を得ていない")
c1.transfer(from_=alice.address, to_=bob.address、 value=4).run( 送信者=ボブ、有効=False
)
sc.h2("Alice が Bob と Bob の転送を承認")
c1.approve(spender=bob.address, value=5).run(sender=alice) c1.transfer(from_=alice.address, to_=bob.address、 値=4).run(送信者=ボブ) sc.h2("ボブはアリスから過剰転送を試みます")
c1.transfer(from_=alice.address, to_=bob.address、 value=4).run( 送信者=ボブ、有効=False
)
sc.h2("管理者がボブトークンを燃やす")
c1.burn(アドレス= bob.address、 値=1).run(送信者=管理者) sc.verify(c1.data.balances[alice.address].balance == 10)
sc.h2("アリスはボブトークンを燃やそうとします")
c1.burn(アドレス= bob.address、 value=1).run(sender=alice, valid=False) です。
sc.h2("管理者が契約を一時停止し、アリスはもう転送できません")
c1.setPause(True).run(sender=admin) c1.transfer(from_=alice.address, to_=bob.address、 value=4).run( sender=アリス、有効=False
)
sc.verify(c1.data.balances[alice.address].balance == 10)
sc.h2("一時停止中に管理者が転送します")
c1.transfer(from_=alice.address, to_=bob.address、 値=1).run(送信者=管理者) sc.h2("管理者が契約の一時停止を解除し、転送が許可されます")
c1.setPause(False).run(sender = admin) sc.verify(c1.data.balances[alice.address].balance == 9)
c1.transfer(from_=alice.address, to_=bob.address、 値=1).run(送信者=アリス) 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("ビュー")
sc.h2("残高")
view_balance = m.Viewer_nat()
sc += view_balance
target = 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
target = 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
target = 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
target = sp.contract(sp.TNat、view_allowance.address、 "ターゲット").open_some()
c1.getAllowance((sp.record(owner=alice.address, spender=bob.address), ターゲット))
sc.verify_equal(view_allowance.data.last、 sp.some(1))
元のFA1.2コントラクトには、トークンの転送、転送の承認、残高の確認、トークンの総供給量の表示などの基本的な機能があります。 次に、この機能を強化します。
万丈! FA1.2規格を使用してTezosで最初のファンジブルトークンを作成しました。
次のレッスンでは、作成したトークンコントラクトを操作する方法を学習します。 これには、トークンの転送、トークンの転送の承認、トークンの残高と総供給量の確認が含まれます。 乞うご期待!
SmartPy IDE へのアクセス
まず、SmartPy オンライン IDE を開きます https://smartpy.io/ide/
。 これは、スマートコントラクトの作成、テスト、デプロイに使用するプラットフォームです。
FA1.2 テンプレートの開始
左サイドバーの「Templates by Type」をクリックし、「FA1.2」を選択します。 新しいタブが開き、FA1.2 契約テンプレートが表示されます。 これは、FA1.2 標準に準拠したすぐに使用できる契約です。
FA1.2 テンプレートについて
このテンプレートには、トークンの転送、転送の承認、残高の確認、トークンの総供給量の表示など、代替可能なトークンの基本機能があります。 このコントラクトには、トークンの鋳造と燃焼、およびガバナンス管理のための追加機能も含まれています。
このテンプレートを研究し、その機能を理解していることを確認してください。 この時点ですべてを理解していなくても問題ありませんが、このコントラクトで実行できる操作の一般的な感覚をつかむようにしてください。
たとえば、SmartPy IDEまたは以下のテンプレートからコードをコピーできます。
ニシキヘビ
# 代替可能な資産 - FA12
# https://gitlab.com/tzip/tzip/blob/master/A/FA1.2.md に触発されて
SmartPy を SP としてインポートする
#以下のメタデータは単なる例であり、ベースとして機能します。
#コンテンツは、ユーザーが
#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.モジュール
デフm():
class AdminInterface(sp.契約):
@sp.private(with_storage="読み取り専用")
def is_administrator_(self, sender):
sp.cast(sp.sender, sp.addressなど)
"""標準ではない。継承によって再定義される可能性がある。""" True を返す
class CommonInterface(AdminInterface):
デフ __init__(セルフ):
AdminInterfaceです。__init__(自己) self.data.balances = sp.cast( sp.big_map()、
sp.big_map[
sp.address、
sp.record(approvals=sp.map[sp.address, sp.nat]、balance=sp.nat)、 ],
)
self.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(approvals=sp.map[sp.address, sp.nat]、balance=sp.nat)、 ],
)
self.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="読み取り専用")
デフis_paused_(自己):
"""標準ではない。継承によって再定義される可能性がある。""" Falseを返す
クラスFa1_2(CommonInterface):
デフ __init__(自己、メタデータ、台帳、token_metadata):
"""
token_metadata仕様:https://gitlab.com/tzip/tzip/-/blob/master/proposals/tzip-12/tzip-12.md#token-metadata トークン固有のメタデータは、型 (マップ文字列バイト) の Michelson 値として格納/提示されます。 いくつかのキーは予約済みで、事前定義されています。
- "" : トークンメタデータの JSON 表現を指す TZIP-016 URI に対応する必要があります。 - "name" :トークンに「表示名」を与えるUTF-8文字列である必要があります。 - "symbol" :トークンの短い識別子のUTF-8文字列である必要があります(例: XTZ、EUR、...)。
- "decimals" : 整数 (10 進数の UTF-8 文字列に変換) である必要があります。
これは、表示目的でトークン残高の小数点の位置を定義します。 contract_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()のownerの場合: self.data.balances[所有者.キー] = owner.value (所有者.値)
self.data.total_supply += owner.value.balance (所有者.値.残高)
# TODO:この機能が実装されたときにアクティブ化します。 # self.init_metadata("メタデータ", メタデータ)
@sp.エントリポイント
def transfer(self, param):
sp.cast(
paramや
sp.record(from_=sp.address, to_=sp.address、 value=sp.nat).layout( ("from_ as from", ("to_ as to", "value"))
),
)
balance_from = self.data.balances.get( param.from_、 デフォルト=sp.record(balance=0, approvals={})
)
balance_to = self.data.balances.get( param.to_、 デフォルト=sp.record(balance=0, approvals={})
)
balance_from.残高 = sp.as_nat( balance_from.balance - param.value、 エラー="FA1.2_InsufficientBalance"
)
balance_to.balance += param.value
self.is_administrator_(sp.sender)でない場合: assert not self.is_paused_()、 「FA1.2_Paused」 param.from_場合 != sp.sender:
balance_from.承認[sp.sender] = sp.as_nat( balance_from.approvals[sp.sender] - param.value、 エラー="FA1.2_NotAllowed"、
)
self.data.balances[param.from_] = balance_from
self.data.balances[param.to_] = balance_to
@sp.エントリポイント
def approve(self, param):
sp.cast(
paramや
sp.record(spender=sp.address, value=sp.nat).layout( (「浪費者」、「価値」)
),
)
assert not self.is_paused_()、 「FA1.2_Paused」 spender_balance = self.data.balances.get( sp.sender、デフォルト= sp.record(balance=0、 approvals={})
)
alreadyApproved = spender_balance.approvals.get(param.spender, デフォルト = 0)
アサート (
alreadyApproved == 0 または param.value == 0
)、「FA1.2_UnsafeAllowanceChange」 spender_balance.承認[param.spender] = param.value
self.data.balances[sp.sender] = spender_balance
@sp.エントリポイント
デフgetBalance(自己、パラメータ):
(アドレス、コールバック) = param
結果= self.data.balances.get( アドレス、デフォルト= sp.record(balance=0、 approvals={})
).バランス
sp.transfer(結果, sp.tez(0), コールバック)
@sp.エントリポイント
デフgetAllowance(自己、パラメータ):
(引数、コールバック)= param
結果= self.data.balances.get( args.ownerや デフォルト=sp.record(balance=0, approvals={})
).approvals.get(args.spender, デフォルト = 0)
sp.transfer(結果, sp.tez(0), コールバック)
@sp.エントリポイント
デフ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(Admin、Pause、Fa1_2、Mint、Burn、ChangeMetadata):
デフ __init__(自己、管理者、メタデータ、台帳、token_metadata):
ChangeMetadataです。__init__(自己) 燃える。__init__(自己) ミント。__init__(自己) Fa1_2。__init__(self, メタデータ、台帳、token_metadata)
休止。__init__(自己) 管理者__init__(self、 管理者)
クラスViewer_nat(sp.契約):
デフ __init__(セルフ):
self.data.last = sp.cast(なし、 sp.option[sp.nat]) です。 @sp.エントリポイント
def target(self, params):
self.data.last = sp です。一部(パラメータ)
クラスViewer_address(sp.契約):
デフ __init__(セルフ):
self.data.last = sp.cast(なし、 sp.option[sp.address]) @sp.エントリポイント
def target(self, params):
self.data.last = sp です。一部(パラメータ)
「テンプレート」が __name__にない場合:
@sp.add_test(名前= "FA12")
def test()を使用します。
sc = sp.test_scenario(m) sc.h1("FA1.2 テンプレート - 代替可能な資産")
# sp.test_account はED25519キーペアを確定的に生成します。
admin = sp.test_account("管理者") alice = sp.test_account("アリス") bob = sp.test_account("ロバート") # アカウントを表示しましょう。
sc.h1("アカウント")
sc.show([管理者、アリス、ボブ])
sc.h1("契約")
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("オフチェーンビュー - token_metadata")
sc.verify_equal(
SPの。ビュー(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(アドレス=アリス.アドレス、値=12).run(送信者=管理者) c1.mint(アドレス=アリス.アドレス、値=3).run(送信者=管理者) c1.mint(アドレス=アリス.アドレス、値=3).run(送信者=管理者) sc.h2("アリスはボブに転送します")
c1.transfer(from_=alice.address, to_=bob.address、 値=4).run(送信者=アリス) sc.verify(c1.data.balances[alice.address].balance == 14)
sc.h2("ボブはアリスから転勤しようとするが、アリスの承認を得ていない")
c1.transfer(from_=alice.address, to_=bob.address、 value=4).run( 送信者=ボブ、有効=False
)
sc.h2("Alice が Bob と Bob の転送を承認")
c1.approve(spender=bob.address, value=5).run(sender=alice) c1.transfer(from_=alice.address, to_=bob.address、 値=4).run(送信者=ボブ) sc.h2("ボブはアリスから過剰転送を試みます")
c1.transfer(from_=alice.address, to_=bob.address、 value=4).run( 送信者=ボブ、有効=False
)
sc.h2("管理者がボブトークンを燃やす")
c1.burn(アドレス= bob.address、 値=1).run(送信者=管理者) sc.verify(c1.data.balances[alice.address].balance == 10)
sc.h2("アリスはボブトークンを燃やそうとします")
c1.burn(アドレス= bob.address、 value=1).run(sender=alice, valid=False) です。
sc.h2("管理者が契約を一時停止し、アリスはもう転送できません")
c1.setPause(True).run(sender=admin) c1.transfer(from_=alice.address, to_=bob.address、 value=4).run( sender=アリス、有効=False
)
sc.verify(c1.data.balances[alice.address].balance == 10)
sc.h2("一時停止中に管理者が転送します")
c1.transfer(from_=alice.address, to_=bob.address、 値=1).run(送信者=管理者) sc.h2("管理者が契約の一時停止を解除し、転送が許可されます")
c1.setPause(False).run(sender = admin) sc.verify(c1.data.balances[alice.address].balance == 9)
c1.transfer(from_=alice.address, to_=bob.address、 値=1).run(送信者=アリス) 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("ビュー")
sc.h2("残高")
view_balance = m.Viewer_nat()
sc += view_balance
target = 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
target = 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
target = 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
target = sp.contract(sp.TNat、view_allowance.address、 "ターゲット").open_some()
c1.getAllowance((sp.record(owner=alice.address, spender=bob.address), ターゲット))
sc.verify_equal(view_allowance.data.last、 sp.some(1))
元のFA1.2コントラクトには、トークンの転送、転送の承認、残高の確認、トークンの総供給量の表示などの基本的な機能があります。 次に、この機能を強化します。
万丈! FA1.2規格を使用してTezosで最初のファンジブルトークンを作成しました。
次のレッスンでは、作成したトークンコントラクトを操作する方法を学習します。 これには、トークンの転送、トークンの転送の承認、トークンの残高と総供給量の確認が含まれます。 乞うご期待!