# Custom Object Transfer Rules

*[Documentation index](/llms.txt) · [Full index](/llms-full.txt)*

Every Sui [object](/develop/sui-architecture/object-model) must have the `key` ability. The `store` ability, on the other hand, is an optional ability you can add to Sui objects. Objects with the `store` ability are transferable by anyone using the [`transfer::public_transfer`](/references/framework/sui_sui/transfer#sui_transfer_public_transfer) function and can be [wrapped](/develop/objects/object-ownership/wrapped) in other objects.

For custom transfer rules, if the Sui object `Object` does not have the `store` ability, you cannot call the [`sui::transfer::public_transfer`](/references/framework/sui_sui/transfer#sui_transfer_public_transfer) function to transfer it. The Move module that defines `Object` is the only entity that can transfer objects of that type using the [`sui::transfer::transfer`](/references/framework/sui_sui/transfer#sui_transfer_transfer) function. Consequently, the module that defines the object `Object` can define a custom transfer function for `Object` that can take any number of arguments and enforce any restrictions desired for performing a transfer operation, for example, a fee must be paid to transfer the object.

## `store` ability

Custom transfer rules for objects enable you to define the transfer conditions that must be met for a valid transfer operation. You should be intentional about adding the `store` ability to an object because you are providing unrestricted access to that object without having to go through the module that defines it.

:::caution

After you enable public transfers on an object, there is no way of re-enabling custom transfer rules or any type of restrictions regarding the transfer of the object.

:::

## Example

This example creates an object type `Object` that is transferable only if the `unlocked` flag inside of it is set to `true`:

```move
public struct Object has key {
    id: UID,
    // An `Object` object can only be transferred if this field is `true`
    unlocked: bool,
}
```

Within the same module that defines the object `Object`, you can then define a custom transfer rule `transfer_unlocked` for `Object` that takes the object to transfer and the address to transfer it to, then verifies that the object is unlocked before transferring it to the specified address:

```move
module examples::custom_transfer;

// Error code for trying to transfer a locked object
const EObjectLocked: u64 = 0;

public struct Object has key {
    id: UID,
    // An `Object` object can only be transferred if this field is `true`
    unlocked: bool,
}

// Check that `Object` is unlocked before transferring it
public fun transfer_unlocked(object: Object, to: address) {
    assert!(object.unlocked, EObjectLocked);
    transfer::transfer(object, to)
}
```

You can define multiple different transfer rules for the same object. Each of these rules might have different restrictions that execution of the transaction can dynamically enforce. If you wanted to allow only locked objects to be transferred to a specific address you could add the following function to the previous module:

```move
const EObjectNotLocked: u64 = 1;
const HOME_ADDRESS = @0xCAFE;

public fun transfer_locked(object: Object) {
    assert!(!object.unlocked, EObjectNotLocked);
    transfer::transfer(object, HOME_ADDRESS)
}
```

With these rules in place, there are different custom transfer rules for any object `Object`. Either it's unlocked and anyone can transfer it, or it's locked and it can only be transferred to `0xCAFE`. Importantly, these are the only ways of transferring any object of type `Object`. In particular, because `Object` does not have the `store` ability, you cannot transfer it using the [`sui::transfer::public_transfer`](/references/framework/sui_sui/transfer#sui_transfer_public_transfer) function.
