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 7 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
33 changes: 33 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,33 @@
## Strategy for Selling Any Item at a Fixed Price

This contract, StrategySaleAnyItemAtFixedPrice, implements a strategy for selling items at a fixed price on StarkNet. The contract manages sales by allowing owners to list items (identified by token_id), set prices, handle buyer bids, and upgrade the contract.
Key Functionalities

Initialization (constructor):
Initializes the contract with the owner's address and a protocol fee. The owner's address must not be zero, and the protocol fee is set during the initialization process.

Updating Protocol Fee (update_protocol_fee):
Allows the contract owner to update the protocol fee, which is a fee applied to transactions executed within the marketplace. Only the contract owner has the authority to modify this fee.

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

Setting Buy-Back Price for an Item (set_buy_back_price_for_item):
mubarak23 marked this conversation as resolved.
Show resolved Hide resolved
Allows a buyer to set a buy-back price for any item from a specific collection.

Executing Buyer Bids (can_execute_buyer_bid):
Verifies whether a buyer's bid can be executed. The contract checks if the token was listed for sale by the seller and compares the bid price with the existing buy-back price to determine if the bid is valid and executable.

Contract Upgrading (upgrade):
Allows the contract owner to upgrade the contract's implementation using a new ClassHash. This operation can only be performed by the contract owner to ensure the contract's integrity.

Events

SetBuyBackPriceForItem:
Emitted when a buyer sets a buy-back price for an item. This event logs the buyer's address, the price set, and the collection address.

Integrations

This contract integrates with StarkNet's OwnableComponent for ownership management and UpgradeableComponent for upgradability. These integrations ensure that sensitive operations, such as updating protocol fees and upgrading the contract, are restricted to authorized users only.

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, (collectionAddress, price)> LegacyMap<u128, u128>
#[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
}