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

Refactor events/logic for indexing support #321

Merged
merged 58 commits into from
Jul 19, 2023
Merged

Refactor events/logic for indexing support #321

merged 58 commits into from
Jul 19, 2023

Conversation

alnoki
Copy link
Member

@alnoki alnoki commented Jul 3, 2023

No description provided.

@vercel
Copy link

vercel bot commented Jul 3, 2023

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
econia ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jul 18, 2023 11:30pm

@alnoki
Copy link
Member Author

alnoki commented Jul 3, 2023

The most recent commit adds on top of #315 draft structures to address recent discussions about user- and market-level indexing.

@alnoki alnoki marked this pull request as draft July 3, 2023 14:29
@vercel vercel bot temporarily deployed to Preview July 3, 2023 14:30 Inactive
src/move/econia/sources/market.move Outdated Show resolved Hide resolved
src/move/econia/sources/market.move Outdated Show resolved Hide resolved
src/move/econia/sources/market.move Outdated Show resolved Hide resolved
@vercel vercel bot temporarily deployed to Preview July 5, 2023 03:48 Inactive
@alnoki
Copy link
Member Author

alnoki commented Jul 5, 2023

The most recent commit provides the following design, which compiles but which hasn't been tested (nor have docs been updated accordingly):

MarketEvents under Econia resource account, with lookup via MarketAccountInfo:

struct MarketAccountInfo has copy, drop, store {
market_id: u64,
user: address,
custodian_id: u64
}
/// An event handle can be looked up via a view functions, then
/// passed into the REST API to query events emitted to the handle.
///
/// Events are emitted to handle for the market, and to a handle for
/// affected user/custodian ID tuple (affected signer in the case of
/// a swap, which is is not affiliated with a market account).
struct MarketEventHandles has key {
/// match() (events deferred to calling function)
match_events_by_market:
Table<u64, EventHandle<MatchEvent>>,
match_events_by_market_account:
Table<MarketAccountInfo, EventHandle<MatchEvent>>,

MatchEvent design:

struct MatchEvent has copy, drop, store {
market_id: u64,
size: u64,
price: u64,
maker_side: bool,
maker: address,
maker_custodian_id: u64,
maker_order_id: u128,
taker: address,
taker_custodian_id: u64,
taker_order_id: u128,
}

MatchEvent queue behavior inside match():

vector::push_back(event_queue_ref_mut, MatchEvent{
market_id,
size: fill_size,
price,
maker_side: side,
maker,
maker_custodian_id: maker_custodian_id,
maker_order_id: market_order_id,
taker,
taker_custodian_id: custodian_id,
taker_order_id:
((order_book_ref_mut.counter as u128) << SHIFT_COUNTER)
});

MatchEvent emission inside place_limit_order() after a common order ID has been assigned for both taker and maker sides of order:

let order_id_as_maker = (NIL as u128); // Assume no post.
// If size big enough to post and not immediate-or-cancel:
if ((size >= order_book_ref_mut.min_size) &&
(restriction != IMMEDIATE_OR_CANCEL)) {
// Get next order access key for user-side order placement.
let order_access_key = user::get_next_order_access_key_internal(
user_address, market_id, custodian_id, side);
// Get orders AVL queue for maker side.
let orders_ref_mut = if (side == ASK)
&mut order_book_ref_mut.asks else &mut order_book_ref_mut.bids;
// Declare order to insert to book.
let order = Order{size, price, user: user_address, custodian_id,
order_access_key};
// Get new AVL queue access key, evictee access key, and evictee
// value by attempting to insert for given critical height.
let (avlq_access_key, evictee_access_key, evictee_value) =
avl_queue::insert_check_eviction(
orders_ref_mut, price, order, critical_height);
// Assert that order could be inserted to AVL queue.
assert!(avlq_access_key != NIL, E_PRICE_TIME_PRIORITY_TOO_LOW);
// Get market order ID from AVL queue access key, counter.
let market_order_id = (avlq_access_key as u128) |
((order_book_ref_mut.counter as u128) << SHIFT_COUNTER);
user::place_order_internal( // Place order user-side.
user_address, market_id, custodian_id, side, size, price,
market_order_id, order_access_key);
// Emit a maker place event.
event::emit_event(&mut order_book_ref_mut.maker_events, MakerEvent{
market_id, side, market_order_id, user: user_address,
custodian_id, type: PLACE, size, price});
if (evictee_access_key == NIL) { // If no eviction required:
// Destroy empty evictee value option.
option::destroy_none(evictee_value);
} else { // If had to evict order at AVL queue tail:
// Unpack evicted order, storing fields for event.
let Order{size, price, user, custodian_id, order_access_key} =
option::destroy_some(evictee_value);
// Cancel order user-side, storing its market order ID.
let market_order_id_cancel = user::cancel_order_internal(
user, market_id, custodian_id, side, size, price,
order_access_key, (NIL as u128));
// Emit a maker evict event.
event::emit_event(&mut order_book_ref_mut.maker_events, MakerEvent{
market_id, side, market_order_id: market_order_id_cancel, user,
custodian_id, type: EVICT, size, price});
};
// Order ID as maker is from post to book.
order_id_as_maker = market_order_id
};
let market_order_id = emit_match_events(
market_event_handles_ref_mut, &mut match_event_queue,
order_id_as_maker);
// Return market order ID and taker trade amounts.
return (market_order_id, base_traded, quote_traded, fees)
}

