Skip to content
On this page

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.

PolicyNameDescription
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
                )
                # ...