I woould like to override an entrypoint for minting nfts, in a contract where there is a maximal amount of tokens - how to achive that based on :
import smartpy as sp
from smartpy.templates import fa2_lib as fa2
Main template for FA2 contracts
main = fa2.main
@sp.module
def 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 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)
main.Admin.__init__(self, admin_address)
# Override this function so anyone can mint for the purposes of the tutorial
@sp.private()
def is_administrator_(self):
return True
@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.ledger.get(
param.address, default=sp.record(balance=0, approvals={})
)
receiver_balance.balance += param.value
self.data.ledger[param.address] = receiver_balance
self.data.total_supply += param.value
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)
Create token metadata
Adapted from fa2.make_metadata
def create_metadata(symbol, name, decimals, displayUri, artifactUri, description, thumbnailUri):
return sp.map(
l={
“name”: sp.scenario_utils.bytes_of_string(name),
“decimals”: sp.scenario_utils.bytes_of_string(“%d” % decimals),
“symbol”: sp.scenario_utils.bytes_of_string(symbol),
“displayUri”: sp.scenario_utils.bytes_of_string(displayUri),
“artifactUri”: sp.scenario_utils.bytes_of_string(artifactUri),
“description”: sp.scenario_utils.bytes_of_string(description),
“thumbnailUri”: sp.scenario_utils.bytes_of_string(thumbnailUri),
}
)
@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”, my_module)
scenario.h1(“FA2 NFT contract test”)
# Define test accounts
#admin = sp.test_account("Admin")
admin = sp.address("tz1eTaZePB1rHygDV9fwt3zJ2FXo9fgGetTe")
alice = sp.test_account("Alice")
bob = sp.test_account("Bob")
# Precreated image on IPFS
token_thumb_uri = "https://gateway.pinata.cloud/ipfs/QmRCp4Qc8afPrEqtM1YdRvNagWCsFGXHgGjbBYrmNsBkcE"
# Define initial token metadata and ownership
tok0_md = create_metadata(
"Tok0",
"Token Zero",
0,
token_thumb_uri,
token_thumb_uri,
"My first token",
token_thumb_uri,
)
tok1_md = create_metadata(
"Tok1",
"Token One",
0,
token_thumb_uri,
token_thumb_uri,
"My second token",
token_thumb_uri,
)
tok2_md = create_metadata(
"Tok2",
"Token Two",
0,
token_thumb_uri,
token_thumb_uri,
"My third token",
token_thumb_uri,
)
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(
# alice.address, sp.big_map(), ledger, token_metadata
#)
contract = my_module.MyNFTContract(
admin, 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")
# Mint a token
contract.mint(
[
sp.record(metadata=nft3_md, to_=bob.address),
],
_sender=bob,
)
# 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)