Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

functions selling any item at a fix price #98

Merged
merged 8 commits into from
Sep 4, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .tool-versions
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
starknet-foundry 0.27.0
scarb 2.6.3
1 change: 1 addition & 0 deletions marketplace/.tool-versions
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
scarb 2.6.3
4 changes: 2 additions & 2 deletions marketplace/Scarb.lock
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,5 @@ source = "git+https://github.com/openzeppelin/cairo-contracts?tag=v0.10.0#d77082

[[package]]
name = "snforge_std"
version = "0.14.0"
source = "git+https://github.com/foundry-rs/starknet-foundry.git?tag=v0.14.0#e8cbecee4e31ed428c76d5173eaa90c8df796fe3"
version = "0.26.0"
source = "git+https://github.com/foundry-rs/starknet-foundry.git?tag=v0.26.0#50eb589db65e113efe4f09241feb59b574228c7e"
2 changes: 1 addition & 1 deletion marketplace/Scarb.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ edition = "2023_01"

[dependencies]
starknet = "2.6.3"
snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry.git", tag = "v0.14.0" }
snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry.git", tag = "v0.26.0" }
openzeppelin = { git = "https://github.com/openzeppelin/cairo-contracts", tag = "v0.10.0" }
alexandria_storage = { git = "https://github.com/keep-starknet-strange/alexandria.git", tag = "cairo-v2.3.0-rc0" }

Expand Down
34 changes: 34 additions & 0 deletions marketplace/Sell_any_item_at_fixed_price.md
mubarak23 marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
## Strategy for selling any item at a fix price

The function implements a Cairo contract named 'StrategySaleAnyItemAtFixedPrice'. This contract manages the sale of items (identified by token_id) at a fixed price, with specific features such as setting prices, handling bids, and upgrading the contract.
Key functionalities:

Initialization (constructor):
Initializes the contract with the owner's address and a protocol fee.

Updating Protocol Fee (update_protocol_fee):
Allows the owner to update the protocol fee, which is the fee applied to transactions.

Getting Protocol Fee (protocol_fee):
Retrieves the current protocol fee.

Setting an Item for Sale (set_item_sale):
Allows the owner of a specific token_id to list it for sale. It checks if the caller is the token owner and emits an event once the item is listed.

Setting Price for an Item (set_price_for_item):
Allows a buyer to set a price for a listed item. It verifies that the buyer is not the token owner, checks if the bid price is different from any existing bids by the buyer, and then records the bid, emitting an event for the action.

Executing Buyer Bids (can_execute_buyer_bid):
Checks if a buyer's bid can be executed. It verifies that the token has been listed by the seller and compares the buyer's bid price against the existing bid to determine if the bid is executable.

Contract Upgrading (upgrade):
Allows the contract owner to upgrade the contract implementation using a new ClassHash, ensuring only the owner can perform this action.

Events:

ItemForSaleAdded: Emitted when a new item is listed for sale.
SetPriceForItemByBuyer: Emitted when a buyer sets a price for a listed item.

This contract integrates with StarkNet's components for ownership (OwnableComponent) and upgradability (UpgradeableComponent), ensuring that only authorized users can perform sensitive operations like updating the protocol fee and upgrading the contract.

PR: https://github.com/Flex-NFT-Marketplace/Flex-Marketplace-Contract/pull/98
Original file line number Diff line number Diff line change
@@ -1 +1,144 @@
use starknet::ContractAddress;
use starknet::class_hash::ClassHash;
use marketplace::utils::order_types::{TakerOrder, MakerOrder, BuyerBidOrder};

#[starknet::interface]
trait IStrategySaleAnyItemAtFixedPrice<TState> {
// fn initializer(ref self: TState, fee: u128, owner: ContractAddress);
fn update_protocol_fee(ref self: TState, fee: u128);
fn protocol_fee(self: @TState) -> u128;
// fn set_item_sale(ref self: TState, token_id: u128);
fn set_buy_back_price_for_item(
ref self: TState, price: u128, collection_address: ContractAddress
);
fn can_execute_buyer_bid(self: @TState, buyer_bid: BuyerBidOrder) -> (bool, u128, u128);
fn upgrade(ref self: TState, impl_hash: ClassHash);
}

