Something went wrong. Try again.
Monorepo for Aesthetic.Computer aesthetic.computer
Something went wrong. Try again.
Python
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217import smartpy as spfrom smartpy.templates import fa2_lib as fa2
# Main template for FA2 contractsmain = fa2.main
@sp.moduledef my_module(): import main
# Order of inheritance: [Admin], [<policy>], <base class>, [<other mixins>]. class MyNFTContract( main.Admin, main.Nft, main.MintNft, main.BurnNft, main.OnchainviewBalanceOf, ): def __init__(self, admin_address, contract_metadata, ledger, token_metadata): """Initializes the contract with administrative permissions and NFT functionalities. The base class is required; all mixins are optional. The initialization must follow this order:
- Other mixins such as OnchainviewBalanceOf, MintNFT, and BurnNFT - Base class: NFT - Transfer policy - Admin """
# Initialize on-chain balance view main.OnchainviewBalanceOf.__init__(self)
# Initialize the NFT-specific entrypoints main.BurnNft.__init__(self) main.MintNft.__init__(self)
# Initialize the NFT base class main.Nft.__init__(self, contract_metadata, ledger, token_metadata)
# Initialize administrative permissions main.Admin.__init__(self, admin_address)
def _get_balance(fa2_contract, args): """Utility function to call the contract's get_balance view to get an account's token balance.""" return sp.View(fa2_contract, "get_balance")(args)
def _total_supply(fa2_contract, args): """Utility function to call the contract's total_supply view to get the total amount of tokens.""" return sp.View(fa2_contract, "total_supply")(args)
@sp.add_test()def test(): # Create and configure the test scenario # Import the types from the FA2 library, the library itself, and the contract module, in that order. scenario = sp.test_scenario("fa2_lib_nft") scenario.h1("FA2 NFT contract test")
# Define test accounts admin = sp.test_account("Admin") alice = sp.test_account("Alice") bob = sp.test_account("Bob")
# Define initial token metadata and ownership tok0_md = fa2.make_metadata(name="Token Zero", decimals=1, symbol="Tok0") tok1_md = fa2.make_metadata(name="Token One", decimals=1, symbol="Tok1") tok2_md = fa2.make_metadata(name="Token Two", decimals=1, symbol="Tok2") token_metadata = [tok0_md, tok1_md, tok2_md] ledger = {0: alice.address, 1: alice.address, 2: bob.address}
# Instantiate the FA2 NFT contract contract = my_module.MyNFTContract( admin.address, sp.big_map(), ledger, token_metadata )
# Build contract metadata content contract_metadata = sp.create_tzip16_metadata( name="My FA2 NFT contract", description="This is an FA2 NFT contract using SmartPy.", version="1.0.0", license_name="CC-BY-SA", license_details="Creative Commons Attribution Share Alike license 4.0 https://creativecommons.org/licenses/by/4.0/", interfaces=["TZIP-012", "TZIP-016"], authors=["SmartPy <https://smartpy.io/#contact>"], homepage="https://smartpy.io/ide?template=fa2_lib_nft.py", # Optionally, upload the source code to IPFS and add the URI here source_uri=None, offchain_views=contract.get_offchain_views(), )
# Add the info specific to FA2 permissions contract_metadata["permissions"] = { # The operator policy chosen: # owner-or-operator-transfer is the default. "operator": "owner-or-operator-transfer", # Those two options should always have these values. # It means that the contract doesn't use the hook mechanism. "receiver": "owner-no-hook", "sender": "owner-no-hook", }
# You must upload the contract metadata to IPFS and get its URI. # You can write the contract_metadata object to a JSON file with json.dumps() and upload it manually. # You can also use sp.pin_on_ipfs() to upload the object via pinata.cloud and get the IPFS URI: # metadata_uri = sp.pin_on_ipfs(contract_metadata, api_key=None, secret_key=None, name = "Metadata for my FA2 contract")
# This is a placeholder value. In production, replace it with your metadata URI. metadata_uri = "ipfs://example"
# Create the metadata big map based on the IPFS URI contract_metadata = sp.scenario_utils.metadata_of_url(metadata_uri)
# Update the scenario instance with the new metadata contract.data.metadata = contract_metadata
# Originate the contract in the test scenario scenario += contract
if scenario.simulation_mode() is sp.SimulationMode.MOCKUP: scenario.p("mockups - fix transfer based testing") return
# Run tests
scenario.h2("Verify the initial owners of the tokens") scenario.verify( _get_balance(contract, sp.record(owner=alice.address, token_id=0)) == 1 ) scenario.verify( _get_balance(contract, sp.record(owner=bob.address, token_id=0)) == 0 ) scenario.verify( _get_balance(contract, sp.record(owner=alice.address, token_id=1)) == 1 ) scenario.verify( _get_balance(contract, sp.record(owner=bob.address, token_id=1)) == 0 ) scenario.verify( _get_balance(contract, sp.record(owner=alice.address, token_id=2)) == 0 ) scenario.verify( _get_balance(contract, sp.record(owner=bob.address, token_id=2)) == 1 )
# Verify the token supply scenario.verify(_total_supply(contract, sp.record(token_id=0)) == 1) scenario.verify(_total_supply(contract, sp.record(token_id=1)) == 1) scenario.verify(_total_supply(contract, sp.record(token_id=2)) == 1)
scenario.h2("Transfer a token") contract.transfer( [ sp.record( from_=alice.address, txs=[sp.record(to_=bob.address, amount=1, token_id=0)], ), ], _sender=alice, ) # Verify the result scenario.verify( _get_balance(contract, sp.record(owner=alice.address, token_id=0)) == 0 ) scenario.verify( _get_balance(contract, sp.record(owner=bob.address, token_id=0)) == 1 ) # Transfer it back contract.transfer( [ sp.record( from_=bob.address, txs=[sp.record(to_=alice.address, amount=1, token_id=0)], ), ], _sender=bob, )
scenario.h2("Mint a token") nft3_md = fa2.make_metadata(name="Token Three", decimals=1, symbol="Tok3") # Verify that only the admin can mint a token contract.mint( [ sp.record(metadata=nft3_md, to_=bob.address), ], _sender=bob, _valid=False, ) # Mint a token contract.mint( [ sp.record(metadata=nft3_md, to_=bob.address), ], _sender=admin, ) # Verify the result scenario.verify(_total_supply(contract, sp.record(token_id=3)) == 1) scenario.verify( _get_balance(contract, sp.record(owner=alice.address, token_id=3)) == 0 ) scenario.verify( _get_balance(contract, sp.record(owner=bob.address, token_id=3)) == 1 )
scenario.h2("Burn a token") # Verify that you can't burn someone else's token contract.burn( [sp.record(token_id=3, from_=bob.address, amount=1)], _sender=alice, _valid=False, )
# Verify that you can burn your own token contract.burn([sp.record(token_id=3, from_=bob.address, amount=1)], _sender=bob)