emit_match_events() for emitting to market, maker, and taker handles for each MatchEvent:

inline fun emit_match_events(
handles_ref_mut: &mut MarketEventHandles,
match_events_queue_ref_mut: &mut vector<MatchEvent>,
new_taker_order_id: u128,
): u128 {
vector::for_each_ref(match_events_queue_ref_mut, |event_ref| {
let event: MatchEvent = *event_ref;
if (new_taker_order_id != (NIL as u128)) {
event.taker_order_id = new_taker_order_id
};
// Event for market.
let has_market_handle = table::contains(
&handles_ref_mut.match_events_by_market,
event.market_id);
if (!has_market_handle) {
table::add(
&mut handles_ref_mut.match_events_by_market,
event.market_id,
account::new_event_handle<MatchEvent>(
&resource_account::get_signer()));
};
let market_handle_ref_mut = table::borrow_mut(
&mut handles_ref_mut.match_events_by_market,
event.market_id);
event::emit_event(market_handle_ref_mut, copy event);
// Event for taker.
if (event.taker != NO_MARKET_ACCOUNT) {
let taker_market_account_info = MarketAccountInfo{
market_id: event.market_id,
user: event.taker,
custodian_id: event.taker_custodian_id
};
let has_taker_handle = table::contains(
&handles_ref_mut.match_events_by_market_account,
taker_market_account_info);
if (!has_taker_handle) {
table::add(
&mut handles_ref_mut.match_events_by_market_account,
taker_market_account_info,
account::new_event_handle<MatchEvent>(
&resource_account::get_signer()));
};
let taker_handle_ref_mut = table::borrow_mut(
&mut handles_ref_mut.match_events_by_market_account,
taker_market_account_info);
event::emit_event(taker_handle_ref_mut, copy event);
};
// Event for maker.
let maker_market_account_info = MarketAccountInfo{
market_id: event.market_id,
user: event.maker,
custodian_id: event.maker_custodian_id
};
let has_maker_handle = table::contains(
&handles_ref_mut.match_events_by_market_account,
maker_market_account_info);
if (!has_maker_handle) {
table::add(
&mut handles_ref_mut.match_events_by_market_account,
maker_market_account_info,
account::new_event_handle<MatchEvent>(
&resource_account::get_signer()));
};
let maker_handle_ref_mut = table::borrow_mut(
&mut handles_ref_mut.match_events_by_market_account,
maker_market_account_info);
event::emit_event(maker_handle_ref_mut, copy event);
});
if (vector::is_empty(match_events_queue_ref_mut)) {
(NIL as u128)
} else if (new_taker_order_id != (NIL as u128)) {
new_taker_order_id
} else {
vector::borrow(match_events_queue_ref_mut, 0).taker_order_id
}
}

src/move/econia/sources/market.move Outdated Show resolved Hide resolved
src/move/econia/sources/market.move Outdated Show resolved Hide resolved
src/move/econia/sources/market.move Outdated Show resolved Hide resolved
@bogberry
Copy link
Contributor

bogberry commented Jul 5, 2023

Also what's the motivation for changing the name from FillEvent to MatchEvent?

@vercel vercel bot temporarily deployed to Preview July 5, 2023 22:46 Inactive
@vercel vercel bot temporarily deployed to Preview July 5, 2023 23:36 Inactive
@vercel vercel bot temporarily deployed to Preview July 6, 2023 00:10 Inactive
@alnoki
Copy link
Member Author

alnoki commented Jul 6, 2023

Key implementations in the latest batch of revisions:

New event structs in user.move:

/// Table keys are market account IDs.
struct MarketEventHandles has key {
fill_events: Table<u128, EventHandle<FillEvent>>,
limit_order_events: Table<u128, EventHandle<LimitOrderEvent>>
}
struct FillEvent has copy, drop, store {
market_id: u64,
size: u64,
price: u64,
maker_side: bool,
maker: address,
maker_custodian_id: u64,
maker_order_id: u128,
taker: address,
taker_custodian_id: u64,
taker_order_id: u128
}
struct LimitOrderEvent has copy, drop, store {
market_id: u64,
user: address,
custodian_id: u64,
integrator: address,
side: bool,
size: u64,
price: u64,
restriction: u8,
self_match_behavior: u8,
base_traded_as_taker: u64,
quote_traded_as_taker: u64,
fees_as_taker: u64,
size_posted_as_maker: u64,
order_id: u128
}

Since market.move has user.move as a dependency, user.move offers the following public friend functions:

