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

Add CloseMarket function to redeem rent from permissioned markets #191

Open
wants to merge 26 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
bbf78b7
first compiling attempt at adding close markets
Henry-E Nov 3, 2021
9be141f
create test that inits a serum market, test passing
Henry-E Dec 1, 2021
e20b46f
Merge branch 'master' of github.com:project-serum/serum-dex into Clos…
Henry-E Dec 1, 2021
693e8ff
add test that sol is retrieved + get request queue sol back as well
Henry-E Dec 2, 2021
941c1f8
add readme
Henry-E Dec 2, 2021
2acfbfc
added queuesAreEmpty checks
Henry-E Dec 2, 2021
cf1f18f
Merge branch 'master' of github.com:project-serum/serum-dex into Clos…
Henry-E Dec 14, 2021
9bc76a8
disable market; add extra wrapped program function for tests
Henry-E Dec 14, 2021
185a322
some code
tomland123 Dec 15, 2021
f675805
some code
tomland123 Dec 15, 2021
6a2b052
more clear errors
Henry-E Dec 15, 2021
660dc0a
Merge branch 'CloseMarkets' of github.com:Henry-E/serum-dex into Clos…
Henry-E Dec 15, 2021
f75cb09
update gitignore
Henry-E Dec 15, 2021
a0d62f2
delete package lock
Henry-E Dec 15, 2021
42a4f00
Finished tests
tomland123 Dec 15, 2021
ad57550
Merge branch 'CloseMarkets' of https://github.com/Henry-E/serum-dex i…
tomland123 Dec 15, 2021
b1783eb
Finished tests
tomland123 Dec 15, 2021
2ecdd2b
Finished tests
tomland123 Dec 15, 2021
e328c0a
Finished tests
tomland123 Dec 15, 2021
b88d783
Finished tests
tomland123 Dec 15, 2021
2876856
Delete package.json
Henry-E Dec 15, 2021
0eaa838
Delete validator-1639598743617.log
Henry-E Dec 28, 2021
787fd82
remove test ledger + run cargo fmt
Henry-E Jan 7, 2022
fab0783
add close market test to travis CI
Henry-E Jan 7, 2022
6a3bab2
make sure tests work
Henry-E Jan 7, 2022
97f9596
Merge branch 'project-serum:master' into CloseMarkets
Henry-E Jan 10, 2022
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
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,8 @@ target/
*.swo
node_modules
yarn.lock
.anchor
.anchor
.DS_Store
**/*.rs.bk
close-markets/Cargo.lock
close-markets/package-lock.json
14 changes: 7 additions & 7 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions dex/src/critbit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -252,13 +252,13 @@ const_assert_eq!(_NODE_ALIGN, align_of::<AnyNode>());

#[derive(Copy, Clone)]
#[repr(packed)]
struct SlabHeader {
pub struct SlabHeader {
bump_index: u64,
free_list_len: u64,
free_list_head: u32,

root_node: u32,
leaf_count: u64,
pub leaf_count: u64,
}
unsafe impl Zeroable for SlabHeader {}
unsafe impl Pod for SlabHeader {}
Expand Down Expand Up @@ -347,7 +347,7 @@ impl Slab {
(header, nodes)
}

fn header(&self) -> &SlabHeader {
pub fn header(&self) -> &SlabHeader {
self.parts().0
}

Expand Down
5 changes: 5 additions & 0 deletions dex/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,11 @@ pub enum DexErrorCode {
WouldSelfTrade,
InvalidOpenOrdersAuthority,

BidQueueNotEmpty,
AskQueueNotEmpty,
RequestQueueNotEmpty,
EventQueueNotEmpty,

Unknown = 1000,

// This contains the line number in the lower 16 bits,
Expand Down
38 changes: 38 additions & 0 deletions dex/src/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,16 @@ pub enum MarketInstruction {
/// accounts.len() - 2 `[writable]` event queue
/// accounts.len() - 1 `[signer]` crank authority
ConsumeEventsPermissioned(u16),
/// Closes a market and retrieves the rent from the bids, asks, event and request queues
///
/// 0. `[writable]` market
/// 1. `[writable]` request queue
/// 2. `[writable]` event queue
/// 3. `[writable]` bids
/// 4. `[writable]` asks
/// 5. `[signer]` prune authority
/// 6. `[writable]` the destination account to send the rent exemption SOL to.
CloseMarket,
}

impl MarketInstruction {
Expand Down Expand Up @@ -568,6 +578,7 @@ impl MarketInstruction {
let limit = array_ref![data, 0, 2];
MarketInstruction::ConsumeEventsPermissioned(u16::from_le_bytes(*limit))
}
(18, 0) => MarketInstruction::CloseMarket,
_ => return None,
})
}
Expand Down Expand Up @@ -1003,6 +1014,33 @@ pub fn prune(
})
}

pub fn close_market(
program_id: &Pubkey,
market: &Pubkey,
request_queue: &Pubkey,
event_queue: &Pubkey,
bids: &Pubkey,
asks: &Pubkey,
prune_authority: &Pubkey,
destination: &Pubkey,
) -> Result<Instruction, DexError> {
let data = MarketInstruction::CloseMarket.pack();
let accounts: Vec<AccountMeta> = vec![
AccountMeta::new(*market, false),
AccountMeta::new(*request_queue, false),
AccountMeta::new(*event_queue, false),
AccountMeta::new(*bids, false),
AccountMeta::new(*asks, false),
AccountMeta::new_readonly(*prune_authority, true),
AccountMeta::new(*destination, false),
];
Ok(Instruction {
program_id: *program_id,
data,
accounts,
})
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
104 changes: 103 additions & 1 deletion dex/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -752,7 +752,7 @@ pub trait QueueHeader: Pod {
}

pub struct Queue<'a, H: QueueHeader> {
header: RefMut<'a, H>,
pub header: RefMut<'a, H>,
buf: RefMut<'a, [H::Item]>,
}

Expand Down Expand Up @@ -2418,6 +2418,72 @@ pub(crate) mod account_parser {
f(args)
}
}

pub struct CloseMarketArgs<'a, 'b: 'a> {
pub market: &'a mut MarketState,
pub request_q_acc: &'a AccountInfo<'b>,
pub event_q_acc: &'a AccountInfo<'b>,
pub bids_acc: &'a AccountInfo<'b>,
pub asks_acc: &'a AccountInfo<'b>,
pub dest_acc: &'a AccountInfo<'b>,
}

impl<'a, 'b: 'a> CloseMarketArgs<'a, 'b> {
pub fn with_parsed_args<T>(
program_id: &'a Pubkey,
accounts: &'a [AccountInfo<'b>],
f: impl FnOnce(CloseMarketArgs) -> DexResult<T>,
) -> DexResult<T> {
// Parse accounts.
check_assert_eq!(accounts.len(), 7)?;
#[rustfmt::skip]
let &[
ref market_acc,
ref request_q_acc,
ref event_q_acc,
ref bids_acc,
ref asks_acc,
ref prune_auth_acc,
ref dest_acc,
] = array_ref![accounts, 0, 7];

// Validate prune authority.
let _prune_authority = SignerAccount::new(prune_auth_acc)?;
let mut market = Market::load(market_acc, program_id, false)?;
check_assert!(market.prune_authority() == Some(prune_auth_acc.key))?;

// Check that request q, event q, bids and asks are completely cleared and empty
let bids = market.load_bids_mut(bids_acc).or(check_unreachable!())?;
let asks = market.load_asks_mut(asks_acc).or(check_unreachable!())?;
let req_q = market.load_request_queue_mut(request_q_acc)?;
let event_q = market.load_event_queue_mut(event_q_acc)?;

let bids_header = bids.header();
if bids_header.leaf_count != 0 {
return Err(DexErrorCode::BidQueueNotEmpty.into());
}
let asks_header = asks.header();
if asks_header.leaf_count != 0 {
return Err(DexErrorCode::AskQueueNotEmpty.into());
}
if req_q.header.count != 0 {
return Err(DexErrorCode::RequestQueueNotEmpty.into());
}
if event_q.header.count != 0 {
return Err(DexErrorCode::EventQueueNotEmpty.into());
}

// Invoke Processor
f(CloseMarketArgs {
market: market.deref_mut(),
request_q_acc,
event_q_acc,
bids_acc,
asks_acc,
dest_acc,
})
}
}
}

#[inline]
Expand Down Expand Up @@ -2541,6 +2607,11 @@ impl State {
limit,
Self::process_prune,
)?,
MarketInstruction::CloseMarket => account_parser::CloseMarketArgs::with_parsed_args(
program_id,
accounts,
Self::process_close_market,
)?,
};
Ok(())
}
Expand All @@ -2550,6 +2621,37 @@ impl State {
unimplemented!()
}

fn process_close_market(args: account_parser::CloseMarketArgs) -> DexResult {
let account_parser::CloseMarketArgs {
market,
request_q_acc,
event_q_acc,
bids_acc,
asks_acc,
dest_acc,
} = args;

// Transfer all lamports to the desintation.
let dest_starting_lamports = dest_acc.lamports();
**dest_acc.lamports.borrow_mut() = dest_starting_lamports
.checked_add(request_q_acc.lamports())
.unwrap()
.checked_add(event_q_acc.lamports())
.unwrap()
.checked_add(bids_acc.lamports())
.unwrap()
.checked_add(asks_acc.lamports())
.unwrap();
**request_q_acc.lamports.borrow_mut() = 0;
**event_q_acc.lamports.borrow_mut() = 0;
**bids_acc.lamports.borrow_mut() = 0;
**asks_acc.lamports.borrow_mut() = 0;

market.account_flags = market.account_flags | (AccountFlag::Disabled as u64);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should use Closed instead of Disabled.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Serum already has the functionality for Disabled built in from the ability to manually shutdown. I didn't find anything similar for Closed

Copy link
Author

@Henry-E Henry-E Jan 5, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://github.com/project-serum/serum-dex/blob/master/dex/src/state.rs#L1884 - Here's where new order already checks for whether a market is disabled. Is this functionality different from closing? It's possible I haven't fully understood the full extent of what the disable flag affects


Ok(())
}

fn process_prune(args: account_parser::PruneArgs) -> DexResult {
let account_parser::PruneArgs {
mut order_book_state,
Expand Down
19 changes: 19 additions & 0 deletions dex/tests/close-markets/Anchor.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[programs.localnet]
close_markets = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"

[test]
startup_wait = 20000

[registry]
url = "https://anchor.projectserum.com"

[provider]
cluster = "localnet"
wallet = "~/.config/solana/id.json"

[[test.genesis]]
address = "9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin"
program = "../../target/deploy/serum_dex.so"

[scripts]
test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts"
4 changes: 4 additions & 0 deletions dex/tests/close-markets/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[workspace]
members = [
"programs/*"
]
12 changes: 12 additions & 0 deletions dex/tests/close-markets/migrations/deploy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Migrations are an early feature. Currently, they're nothing more than this
// single deploy script that's invoked from the CLI, injecting a provider
// configured from the workspace's Anchor.toml.

const anchor = require("@project-serum/anchor");

module.exports = async function (provider) {
// Configure client to use the provider.
anchor.setProvider(provider);

// Add your deploy script here.
}
21 changes: 21 additions & 0 deletions dex/tests/close-markets/programs/close-markets/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
[package]
name = "close-markets"
version = "0.1.0"
description = "Created with Anchor"
edition = "2018"

[lib]
crate-type = ["cdylib", "lib"]
name = "close_markets"

[features]
no-entrypoint = []
no-idl = []
cpi = ["no-entrypoint"]
default = []

[dependencies]
anchor-lang = "0.19.0"
anchor-spl = {version = "0.19.0", features = ["dex"]}
serum_dex = { path = "../../../..", features = ["no-entrypoint"]}
solana-program = "1.8.0"
2 changes: 2 additions & 0 deletions dex/tests/close-markets/programs/close-markets/Xargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[target.bpfel-unknown-unknown.dependencies.std]
features = []
Loading