Policies โ
The operator transfer permission policy specifies who is permitted to transfer tokens.
See tzip-12/permissions-policy.md# for detailed info about the standard.
The FA2 library provides the 3 standard policies and a non-standard one.
Policy | Name | Description |
---|---|---|
NoTransfer | "no-transfer" | Nobody can transfer tokens. Useful when tokens represent permissions. |
OwnerTransfer | "owner-transfer" | Only owners can transfer their tokens. The operators are not supported. |
OwnerOrOperatorTransfer (default) | "owner-or-operator-transfer" | Owner or operators of the owner can transfer tokens. Only owner can change their operators. |
Pause | "pauseable-<policy-name>" | Decorate any policy to add a pause mechanism. Ads set_pause entrypoint. transfer and update_operator are sensible to paused attribute. |
Policies are provided as an argument to the base classes.
Examples:
python
class NftWithoutTransfer(FA2.Fa2Nft):
def __init__(self, admin, metadata):
FA2.Fa2Nft.__init__(self, metadata, policy = FA2.NoTransfer())
class FungibleWithPauseAndOwnerTransfer(FA2.Fa2Fungible):
def __init__(self, admin, metadata):
FA2.Fa2Fungible.__init__(self, metadata, policy = FA2.Pause(FA2.OwnerTransfer()))
class SingleAssetDefault(FA2.Fa2SingleAsset):
def __init__(self, admin, metadata):
FA2.Fa2SingleAsset.__init__(self, metadata)
Writing your own policy โ
You can write your own policy by creating a class that respects the following interface:
python
class MyPolicy:
def init_policy(self, contract):
# Called by the `__init__` of the base class.
# Use it to update the storage if you want
# and to set the expected attributes.
# Name of your policy, added to the contract metadata.
self.name = "your-policy-name"
# Does it support operator?
self.supports_operator = False
# Does it support transfer?
self.supports_transfer = False
# You can update the initial storage
# Example:
# contract.update_initial_storage(
# operators=sp.big_map(tkey=t_operator_permission, tvalue=sp.TUnit)
# )
def check_tx_transfer_permissions(self, contract, from_, to_, token_id):
# Called each time a transfer transaction is being looked at.
def check_operator_update_permissions(self, contract, operator_permission):
# Called each time an update_operator action is being looked at.
def is_operator(self, contract, operator_permission):
# Return sp.bool(True) or sp.bool(False) if `operator_permission`
# describes a registered operator.
return sp.bool(False)
All methods receive a contract
parameter. This one corresponds to the FA2 contract to which the policy is applied.
The contract data is accessible in contract.data
(not self.data
).
Example:
python
def is_operator(self, contract, operator_param):
return contract.data.operators.contains(operator_param)
Entrypoints that rely on policy โ
You can access policies' methods and attributes in your custom entrypoints with self.policy
.
Example:
python
class ExampleFa2Nft(FA2.Fa2Nft):
@sp.entrypoint
def burn(self, batch):
# We check the policy
sp.verify(self.policy.supports_transfer, "FA2_TX_DENIED")
with sp.for_("action", batch) as action:
sp.verify(self.is_defined(action.token_id), "FA2_TOKEN_UNDEFINED")
# We check the policy
self.policy.check_tx_transfer_permissions(
self, action.from_, action.from_, action.token_id
)
# ...