/// Emit fill event to specified handle, if handle exists.
public(friend) fun emit_fill_event_for_maker_and_taker(
event: FillEvent
) acquires MarketEventHandles {
emit_fill_event(event, true);
emit_fill_event(event, false);
}
public(friend) fun emit_limit_order_event(
event: LimitOrderEvent
) acquires MarketEventHandles {
let user_address = event.user;
if (exists<MarketEventHandles>(user_address)) {
let market_event_handles_ref_mut =
borrow_global_mut<MarketEventHandles>(user_address);
let limit_order_events_ref_mut =
&mut market_event_handles_ref_mut.limit_order_events;
let market_account_id =
get_market_account_id(event.market_id, event.custodian_id);
let has_handle = table::contains(
limit_order_events_ref_mut, market_account_id);
if (has_handle) {
let limit_order_event_handle_ref_mut = table::borrow_mut(
limit_order_events_ref_mut, market_account_id);
event::emit_event(limit_order_event_handle_ref_mut, event)
}
}
}
public(friend) fun get_fill_event(
market_id: u64,
size: u64,
price: u64,
maker_side: bool,
maker: address,
maker_custodian_id: u64,
maker_order_id: u128,
taker: address,
taker_custodian_id: u64,
taker_order_id: u128,
): FillEvent {
FillEvent{
market_id,
size,
price,
maker_side,
maker,
maker_custodian_id,
maker_order_id,
taker,
taker_custodian_id,
taker_order_id
}
}
public(friend) fun get_fill_event_taker_order_id(
fill_event_ref: &FillEvent,
): u128 {
fill_event_ref.taker_order_id
}
public(friend) fun set_fill_event_taker_order_id(
fill_event_ref_mut: &mut FillEvent,
taker_order_id: u128
) {
fill_event_ref_mut.taker_order_id = taker_order_id
}
public(friend) fun get_limit_order_event(
market_id: u64,
user: address,
custodian_id: u64,
integrator: address,
side: bool,
size: u64,
price: u64,
restriction: u8,
self_match_behavior: u8,
base_traded_as_taker: u64,
quote_traded_as_taker: u64,
fees_as_taker: u64,
size_posted_as_maker: u64,
order_id: u128
): LimitOrderEvent {
LimitOrderEvent {
market_id,
user,
custodian_id,
integrator,
side,
size,
price,
restriction,
self_match_behavior,
base_traded_as_taker,
quote_traded_as_taker,
fees_as_taker,
size_posted_as_maker,
order_id
}
}

Hence, at the end of market::place_limit_order():

let market_order_id = emit_fill_events(
market_id, &mut fill_event_queue, market_event_handles_ref_mut,
order_id_as_maker);
// Emit a limit order event, creating handle as needed.
let limit_order_event = user::get_limit_order_event(
market_id,
user_address,
custodian_id,
integrator,
side,
size,
price,
restriction,
self_match_behavior,
base_traded,
quote_traded,
fees,
size_posted,
market_order_id);
let has_limit_order_handle = table::contains(
&market_event_handles_ref_mut.limit_order_events, market_id);
if (!has_limit_order_handle) table::add(
&mut market_event_handles_ref_mut.limit_order_events,
market_id,
account::new_event_handle<LimitOrderEvent>(
&resource_account::get_signer()));
let limit_order_handle_ref_mut = table::borrow_mut(
&mut market_event_handles_ref_mut.limit_order_events,
market_id);
event::emit_event(limit_order_handle_ref_mut, limit_order_event);
user::emit_limit_order_event(limit_order_event);
// Return market order ID and taker trade amounts.
return (market_order_id, base_traded, quote_traded, fees)

Here, FillEvents are emitted inside of the MarketEventHandles struct under the Econia resource account, then for each user associated with a fill. After fills are emitted, a LimitOrderEvent is similarly emitted at the market- and user-level.

src/move/econia/sources/market.move Outdated Show resolved Hide resolved
src/move/econia/sources/user.move Outdated Show resolved Hide resolved
src/move/econia/sources/user.move Outdated Show resolved Hide resolved
@vercel vercel bot temporarily deployed to Preview July 6, 2023 22:31 Inactive
@vercel vercel bot temporarily deployed to Preview July 6, 2023 22:42 Inactive
@vercel vercel bot temporarily deployed to Preview July 18, 2023 18:19 Inactive
@vercel vercel bot temporarily deployed to Preview July 18, 2023 21:58 Inactive
@vercel vercel bot temporarily deployed to Preview July 18, 2023 22:09 Inactive
@vercel vercel bot temporarily deployed to Preview July 18, 2023 22:24 Inactive
@vercel vercel bot temporarily deployed to Preview July 18, 2023 22:33 Inactive
@vercel vercel bot temporarily deployed to Preview July 18, 2023 23:06 Inactive
@vercel vercel bot temporarily deployed to Preview July 18, 2023 23:09 Inactive
@vercel vercel bot temporarily deployed to Preview July 18, 2023 23:28 Inactive
@vercel vercel bot temporarily deployed to Preview July 18, 2023 23:30 Inactive
elliottdehn added a commit that referenced this pull request Jul 18, 2023
@alnoki
Copy link
Member Author

alnoki commented Jul 18, 2023

@alnoki alnoki marked this pull request as ready for review July 18, 2023 23:42
@alnoki alnoki requested a review from qdrs July 18, 2023 23:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants