0x0::unprotected_safe

Module of UnprotectedSafe type.

UnprotectedSafe is an abstraction meant to hold NFTs in it. A user that transfers its NFTs to its Safe is able to delegate the power of transferability. One typical issue with on-chain trading is that by sending one's assets to a shared object (the trading primitive), one looses the ability to see them in their wallet, even though one has still technical ownership of such assets, until a trade is effectively executed. To solve for this, we use UnprotectedSafe to hold the user's assets and then instead of transferring the assets to the shared object (trading primitive), the user transfers a TransferCap TransferCap is an object that delegates the ability to transfer a given NFT out of the seller's Safe.

The ownership model of the Safe relies on the object OwnerCap whose holder is the effective owner of the Safe and subsequently the owner of the assets within it.

Two NFT kinds

We support two kinds of NFTs in this safe implementation.

  1. Our protocol nft_protocol::nft::Nft which is guarded with allowlist. This enables creators to have certain guarantees around royalty enforcement.
  2. Arbitrary type of NFTs. Those are not guarded with allowlist. They can be freely transferred between users and safes.

Structs

unprotected_safe::UnprotectedSafe has store, key

Fields:

Name Type Description
id object::UID
refs vec_map::VecMap<object::ID, unprotected_safe::NftRef>

Accounting for deposited NFTs. Each NFT in the object bag is represented in this map.

unprotected_safe::NftRef has copy, drop, store

Fields:

Name Type Description
version object::ID

Is generated anew every time a counter is incremented from zero to one.

We don't use monotonically increasing integer so that we can remove withdrawn NFTs from the map.

transfer_cap_counter u64

How many transfer caps are there for this version.

is_exclusively_listed bool

Only one TransferCap of the latest version can exist. An exclusively listed NFT cannot have its TransferCap revoked.

is_generic bool

Signalizes whether given NFT is wrapped in our NFT type (nft::Nft) or whether it's a 3rd party type.

This has implications on how the NFT is transferred.

object_type type_name::TypeName

What's the NFT type.

If it's not generic, it will contain the Nft<C> wrapper.

Keeps info about an NFT which enables us to issue transfer caps etc.

unprotected_safe::OwnerCap has store, key

Fields:

Name Type Description
id object::UID
safe object::ID

Whoever owns this object can perform some admin actions against the Safe shared object with the corresponding id.

unprotected_safe::TransferCap has store, key

Fields:

Name Type Description
id object::UID
safe object::ID
nft object::ID
version object::ID
is_exclusive bool

If set to true, only one TransferCap can be issued for this NFT. It also cannot be revoked without burning this object.

This is useful for trading flows that cannot guarantee that the NFT is claimed atomically.

If an NFT is listed exclusively, it cannot be revoked without burning the TransferCap first.

is_generic bool

Signalizes whether given NFT is wrapped in our NFT type (nft::Nft) or whether it's a 3rd party type.

This has implications on how the NFT is transferred.

object_type type_name::TypeName

What's the NFT type.

If it's not generic, it will contain the Nft<C> wrapper.

Enables the owner to transfer given NFT out of the Safe.

unprotected_safe::DepositEvent has copy, drop

Fields:

Name Type Description
safe object::ID
nft object::ID

unprotected_safe::TransferEvent has copy, drop

Fields:

Name Type Description
safe object::ID
nft object::ID

Methods

public fun new(
    ctx: &mut tx_context::TxContext,
): 
    (unprotected_safe::UnprotectedSafe, unprotected_safe::OwnerCap)

public entry fun create_for_sender(
    ctx: &mut tx_context::TxContext,
)

Instantiates a new shared object Safe and transfer OwnerCap to the tx sender.

public fun create_safe(ctx: &mut tx_context::TxContext): 
    unprotected_safe::OwnerCap

Creates a new Safe shared object and returns the authority capability that grants authority over this safe.

public fun create_transfer_cap(
    nft: object::ID,
    owner_cap: &unprotected_safe::OwnerCap,
    safe: &mut unprotected_safe::UnprotectedSafe,
    ctx: &mut tx_context::TxContext,
): unprotected_safe::TransferCap

Creates a TransferCap which must be claimed atomically.

Otherwise, there's a risk of a race condition as multiple non-exclusive transfer caps can be created.

public fun create_exclusive_transfer_cap(
    nft: object::ID,
    owner_cap: &unprotected_safe::OwnerCap,
    safe: &mut unprotected_safe::UnprotectedSafe,
    ctx: &mut tx_context::TxContext,
): unprotected_safe::TransferCap

Creates an irrevocable and exclusive transfer cap.

Useful for trading contracts which cannot claim an NFT atomically.

public entry fun deposit_nft<T>(
    nft: nft::Nft<T>,
    safe: &mut unprotected_safe::UnprotectedSafe,
    ctx: &mut tx_context::TxContext,
)

Transfer an NFT into the Safe.

public entry fun deposit_generic_nft<T: store + key>(
    nft: T,
    safe: &mut unprotected_safe::UnprotectedSafe,
    ctx: &mut tx_context::TxContext,
)

