Skip to content

Commit

Permalink
Contracts: ark_orderbook add whitelist broker
Browse files Browse the repository at this point in the history
  • Loading branch information
ybensacq committed Feb 19, 2024
1 parent 828ae5e commit e43a3ae
Show file tree
Hide file tree
Showing 14 changed files with 159 additions and 59 deletions.
2 changes: 1 addition & 1 deletion contracts/ark_orderbook/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@ To build:

# Testing

For testing, you must have [starknet forge](https://foundry-rs.github.io/starknet-foundry/getting-started/installation.html) installed (at least `0.6.0`). Then you can run:
For testing, you must have [starknet forge](https://foundry-rs.github.io/starknet-foundry/getting-started/installation.html) installed (at least `0.9.1`). Then you can run:

`snforge`
70 changes: 70 additions & 0 deletions contracts/ark_orderbook/src/broker/database.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
//! Orders database.
//!
//! The order database uses the `storage_read` and `storage_write`
//! syscalls directly to optimize how data are stored.
//!
//! The status is always stored independently of the seriliazed
//! broker. This allows a quick and cheap storage/retrieval of the status
//! without having to write/read the whole order.
//!
//! The only assumption for now is that,
//! a single order serialized buffer must not exceed
//! 256 felts.
//!
//! The storage layout is the following:
//!
//! 1. Compute the storage base address with [ORDER_DB_BASE_KEY, order_hash]
//! 2. At base address + offset 0 => The order status.
//! 3. At base address + offset 1 => The length of the serialized order.
//! 4. At base addresss + offset 2 => First felt of the serialized order.
use starknet::SyscallResultTrait;


/// Must remain equal to 0 for now.
const ADDRESS_DOMAIN: u32 = 0;
/// A constant value used in the base key hash.
const BROKER_DB_BASE_KEY: felt252 = 'broker whitelist';



/// Reads whitelist status of broker.
///
/// # Arguments
///
/// * `broker_id` - ID of the broker.
fn broker_whitelist_read(broker_id: felt252) -> bool {
let key = array![BROKER_DB_BASE_KEY, broker_id];

let base = starknet::storage_base_address_from_felt252(
poseidon::poseidon_hash_span(key.span())
);

// First offset is the status.
let whitelisted: felt252 = starknet::storage_read_syscall(
ADDRESS_DOMAIN, starknet::storage_address_from_base(base)
).unwrap_syscall();

whitelisted == 1
}

/// Writes only the whitelisted brokers.
/// It can be whitelisted or blacklisted.
///
/// # Arguments
///
/// * `broker_id` - ID of the broker.
/// * `status` - 1 if whitelisted, 0 if not.
fn broker_whitelist_write(broker_id: felt252, status: felt252) -> bool {

let key = array![BROKER_DB_BASE_KEY, broker_id];

let base = starknet::storage_base_address_from_felt252(
poseidon::poseidon_hash_span(key.span())
);

starknet::storage_write_syscall(
ADDRESS_DOMAIN, starknet::storage_address_from_base(base), status
);

true
}
4 changes: 4 additions & 0 deletions contracts/ark_orderbook/src/lib.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,9 @@ mod order {
mod order_v1;
}

mod broker {
mod database;
}

mod orderbook;
mod orderbook_event_mock;
16 changes: 12 additions & 4 deletions contracts/ark_orderbook/src/order/order_v1.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,22 @@ use core::array::ArrayTrait;
use core::traits::Into;
use core::traits::TryInto;
use core::option::OptionTrait;
use ark_orderbook::orderbook::is_broker_whitelisted;

use ark_orderbook::broker::database::{
broker_whitelist_read
};

//! Order v1 supported by the Orderbook.
//!
use starknet::ContractAddress;
use starknet::contract_address_to_felt252;
use ark_common::protocol::order_types::{OrderTrait, OrderValidationError, OrderType, RouteType};
use ark_common::protocol::order_types::FulfillInfo;
use poseidon::poseidon_hash_span;
use starknet::SyscallResultTrait;

/// Must remain equal to 0 for now.
const ADDRESS_DOMAIN: u32 = 0;
const ORDER_VERSION_V1: felt252 = 'v1';
// Auction -> end_amount (reserve price) > start_amount (starting price).
// Auction -> ERC721_ERC20.
Expand Down Expand Up @@ -46,8 +53,6 @@ struct OrderV1 {
end_date: u64,
// Broker public identifier.
broker_id: felt252,
// Broker type (0: creator, 1: fullfiller).
broker_type: felt252,
// Additional data, limited to ??? felts.
additional_data: Span<felt252>,
}
Expand Down Expand Up @@ -106,7 +111,10 @@ impl OrderTraitOrderV1 of OrderTrait<OrderV1> {
return Result::Err(OrderValidationError::InvalidContent);
}

if (is_broker_whitelisted(*self.broker_id, *self.broker_type) == 0) {
// check if the broker is whitelisted.
let whitelisted = broker_whitelist_read(*self.broker_id);

if whitelisted == false {
return Result::Err(OrderValidationError::InvalidBroker);
}

Expand Down
50 changes: 12 additions & 38 deletions contracts/ark_orderbook/src/orderbook.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,14 @@ trait Orderbook<T> {
/// # Arguments
///
/// * `broker_id` - ID of the broker.
/// * `broker_type` - Type of the broker (0: CREATOR, 1: FULFILLER).
fn whitelist_broker(ref self: T, broker_id: felt252, broker_type: felt252);
fn whitelist_broker(ref self: T, broker_id: felt252);

/// Remove a broker from the whitelist.
///
/// # Arguments
///
/// * `broker_id` - ID of the broker.
/// * `broker_type` - Type of the broker (0: CREATOR, 1: FULFILLER).
fn unwhitelist_broker(ref self: T, broker_id: felt252, broker_type: felt252);
fn unwhitelist_broker(ref self: T, broker_id: felt252);

/// Submits and places an order to the orderbook if the order is valid.
///
Expand Down Expand Up @@ -127,6 +125,7 @@ mod orderbook_errors {
const ORDER_OPEN: felt252 = 'OB: order is not open';
const USE_FULFILL_AUCTION: felt252 = 'OB: must use fulfill auction';
const OFFER_NOT_STARTED: felt252 = 'OB: offer is not started';
const INVALID_BROKER: felt252 = 'OB: broker is not whitelisted';
}

/// StarkNet smart contract module for an order book.
Expand All @@ -153,6 +152,10 @@ mod orderbook {
order_read, order_status_read, order_write, order_status_write, order_type_read
};

use ark_orderbook::broker::database::{
broker_whitelist_write
};

const EXTENSION_TIME_IN_SECONDS: u64 = 600;
const AUCTION_ACCEPTING_TIME_SECS: u64 = 172800;
/// Storage struct for the Orderbook contract.
Expand All @@ -168,12 +171,6 @@ mod orderbook {
/// Mapping of broker addresses to their whitelisted status.
/// Represented as felt252, set to 1 if the broker is registered.
brokers: LegacyMap<felt252, felt252>,
/// Mapping of creator_brokers addresses to their whitelisted status.
/// Represented as felt252, set to 1 if the broker is registered.
creator_brokers: LegacyMap<felt252, felt252>,
/// Mapping of fulfiller_brokers addresses to their whitelisted status.
/// Represented as felt252, set to 1 if the broker is registered.
fulfiller_brokers: LegacyMap<felt252, felt252>,
/// Mapping of token_hash to order_hash.
token_listings: LegacyMap<felt252, felt252>,
/// Mapping of token_hash to auction details (order_hash and end_date, auction_offer_count).
Expand Down Expand Up @@ -280,17 +277,6 @@ mod orderbook {
self.emit(OrderExecuted { order_hash: info.order_hash, order_status: order_status });
}

/// Check if a broker is whitelisted
fn is_broker_whitelisted(ref self: ContractState, broker_id: felt252, broker_type: felt252) -> felt252 {
let mut is_whitelisted = 0;
if (broker_type == 0) {
is_whitelisted = self.creator_brokers.read(broker_id);
} else if (broker_type == 1) {
is_whitelisted = self.fulfiller_brokers.read(broker_id);
}
is_whitelisted
}

// *************************************************************************
// EXTERNAL FUNCTIONS
// *************************************************************************
Expand Down Expand Up @@ -375,27 +361,15 @@ mod orderbook {
}

/// Whitelists a broker.
fn whitelist_broker(ref self: ContractState, broker_id: felt252, broker_type: felt252) {
fn whitelist_broker(ref self: ContractState, broker_id: felt252) {
assert(starknet::get_caller_address() == self.admin.read(), 'Unauthorized update');

self.brokers.write(broker_id, 1);

if (broker_type == 0) {
self.creator_brokers.write(broker_id, 1);
} else if (broker_type == 1) {
self.fulfiller_brokers.write(broker_id, 1);
}
broker_whitelist_write(broker_id, 1);
}

fn unwhitelist_broker(ref self: ContractState, broker_id: felt252, broker_type: felt252) {
/// Remove a broker from whitelist.
fn unwhitelist_broker(ref self: ContractState, broker_id: felt252) {
assert(starknet::get_caller_address() == self.admin.read(), 'Unauthorized update');
self.brokers.write(broker_id, 0);

if (broker_type == 0) {
self.creator_brokers.write(broker_id, 0);
} else if (broker_type == 1) {
self.fulfiller_brokers.write(broker_id, 0);
}
broker_whitelist_write(broker_id, 0);
}

/// Submits and places an order to the orderbook if the order is valid.
Expand Down
3 changes: 0 additions & 3 deletions contracts/ark_orderbook/src/orderbook_event_mock.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,6 @@ mod orderbook_event_mock {
start_date: 5,
end_date: 6,
broker_id: 'broker',
broker_type: 0,
additional_data: array![].span(),
}
}
Expand Down Expand Up @@ -140,7 +139,6 @@ mod orderbook_event_mock {
start_date: 5,
end_date: 6,
broker_id: 'broker',
broker_type: 1,
additional_data: array![].span(),
}
}
Expand Down Expand Up @@ -171,7 +169,6 @@ mod orderbook_event_mock {
start_date: 2,
end_date: 0,
broker_id: 'broker',
broker_type: 0,
additional_data: array![].span(),
}
}
Expand Down
13 changes: 12 additions & 1 deletion contracts/ark_orderbook/tests/common/setup.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,15 @@ use core::traits::Into;
use ark_common::protocol::order_types::{RouteType, FulfillInfo, OrderTrait, OrderType, OrderStatus};
use ark_common::crypto::signer::{Signer, SignInfo};
use ark_orderbook::order::order_v1::OrderV1;
use ark_orderbook::orderbook::{OrderbookDispatcher, OrderbookDispatcherTrait};

use snforge_std::signature::{
StarkCurveKeyPair, StarkCurveKeyPairTrait, Signer as SNSigner, Verifier
};

use snforge_std::{start_prank, stop_prank};
use starknet::ContractAddress;

use super::super::common::signer::sign_mock;
/// Utility function to setup orders for test environment.
///
Expand Down Expand Up @@ -215,7 +220,7 @@ fn setup_listing_order(price: felt252) -> (OrderV1, felt252, felt252) {
/// Utility function to setup a listing order for test environment.
///
/// # Returns a tuple of the different orders data
///
///
/// * order_listing
/// * sign_info
/// * order_hash
Expand Down Expand Up @@ -366,3 +371,9 @@ fn get_offer_order() -> OrderV1 {
additional_data: data_span,
}
}

fn whitelist_creator_broker(contract_address: ContractAddress, broker_id: felt252, dispatcher: OrderbookDispatcher) {
start_prank(contract_address, 0x00E4769a4d2F7F69C70951A003eBA5c32707Cef3CdfB6B27cA63567f51cdd078.try_into().unwrap());
dispatcher.whitelist_broker(broker_id);
stop_prank(contract_address);
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use snforge_std::{
};

use super::super::common::setup::{
setup_auction_order, setup_listing, sign_mock, setup_orders, setup_offer
setup_auction_order, setup_listing, sign_mock, setup_orders, setup_offer, whitelist_creator_broker
};

#[test]
Expand All @@ -34,6 +34,7 @@ fn test_cancel_auction() {
let contract_address = contract.deploy(@contract_data).unwrap();

let dispatcher = OrderbookDispatcher { contract_address };
whitelist_creator_broker(contract_address, auction_listing_order.broker_id, dispatcher);
dispatcher.create_order(order: auction_listing_order, signer: signer);

let cancel_info = CancelInfo {
Expand Down Expand Up @@ -100,6 +101,7 @@ fn test_invalid_cancel_auction_order() {
let contract_address = contract.deploy(@contract_data).unwrap();

let dispatcher = OrderbookDispatcher { contract_address };
whitelist_creator_broker(contract_address, auction_listing_order.broker_id, dispatcher);
dispatcher.create_order(order: auction_listing_order, signer: signer);

let order_type = dispatcher.get_order_type(order_hash);
Expand Down Expand Up @@ -138,6 +140,7 @@ fn test_cancel_auction_during_the_extended_time() {
];
let contract_address = contract.deploy(@contract_data).unwrap();
let dispatcher = OrderbookDispatcher { contract_address };
whitelist_creator_broker(contract_address, auction_listing_order.broker_id, dispatcher);
dispatcher.create_order(order: auction_listing_order, signer: auction_listing_signer);
let order_type = dispatcher.get_order_type(order_hash);
assert(order_type == OrderType::Auction.into(), 'order is not auction');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use snforge_std::{
start_warp, declare, ContractClassTrait, spy_events, EventSpy, EventFetcher, EventAssertions,
Event, SpyOn, test_address, signature::{StarkCurveKeyPair, StarkCurveKeyPairTrait, Verifier}
};
use super::super::common::setup::{setup_auction_order, sign_mock, setup_orders, setup_offer};
use super::super::common::setup::{setup_auction_order, sign_mock, setup_orders, setup_offer, whitelist_creator_broker};

#[test]
fn test_create_valid_auction_offer() {
Expand All @@ -29,6 +29,7 @@ fn test_create_valid_auction_offer() {
let contract_address = contract.deploy(@contract_data).unwrap();

let dispatcher = OrderbookDispatcher { contract_address };
whitelist_creator_broker(contract_address, auction_listing_order.broker_id, dispatcher);
dispatcher.create_order(order: auction_listing_order, signer: signer);

let (auction_offer, signer, order_hash, token_hash) = setup_offer(
Expand All @@ -53,6 +54,7 @@ fn test_accept_auction_after_expiration() {
let contract_address = contract.deploy(@contract_data).unwrap();

let dispatcher = OrderbookDispatcher { contract_address };
whitelist_creator_broker(contract_address, auction_listing_order.broker_id, dispatcher);
dispatcher.create_order(order: auction_listing_order, signer: signer);

let (auction_offer, signer, auction_offer_order_hash, token_hash) = setup_offer(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use ark_orderbook::order::order_v1::OrderV1;
use ark_orderbook::orderbook::{OrderbookDispatcher, OrderbookDispatcherTrait};
use starknet::deploy_syscall;
use super::super::common::setup::{
setup_auction_order, setup_listing, sign_mock, setup_orders, setup_offer
setup_auction_order, setup_listing, sign_mock, setup_orders, setup_offer, whitelist_creator_broker
};
use snforge_std::{
start_warp, declare, ContractClassTrait, spy_events, EventSpy, EventFetcher, EventAssertions,
Expand All @@ -30,6 +30,7 @@ fn test_create_existing_order() {
];
let contract_address = contract.deploy(@contract_data).unwrap();
let dispatcher = OrderbookDispatcher { contract_address };
whitelist_creator_broker(contract_address, order_listing.broker_id, dispatcher);
dispatcher.create_order(order: order_listing, signer: signer);
dispatcher.create_order(order: order_listing, signer: signer);
}
Expand All @@ -48,6 +49,7 @@ fn test_create_listing_order() {
];
let contract_address = contract.deploy(@contract_data).unwrap();
let dispatcher = OrderbookDispatcher { contract_address };
whitelist_creator_broker(contract_address, order_listing.broker_id, dispatcher);
dispatcher.create_order(order: order_listing, signer: signer);
let order = dispatcher.get_order(_order_hash);
let order_status = dispatcher.get_order_status(_order_hash);
Expand Down Expand Up @@ -103,8 +105,8 @@ fn test_auction_order_with_extended_time_order() {
0x00E4769a4d2F7F69C70951A003eBA5c32707Cef3CdfB6B27cA63567f51cdd078, chain_id
];
let contract_address = contract.deploy(@contract_data).unwrap();

let dispatcher = OrderbookDispatcher { contract_address };
whitelist_creator_broker(contract_address, auction_listing_order.broker_id, dispatcher);
dispatcher.create_order(order: auction_listing_order, signer: auction_listing_signer);

let order_type = dispatcher.get_order_type(order_hash);
Expand Down
Loading

0 comments on commit e43a3ae

Please sign in to comment.