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 three 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.
PauseOwnerOrOperatorTransfer"pauseable-owner-or-operator-transfer"Equivalent to OwnerOrOperatorTransfer on which it adds a pause mechanism. This adds the set_pause entrypoint. transfer and update_operator are sensible to paused attribute. It requires the admin mixin.

Policies are inherited in addition to the base classes.

INFO

In SmartPy the order in which superclasses are listed is important.

The expected order of inheritance for the FA2 lib classes is: Admin, policy, base class, followed by any others. Only the base class is mandatory.

Examples:

smartpy
class NftNoTransfer(main.NoTransfer, main.Nft):
    def __init__(self, admin, metadata, ledger, token_metadata):
        main.Nft.__init__(self, metadata, ledger, token_metadata)
        main.NoTransfer.__init__(self)
smartpy
class NftPauseOwnerOrOperatorTransfer(
    main.Admin,
    main.PauseOwnerOrOperatorTransfer,
    main.Nft):
    def __init__(self, administrator, metadata, ledger, token_metadata):
        main.Nft.__init__(self, metadata, ledger, token_metadata)
        main.PauseOwnerOrOperatorTransfer.__init__(self)
        main.Admin.__init__(self, administrator)
smartpy
class NftDefault(main.Nft):
    def __init__(self, admin,  metadata, ledger, token_metadata):
        # `ownerOrTransferTransfer` is the default policy.
        main.Nft.__init__(self,  metadata, ledger, token_metadata)

Writing your own policy

You can write your own policy by creating a class that respects the following interface:

smartpy
class MyPolicy:
    def __init__(self):
        # Use it to update the storage if you want
        # and to set the expected attributes.

        self.private.policy = sp.record(
            # Name of your policy, added to the contract metadata.
            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:
        # self.data.my_var = ...

    @sp.private(with_storage="read-only")
    def check_tx_transfer_permissions(self, params):
        """Called each time a transfer transaction is being looked at."""
        sp.cast(
            params,
            sp.record(
                from_=sp.address,
                to_=sp.address,
                token_id=sp.nat,
            ),
        )
        # ...

    @sp.private(with_storage="read-only")
    def check_operator_update_permissions(self, operator_permission):
        """Called each time an update_operator action is being looked at."""
        sp.cast(operator_permission, t.operator_permission)
        # ...

    @sp.private(with_storage="read-only")
    def is_operator(self, operator_permission) -> sp.bool:
        """Return True if `operator_permission` describes a registered operator, False otherwise."""
        sp.cast(operator_permission, t.operator_permission)
        # ...

Entrypoints that rely on policy

You can access policies' methods and attributes in your custom entrypoints via self.private.

Example:

smartpy
class ExampleFa2Nft(main.Nft):
        @sp.entrypoint
        def burn(self, batch):
            # We check that transfer is allowed
            assert self.private.policy.supports_transfer, "FA2_TX_DENIED"
            for action in batch:
                self.check_tx_transfer_permissions_(
                    sp.record(
                        from_=action.from_, to_=action.from_, token_id=action.token_id
                    )
                )
                # ...