Transfer an NFT into the Safe.

The type T here can refer to any object, not just the NFT protocol's exported NFT type.

public fun transfer_nft_to_recipient<T, Auth: drop>(
    transfer_cap: unprotected_safe::TransferCap,
    recipient: address,
    authority: Auth,
    allowlist: &transfer_allowlist::Allowlist,
    safe: &mut unprotected_safe::UnprotectedSafe,
)

Use a transfer cap to get an NFT out of the Safe.

If the NFT is not exclusively listed, it can happen that the transfer cap is no longer valid. The NFT could've been traded or the trading cap revoked.

public fun transfer_generic_nft_to_recipient<T: store + key>(
    transfer_cap: unprotected_safe::TransferCap,
    recipient: address,
    safe: &mut unprotected_safe::UnprotectedSafe,
)

public fun transfer_nft_to_safe<T, Auth: drop>(
    transfer_cap: unprotected_safe::TransferCap,
    recipient: address,
    authority: Auth,
    allowlist: &transfer_allowlist::Allowlist,
    source: &mut unprotected_safe::UnprotectedSafe,
    target: &mut unprotected_safe::UnprotectedSafe,
    ctx: &mut tx_context::TxContext,
)

Use a transfer cap to get an NFT out of source Safe and deposit it to the target Safe. The recipient address should match the owner of the target Safe.

If the NFT is not exclusively listed, it can happen that the transfer cap is no longer valid. The NFT could've been traded or the trading cap revoked.

public fun transfer_generic_nft_to_safe<T: store + key>(
    transfer_cap: unprotected_safe::TransferCap,
    source: &mut unprotected_safe::UnprotectedSafe,
    target: &mut unprotected_safe::UnprotectedSafe,
    ctx: &mut tx_context::TxContext,
)

public entry fun burn_transfer_cap(
    transfer_cap: unprotected_safe::TransferCap,
    safe: &mut unprotected_safe::UnprotectedSafe,
)

Destroys given transfer cap. This is mainly useful for exclusively listed NFTs.

public entry fun delist_nft(
    nft: object::ID,
    owner_cap: &unprotected_safe::OwnerCap,
    safe: &mut unprotected_safe::UnprotectedSafe,
    ctx: &mut tx_context::TxContext,
)

Changes the transfer ref version, thereby invalidating all existing TransferCap objects.

Can happen only if the NFT is not listed exclusively.

public fun borrow_nft<C>(
    nft: object::ID,
    safe: &unprotected_safe::UnprotectedSafe,
): &nft::Nft<C>

public fun has_nft<C>(
    nft: object::ID,
    safe: &unprotected_safe::UnprotectedSafe,
): bool

public fun borrow_generic_nft<C: store + key>(
    nft: object::ID,
    safe: &unprotected_safe::UnprotectedSafe,
): &C

public fun has_generic_nft<T: store + key>(
    nft: object::ID,
    safe: &unprotected_safe::UnprotectedSafe,
): bool

public fun owner_cap_safe(cap: &unprotected_safe::OwnerCap): 
    object::ID

public fun transfer_cap_safe(
    cap: &unprotected_safe::TransferCap,
): object::ID

public fun transfer_cap_nft(
    cap: &unprotected_safe::TransferCap,
): object::ID

public fun transfer_cap_object_type(
    cap: &unprotected_safe::TransferCap,
): type_name::TypeName

public fun transfer_cap_version(
    cap: &unprotected_safe::TransferCap,
): object::ID

public fun transfer_cap_is_exclusive(
    cap: &unprotected_safe::TransferCap,
): bool

public fun transfer_cap_is_nft_generic(
    cap: &unprotected_safe::TransferCap,
): bool

public fun assert_owner_cap(
    cap: &unprotected_safe::OwnerCap,
    safe: &unprotected_safe::UnprotectedSafe,
)

public fun assert_transfer_cap_of_safe(
    cap: &unprotected_safe::TransferCap,
    safe: &unprotected_safe::UnprotectedSafe,
)

public fun assert_nft_of_transfer_cap(
    nft: &object::ID,
    cap: &unprotected_safe::TransferCap,
)

public fun assert_has_nft(
    nft: &object::ID,
    safe: &unprotected_safe::UnprotectedSafe,
)

public fun assert_not_exclusively_listed(
    cap: &unprotected_safe::TransferCap,
)

public fun assert_id(
    safe: &unprotected_safe::UnprotectedSafe,
    id: object::ID,
)

public fun assert_transfer_cap_exclusive(
    cap: &unprotected_safe::TransferCap,
)

public fun assert_transfer_cap_of_native_nft(
    cap: &unprotected_safe::TransferCap,
)

public fun assert_nft_type<C>(
    cap: &unprotected_safe::TransferCap,
)

Checks that the transfer cap is issued for an NFT of type Nft<C>

public fun assert_generic_nft_type<C>(
    cap: &unprotected_safe::TransferCap,
)

Checks that the transfer cap is issued for an NFT of type C