Monorepo for Aesthetic.Computer aesthetic.computer
4
fork

Configure Feed

Select the types of activity you want to include in your feed.

at 2c33ecdf94c8d90b421cc222b310e1e2d179f062 216 lines 7.6 kB view raw
1import smartpy as sp 2from smartpy.templates import fa2_lib as fa2 3 4# Main template for FA2 contracts 5main = fa2.main 6 7 8@sp.module 9def my_module(): 10 import main 11 12 # Order of inheritance: [Admin], [<policy>], <base class>, [<other mixins>]. 13 class MyNFTContract( 14 main.Admin, 15 main.Nft, 16 main.MintNft, 17 main.BurnNft, 18 main.OnchainviewBalanceOf, 19 ): 20 def __init__(self, admin_address, contract_metadata, ledger, token_metadata): 21 """Initializes the contract with administrative permissions and NFT functionalities. 22 The base class is required; all mixins are optional. 23 The initialization must follow this order: 24 25 - Other mixins such as OnchainviewBalanceOf, MintNFT, and BurnNFT 26 - Base class: NFT 27 - Transfer policy 28 - Admin 29 """ 30 31 # Initialize on-chain balance view 32 main.OnchainviewBalanceOf.__init__(self) 33 34 # Initialize the NFT-specific entrypoints 35 main.BurnNft.__init__(self) 36 main.MintNft.__init__(self) 37 38 # Initialize the NFT base class 39 main.Nft.__init__(self, contract_metadata, ledger, token_metadata) 40 41 # Initialize administrative permissions 42 main.Admin.__init__(self, admin_address) 43 44 45def _get_balance(fa2_contract, args): 46 """Utility function to call the contract's get_balance view to get an account's token balance.""" 47 return sp.View(fa2_contract, "get_balance")(args) 48 49 50def _total_supply(fa2_contract, args): 51 """Utility function to call the contract's total_supply view to get the total amount of tokens.""" 52 return sp.View(fa2_contract, "total_supply")(args) 53 54 55@sp.add_test() 56def test(): 57 # Create and configure the test scenario 58 # Import the types from the FA2 library, the library itself, and the contract module, in that order. 59 scenario = sp.test_scenario("fa2_lib_nft") 60 scenario.h1("FA2 NFT contract test") 61 62 # Define test accounts 63 admin = sp.test_account("Admin") 64 alice = sp.test_account("Alice") 65 bob = sp.test_account("Bob") 66 67 # Define initial token metadata and ownership 68 tok0_md = fa2.make_metadata(name="Token Zero", decimals=1, symbol="Tok0") 69 tok1_md = fa2.make_metadata(name="Token One", decimals=1, symbol="Tok1") 70 tok2_md = fa2.make_metadata(name="Token Two", decimals=1, symbol="Tok2") 71 token_metadata = [tok0_md, tok1_md, tok2_md] 72 ledger = {0: alice.address, 1: alice.address, 2: bob.address} 73 74 # Instantiate the FA2 NFT contract 75 contract = my_module.MyNFTContract( 76 admin.address, sp.big_map(), ledger, token_metadata 77 ) 78 79 # Build contract metadata content 80 contract_metadata = sp.create_tzip16_metadata( 81 name="My FA2 NFT contract", 82 description="This is an FA2 NFT contract using SmartPy.", 83 version="1.0.0", 84 license_name="CC-BY-SA", 85 license_details="Creative Commons Attribution Share Alike license 4.0 https://creativecommons.org/licenses/by/4.0/", 86 interfaces=["TZIP-012", "TZIP-016"], 87 authors=["SmartPy <https://smartpy.io/#contact>"], 88 homepage="https://smartpy.io/ide?template=fa2_lib_nft.py", 89 # Optionally, upload the source code to IPFS and add the URI here 90 source_uri=None, 91 offchain_views=contract.get_offchain_views(), 92 ) 93 94 # Add the info specific to FA2 permissions 95 contract_metadata["permissions"] = { 96 # The operator policy chosen: 97 # owner-or-operator-transfer is the default. 98 "operator": "owner-or-operator-transfer", 99 # Those two options should always have these values. 100 # It means that the contract doesn't use the hook mechanism. 101 "receiver": "owner-no-hook", 102 "sender": "owner-no-hook", 103 } 104 105 # You must upload the contract metadata to IPFS and get its URI. 106 # You can write the contract_metadata object to a JSON file with json.dumps() and upload it manually. 107 # You can also use sp.pin_on_ipfs() to upload the object via pinata.cloud and get the IPFS URI: 108 # metadata_uri = sp.pin_on_ipfs(contract_metadata, api_key=None, secret_key=None, name = "Metadata for my FA2 contract") 109 110 # This is a placeholder value. In production, replace it with your metadata URI. 111 metadata_uri = "ipfs://example" 112 113 # Create the metadata big map based on the IPFS URI 114 contract_metadata = sp.scenario_utils.metadata_of_url(metadata_uri) 115 116 # Update the scenario instance with the new metadata 117 contract.data.metadata = contract_metadata 118 119 # Originate the contract in the test scenario 120 scenario += contract 121 122 if scenario.simulation_mode() is sp.SimulationMode.MOCKUP: 123 scenario.p("mockups - fix transfer based testing") 124 return 125 126 # Run tests 127 128 scenario.h2("Verify the initial owners of the tokens") 129 scenario.verify( 130 _get_balance(contract, sp.record(owner=alice.address, token_id=0)) == 1 131 ) 132 scenario.verify( 133 _get_balance(contract, sp.record(owner=bob.address, token_id=0)) == 0 134 ) 135 scenario.verify( 136 _get_balance(contract, sp.record(owner=alice.address, token_id=1)) == 1 137 ) 138 scenario.verify( 139 _get_balance(contract, sp.record(owner=bob.address, token_id=1)) == 0 140 ) 141 scenario.verify( 142 _get_balance(contract, sp.record(owner=alice.address, token_id=2)) == 0 143 ) 144 scenario.verify( 145 _get_balance(contract, sp.record(owner=bob.address, token_id=2)) == 1 146 ) 147 148 # Verify the token supply 149 scenario.verify(_total_supply(contract, sp.record(token_id=0)) == 1) 150 scenario.verify(_total_supply(contract, sp.record(token_id=1)) == 1) 151 scenario.verify(_total_supply(contract, sp.record(token_id=2)) == 1) 152 153 scenario.h2("Transfer a token") 154 contract.transfer( 155 [ 156 sp.record( 157 from_=alice.address, 158 txs=[sp.record(to_=bob.address, amount=1, token_id=0)], 159 ), 160 ], 161 _sender=alice, 162 ) 163 # Verify the result 164 scenario.verify( 165 _get_balance(contract, sp.record(owner=alice.address, token_id=0)) == 0 166 ) 167 scenario.verify( 168 _get_balance(contract, sp.record(owner=bob.address, token_id=0)) == 1 169 ) 170 # Transfer it back 171 contract.transfer( 172 [ 173 sp.record( 174 from_=bob.address, 175 txs=[sp.record(to_=alice.address, amount=1, token_id=0)], 176 ), 177 ], 178 _sender=bob, 179 ) 180 181 scenario.h2("Mint a token") 182 nft3_md = fa2.make_metadata(name="Token Three", decimals=1, symbol="Tok3") 183 # Verify that only the admin can mint a token 184 contract.mint( 185 [ 186 sp.record(metadata=nft3_md, to_=bob.address), 187 ], 188 _sender=bob, 189 _valid=False, 190 ) 191 # Mint a token 192 contract.mint( 193 [ 194 sp.record(metadata=nft3_md, to_=bob.address), 195 ], 196 _sender=admin, 197 ) 198 # Verify the result 199 scenario.verify(_total_supply(contract, sp.record(token_id=3)) == 1) 200 scenario.verify( 201 _get_balance(contract, sp.record(owner=alice.address, token_id=3)) == 0 202 ) 203 scenario.verify( 204 _get_balance(contract, sp.record(owner=bob.address, token_id=3)) == 1 205 ) 206 207 scenario.h2("Burn a token") 208 # Verify that you can't burn someone else's token 209 contract.burn( 210 [sp.record(token_id=3, from_=bob.address, amount=1)], 211 _sender=alice, 212 _valid=False, 213 ) 214 215 # Verify that you can burn your own token 216 contract.burn([sp.record(token_id=3, from_=bob.address, amount=1)], _sender=bob)