#[feature("deprecated_legacy_map")]
#[starknet::contract]
mod StrategySaleAnyItemAtFixedPrice {
use starknet::{ContractAddress, contract_address_const, get_caller_address};
use starknet::class_hash::ClassHash;
use starknet::get_block_timestamp;

use openzeppelin::access::ownable::OwnableComponent;
use openzeppelin::upgrades::{
{UpgradeableComponent, interface::IUpgradeable},
upgradeable::UpgradeableComponent::InternalTrait as UpgradeableInternalTrait
};
component!(path: OwnableComponent, storage: ownable, event: OwnableEvent);
component!(path: UpgradeableComponent, storage: upgradable, event: UpgradeableEvent);

#[abi(embed_v0)]
impl OwnableImpl = OwnableComponent::OwnableImpl<ContractState>;

impl OwnableInternalImpl = OwnableComponent::InternalImpl<ContractState>;

use marketplace::utils::order_types::{TakerOrder, MakerOrder, BuyerBidOrder};

#[derive(Debug, Drop, Copy, Serde, starknet::Store)]
pub struct BuyBack {
pub price: u128,
pub collection_address: ContractAddress
}

#[storage]
struct Storage {
protocol_fee: u128,
item_for_sale: LegacyMap::<
u128, ContractAddress
>, // token_id: u128, seller_address:ContractAddress
buyer_bids: LegacyMap<
ContractAddress, BuyBack
>, // LegacyMap<buyer_addesss, (collectionId, price)> LegacyMap<u128, u128>
mubarak23 marked this conversation as resolved.
Show resolved Hide resolved
#[substorage(v0)]
ownable: OwnableComponent::Storage,
#[substorage(v0)]
upgradable: UpgradeableComponent::Storage,
}

#[event]
#[derive(Drop, starknet::Event)]
enum Event {
#[flat]
OwnableEvent: OwnableComponent::Event,
#[flat]
UpgradeableEvent: UpgradeableComponent::Event,
SetBuyBackPriceForItem: SetBuyBackPriceForItem
}


#[derive(Drop, starknet::Event)]
pub struct SetBuyBackPriceForItem {
#[key]
pub buyer_address: ContractAddress,
pub price: u128,
pub collection_address: ContractAddress
}


#[constructor]
fn constructor(ref self: ContractState, owner: ContractAddress, fee: u128) {
assert(!owner.is_zero(), 'Owner cannot be a zero addr');
self.ownable.initializer(owner);
self.protocol_fee.write(fee);
}

#[abi(embed_v0)]
impl StrategySaleAnyItemAtFixedPriceImpl of super::IStrategySaleAnyItemAtFixedPrice<
ContractState
> {
fn update_protocol_fee(ref self: ContractState, fee: u128) {
self.ownable.assert_only_owner();
self.protocol_fee.write(fee);
}
fn protocol_fee(self: @ContractState) -> u128 {
self.protocol_fee.read()
}

fn set_buy_back_price_for_item(
ref self: ContractState, price: u128, collection_address: ContractAddress
) {
let owner = get_caller_address();

let existing_buyer_bids = self.buyer_bids.read(owner);

let buyer_token_price = existing_buyer_bids.price;
assert(buyer_token_price != price, 'Buy Back Price Set Already');

let new_buy_back_price = BuyBack {
price: price, collection_address: collection_address
};
self.buyer_bids.write(owner, new_buy_back_price);
// emit an event
self
.emit(
SetBuyBackPriceForItem {
buyer_address: owner, price: price, collection_address: collection_address
}
);
}

fn can_execute_buyer_bid(
self: @ContractState, buyer_bid: BuyerBidOrder
) -> (bool, u128, u128) {
let seller_item_listed_address = self.item_for_sale.read(buyer_bid.token_id);
let seller_address = get_caller_address();
// check if seller has listed the token to be sold at any price
assert(seller_address == seller_item_listed_address, 'Not avaialable for sale');

// get the buyer token from the bid
let buyer_bids = self.buyer_bids.read(buyer_bid.buyer_adddress);
let buyer_token_price = buyer_bids.price;
if (buyer_token_price < 0) {
return (false, buyer_bid.token_id, buyer_bid.price);
}
return (true, buyer_bid.token_id, buyer_bid.price);
}
fn upgrade(ref self: ContractState, impl_hash: ClassHash) {
self.ownable.assert_only_owner();
self.upgradable._upgrade(impl_hash);
}
}
}
7 changes: 7 additions & 0 deletions marketplace/src/utils/order_types.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,10 @@ struct TakerOrder {
min_percentage_to_ask: u128, // slippage protection (9000 = 90% of the final price must return to ask)
params: felt252,
}

#[derive(Copy, Drop, Serde, Default)]
struct BuyerBidOrder {
token_id: u128,
buyer_adddress: ContractAddress,
price: u128
}