-
Notifications
You must be signed in to change notification settings - Fork 6
Sales
There are two kinds of sales.
Direct sales list assets for a certain on-chain token, which are subsequently also paid with that token.
Delphi sales use the delphioracle to allow for sales that use a listing price that has a different symbol than the token the sale is actually paid in. An example would be listing a sale for 10.00 USD, but when someone decides to purchase it, it is paid in WAX tokens, which are worth 10.00 USD at the time of purchase.
⚠️ If you're planning on developing an interface for the AtomicMarket, please take also take a look at the Inactive / Invalid Sales and the A note about smart contract sellers sections.
First, you need to announce the sale using the announcesale action. Any supported token (found in the config) can be used for the listing_price. For settlement_symbol, you need to use the symbol of the listing price.
An example would be listing_price: "10.00000000 WAX" , settlement_symbol: "8,WAX"
You can sell any number of assets in a single sale. However, all assets need to belong to the same collection, and need to be transferable. Also, the seller needs to own all the assets.
Then, an AtomicAssets offer needs to be created using the createoffer action in the AtomicAssets contract.
This offer needs to be from the seller to the AtomicMarket account and need to offer the assets of the announced sale and ask for nothing in return. The memo needs to be sale
.
This offer will only be accepted by the AtomicMarket account when somebody purchases the sale. Therefore, sellers keep ownership of their assets while they are listed for sale.
Sales are paid from internal balance of the buyer. Therefore, the buyer first needs to deposit the necessary tokens (More Details).
After depositing the required tokens, the sale can be purchased using the purchasesale action. The intended_delphi_median is only used for Delphi sales, therefore it needs to be set to 0.
The offer previously created by the seller is then accepted by the AtomicMarket contract, and the assets are consequently transferred to the buyer. The price is deducted from the buyer's balance and the seller / the marketplaces / the collection all have their share of the price added to their balance (Fee structure).
Sales can be cancelled using the cancelsale action.
The process for creating a Delphi sale is similar. First, the sale needs to be announced using the announcesale action. The supported pairs for Delphi sales are listed in the supported_symbol_pairs field in the config. The listing price needs to use the listing_symbol defined in the pair, and the settlement_symbol needs to equal the one defined in the pair.
Just like for direct sales, any amount of transferable assets from the same collection can be sold at once.
And just like for direct sales, the seller has to create an AtomicAssets offer, offering the assets with the memo sale
.
After depositing the required tokens (the ones belonging to the settlement_symbol), once again the purchasesale action needs to be called.
The intended_delphi_median parameter now needs to be the delphioracle median that should be used for the price calculation. By supplying this as a parameter (instead of e.g. always using the latest median), you can already calculate how much actual tokens of the settlement_symbol the purchase will cost at the time of signing the action.
The datapoints table (belonging to the delphi_pair_name defined in the used SYMBOLPAIR) in the delphioracle smart contract is then checked. At least one row of that needs to have the same median as the intended_delphi_median parameter.
Assuming there are an adequate number of block producers pushing price data to the delphioracle, this means that the intended_delphi_median needs to equal a real median of the last few minutes, thus giving users enough time between creating the transaction and actually signing and broadcasting it, but without opening the door for exploits that try to use old price data.
The final price that needs to be pair (for non-inverted symbol pairs) is:
uint64_t settlement_price_amount = (double) sale_itr->listing_price.amount / (double) intended_delphi_median * pow(
10,
pair_itr->quoted_precision + sale_itr->settlement_symbol.precision() - sale_itr->listing_price.symbol.precision()
);
sale_price = asset(settlement_price_amount, sale_itr->settlement_symbol);
Just like with direct sales, the offer that the seller previously created is then accepted and the assets are transferred to the buyer. The settlement price is deducted from the buyer's balance and added the seller / marketplaces / collection get their cut of the sale.
Not all sales in the sales table can actually be accepted. Inactive and invalid sales should therefore not be displayed by marketplaces, as they would result in an error when attempting to purchase them.
When the offer_id in the sales table row is -1
, no offer for this sale has been created by the seller. Therefore, the AtomicMarket contract can't get ownership of the assets, and the sale is considered inactive.
There are two things that can make a sale invalid:
- If the offer_id stored in the sales entry of this sale does not exist anymore in the AtomicAssets offers table, that means it was cancelled by the seller. The AtomicMarket therefore can't get ownership of the assets, and the sale is considered invalid.
- If the seller does not own all of the assets of the sale, because they transferred it away after announcing the sale, the created offer can not be accepted and the AtomicMarket therefore can't get ownership of the assets, and the sale is considered invalid.
Invalid offers can be cancelled using the cancelsale action without the authorization of the seller. This is meant to be used as a convenience functionality, so that dapps can run scripts off-chain that automatically cancel invalid offers to free the RAM.
When a sale listing is purchased, the AtomicAssets offer created by the seller is accepted. This triggers an inline notification to the sender of the offer = the seller. The seller could deploy a smart contract on their account that makes the transaction throw.
An attacker could for example list assets for way below market price, and then make every purchase attempt throw through a smart contract deployed on their account. This is not harmful in and of itself, however it can cause a bad experience for the tricked buyer who probably won't understand why their purchase does not work.
When developing public marketplaces, we therefore advise not to display sales of accounts that have a smart contract deployed, or only display them from trusted smart contract accounts.
Marketplace for atomicassets.io - developed with ❤️ by pink.network