Skip to content

Commit

Permalink
Merge pull request #98 from mubarak23/ft_strategy_any_item
Browse files Browse the repository at this point in the history
functions selling any item at a fix price
  • Loading branch information
0xandee authored Sep 4, 2024
2 parents fedc625 + c186583 commit 6e8e9cc
Show file tree
Hide file tree
Showing 7 changed files with 189 additions and 3 deletions.
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
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 any item (set_buy_back_price_for_any_item):
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
}

0 comments on commit 6e8e9cc

Please sign in to comment.