Write Entry Functions

Having outlined how to store listing data for token sales, the next step is to define entry functions that enable our smart contract to operate autonomously. We can define two entry functions that are triggered by user actions in the marketplace:

  1. Seller Lists a Token for Sale: list_with_fixed_price function
  2. Buyer Purchases the Listed Token: purchase function

1. Function list_with_fixed_price

NFT holders can call this function to list their NFT for sale at a fixed price.

In this function, we create a new object owned by the seller and move the Listing and FixedPriceListing resources to the object. We also transfer the token to the object signer, which works as an escrow account.

  • A new object is created from the seller's account, calling the create_object function from the object module. We will store Listing and FixedPriceListing resources in this object.

/// List an time for sale at a fixed price.
public entry fun list_with_fixed_price<CoinType>(
seller: &signer,
object: Object<ObjectCore>,
price: u64,
) {
let constructor_ref = object::create_object(signer::address_of(seller));
let transfer_ref = object::generate_transfer_ref(&constructor_ref);
object::disable_ungated_transfer(&transfer_ref);
let listing_signer = object::generate_signer(&constructor_ref);
let listing = Listing {
object,
seller: signer::address_of(seller),
delete_ref: object::generate_delete_ref(&constructor_ref),
extend_ref: object::generate_extend_ref(&constructor_ref),
};
let fixed_price_listing = FixedPriceListing<CoinType> {
price,
};
move_to(&listing_signer, listing);
move_to(&listing_signer, fixed_price_listing);
object::transfer(seller, object, signer::address_of(&listing_signer));
}


  • We will not allow ungated transfer of the object by calling the disable_ungated_transfer function from the object module.

object::disable_ungated_transfer(&transfer_ref);

  • We create a new Listing and FixedPriceListing resources and move them to the object signer.

let listing_signer = object::generate_signer(&constructor_ref);
let listing = Listing {
object,
seller: signer::address_of(seller),
delete_ref: object::generate_delete_ref(&constructor_ref),
extend_ref: object::generate_extend_ref(&constructor_ref),
};
let fixed_price_listing = FixedPriceListing<CoinType> {
price,
};
move_to(&listing_signer, listing);
move_to(&listing_signer, fixed_price_listing);

  • Finally we transfer the token to the object signer, which work as a escrow account.

object::transfer(seller, object, signer::address_of(&listing_signer));

2. Function purchase

Buyers can call this function to purchase an NFT from a fixed price listing.

This function will transfer the NFT to the buyer and the payment to the seller. It will also delete the object which contains Listing and FixedPriceListing resources.


/// Purchase outright an item from a fixed price listing.
public entry fun purchase<CoinType>(
purchaser: &signer,
object: Object<ObjectCore>,
) acquires FixedPriceListing, Listing {
let listing_addr = object::object_address(&object);
assert!(exists<Listing>(listing_addr), error::not_found(ENO_LISTING));
assert!(exists<FixedPriceListing<CoinType>>(listing_addr), error::not_found(ENO_LISTING));
let FixedPriceListing {
price,
} = move_from<FixedPriceListing<CoinType>>(listing_addr);
// The listing has concluded, transfer the asset and delete the listing. Returns the seller
// for depositing any profit.
let coins = coin::withdraw<CoinType>(purchaser, price);
let Listing {
object,
seller, // get seller from Listing object
delete_ref,
extend_ref,
} = move_from<Listing>(listing_addr);
let obj_signer = object::generate_signer_for_extending(&extend_ref);
object::transfer(&obj_signer, object, signer::address_of(purchaser));
object::delete(delete_ref); // Clean-up the listing object.
aptos_account::deposit_coins(seller, coins);
}

  • Check if the Listing and FixedPriceListing resources exist for the given object address, using exists native function.

assert!(exists<Listing>(listing_addr), error::not_found(ENO_LISTING));
assert!(exists<FixedPriceListing<CoinType>>(listing_addr), error::not_found(ENO_LISTING));

  • We move the FixedPriceListing resource from the global storage (under object address) and extract the price of the NFT. Then, we withdraw the payment from the buyer's account using the coin::withdraw function.

let FixedPriceListing {
price,
} = move_from<FixedPriceListing<CoinType>>(listing_addr);
// The listing has concluded, transfer the asset and delete the listing. Returns the seller
// for depositing any profit.
let coins = coin::withdraw<CoinType>(purchaser, price);

  • We move the Listing resource from the global storage (under object address) and extract the object, seller, delete_ref, and extend_ref fields.

let Listing {
object,
seller, // get seller from Listing object
delete_ref,
extend_ref,
} = move_from<Listing>(listing_addr);

  • We transfer the NFT to the buyer and delete the object which contains Listing and FixedPriceListing resources. We use the extend_ref to generate a signer for the object and transfer the escrowed NFT to the buyer.

let obj_signer = object::generate_signer_for_extending(&extend_ref);
object::transfer(&obj_signer, object, signer::address_of(purchaser));
object::delete(delete_ref); // Clean-up the listing object.

  • Finally, we transfer the payment to the seller using the aptos_account::deposit_coins function.

aptos_account::deposit_coins(seller, coins);



More Resources

Learn more about Aptos Move functions.

Link