Skip to content

Commit

Permalink
solana: add security deposit params (#61)
Browse files Browse the repository at this point in the history
Co-authored-by: A5 Pickle <[email protected]>
  • Loading branch information
a5-pickle and a5-pickle authored Apr 3, 2024
1 parent d02e73d commit 0bc8834
Show file tree
Hide file tree
Showing 10 changed files with 500 additions and 139 deletions.
7 changes: 4 additions & 3 deletions solana/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,15 @@ cargo-test:
cargo test --workspace --all-targets --features $(NETWORK)

.PHONY: build
build: $(out_$(NETWORK))
$(out_$(NETWORK)): cargo-test
build:
ifdef out_$(NETWORK)
mkdir $(out_$(NETWORK)) # do not want to continue if the directory already exists
anchor build --arch sbf -- --features $(NETWORK)
mkdir -p $(out_$(NETWORK))
cp target/deploy/*.so $(out_$(NETWORK))/
endif

$(out_$(NETWORK)): cargo-test

.PHONY: test
test: node_modules
NETWORK=localnet $(MAKE) cargo-test
Expand Down
83 changes: 78 additions & 5 deletions solana/programs/matching-engine/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ pub enum MatchingEngineError {
OwnerOnly = 0x2,
OwnerOrAssistantOnly = 0x4,

U64Overflow = 0x10,

SameEndpoint = 0x20,
InvalidEndpoint = 0x22,

Expand All @@ -25,11 +27,18 @@ pub enum MatchingEngineError {
AssistantZeroPubkey = 0x100,
FeeRecipientZeroPubkey = 0x101,
ImmutableProgram = 0x102,
InvalidAuctionDuration = 0x104,
InvalidAuctionGracePeriod = 0x106,
UserPenaltyTooLarge = 0x108,
InitialPenaltyTooLarge = 0x10a,
MinOfferDeltaTooLarge = 0x10c,
ZeroDuration = 0x104,
ZeroGracePeriod = 0x106,
ZeroPenaltyPeriod = 0x107,
#[msg("Value exceeds 1000000")]
UserPenaltyRewardBpsTooLarge = 0x108,
#[msg("Value exceeds 1000000")]
InitialPenaltyBpsTooLarge = 0x10a,
#[msg("Value exceeds 1000000")]
MinOfferDeltaBpsTooLarge = 0x10c,
ZeroSecurityDepositBase = 0x10e,
#[msg("Value exceeds 1000000")]
SecurityDepositBpsTooLarge = 0x10f,

InvalidNewOwner = 0x202,
AlreadyOwner = 0x204,
Expand Down Expand Up @@ -60,3 +69,67 @@ pub enum MatchingEngineError {
AuctionHistoryNotFull = 0x502,
AuctionHistoryFull = 0x504,
}

#[cfg(test)]
mod test {
use crate::FEE_PRECISION_MAX;
use anchor_lang::prelude::*;

use super::*;

#[test]
fn test_user_penalty_rewards_bps_too_large() {
match error!(MatchingEngineError::UserPenaltyRewardBpsTooLarge) {
Error::AnchorError(error) => {
assert_eq!(error.error_code_number, 6000 + 0x108);
assert_eq!(
error.error_msg,
format!("Value exceeds {FEE_PRECISION_MAX}")
);
}
_ => panic!(),
}
}

#[test]
fn test_initial_penalty_bps_too_large() {
match error!(MatchingEngineError::InitialPenaltyBpsTooLarge) {
Error::AnchorError(error) => {
assert_eq!(error.error_code_number, 6000 + 0x10a);
assert_eq!(
error.error_msg,
format!("Value exceeds {FEE_PRECISION_MAX}")
);
}
_ => panic!(),
}
}

#[test]
fn test_min_offer_delta_bps_too_large() {
match error!(MatchingEngineError::MinOfferDeltaBpsTooLarge) {
Error::AnchorError(error) => {
assert_eq!(error.error_code_number, 6000 + 0x10c);
assert_eq!(
error.error_msg,
format!("Value exceeds {FEE_PRECISION_MAX}")
);
}
_ => panic!(),
}
}

#[test]
fn test_security_deposit_bps_too_large() {
match error!(MatchingEngineError::SecurityDepositBpsTooLarge) {
Error::AnchorError(error) => {
assert_eq!(error.error_code_number, 6000 + 0x10f);
assert_eq!(
error.error_msg,
format!("Value exceeds {FEE_PRECISION_MAX}")
);
}
_ => panic!(),
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,16 @@ pub fn place_initial_offer(ctx: Context<PlaceInitialOffer>, offer_price: u64) ->

// Parse the transfer amount from the VAA.
let amount_in = order.amount_in();
let security_deposit = order.max_fee();

// Saturating to u64::MAX is safe here. If the amount really ends up being this large, the
// checked addition below will catch it.
let security_deposit =
order
.max_fee()
.saturating_add(utils::auction::compute_notional_security_deposit(
&ctx.accounts.auction_config,
amount_in,
));

// Set up the Auction account for this auction.
let config = &ctx.accounts.auction_config;
Expand Down Expand Up @@ -172,6 +181,8 @@ pub fn place_initial_offer(ctx: Context<PlaceInitialOffer>, offer_price: u64) ->
&[ctx.bumps.transfer_authority],
]],
),
amount_in + security_deposit,
amount_in
.checked_add(security_deposit)
.ok_or(MatchingEngineError::U64Overflow)?,
)
}
10 changes: 9 additions & 1 deletion solana/programs/matching-engine/src/state/auction_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,21 @@ pub struct AuctionParameters {

// The minimum offer increment in percentage terms.
pub min_offer_delta_bps: u32,

/// The base security deposit, which will the the additional amount an auction participant must
/// deposit to participate in an auction.
pub security_deposit_base: u64,

/// Additional security deposit based on the notional of the order amount.
pub security_deposit_bps: u32,
}

#[account]
#[derive(Debug, InitSpace, Copy)]
pub struct AuctionConfig {
/// Monotonically increasing identifier for auction configs.
pub id: u32,

/// Auction parameters, which are validated by [crate::utils::auction::require_valid_parameters].
pub parameters: AuctionParameters,
}

Expand Down
45 changes: 34 additions & 11 deletions solana/programs/matching-engine/src/utils/auction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,26 +47,47 @@ pub fn compute_min_allowed_offer(params: &AuctionParameters, info: &AuctionInfo)
.saturating_sub(mul_bps_unsafe(info.offer_price, params.min_offer_delta_bps))
}

#[inline]
pub fn compute_notional_security_deposit(params: &AuctionParameters, notional: u64) -> u64 {
params
.security_deposit_base
.saturating_add(mul_bps_unsafe(notional, params.security_deposit_bps))
}

pub fn require_valid_parameters(params: &AuctionParameters) -> Result<()> {
let AuctionParameters {
user_penalty_reward_bps,
initial_penalty_bps,
duration,
grace_period,
penalty_period,
min_offer_delta_bps,
security_deposit_base,
security_deposit_bps,
} = params;

require!(*duration > 0, MatchingEngineError::ZeroDuration);
require!(*grace_period > 0, MatchingEngineError::ZeroGracePeriod);
require!(*penalty_period > 0, MatchingEngineError::ZeroPenaltyPeriod);
require!(
params.duration > 0,
MatchingEngineError::InvalidAuctionDuration
*user_penalty_reward_bps <= FEE_PRECISION_MAX,
MatchingEngineError::UserPenaltyRewardBpsTooLarge
);
require!(
params.grace_period > 0,
MatchingEngineError::InvalidAuctionGracePeriod
*initial_penalty_bps <= FEE_PRECISION_MAX,
MatchingEngineError::InitialPenaltyBpsTooLarge
);
require!(
params.user_penalty_reward_bps <= FEE_PRECISION_MAX,
MatchingEngineError::UserPenaltyTooLarge
*min_offer_delta_bps <= FEE_PRECISION_MAX,
MatchingEngineError::MinOfferDeltaBpsTooLarge
);
require!(
params.initial_penalty_bps <= FEE_PRECISION_MAX,
MatchingEngineError::InitialPenaltyTooLarge
*security_deposit_base > 0,
MatchingEngineError::ZeroSecurityDepositBase,
);
require!(
params.min_offer_delta_bps <= FEE_PRECISION_MAX,
MatchingEngineError::MinOfferDeltaTooLarge
*security_deposit_bps <= FEE_PRECISION_MAX,
MatchingEngineError::SecurityDepositBpsTooLarge
);

Ok(())
Expand Down Expand Up @@ -350,7 +371,9 @@ mod test {
duration: 2,
grace_period: 4,
penalty_period: 20,
min_offer_delta_bps: 50000, // 5%
min_offer_delta_bps: 50000, // 5%
security_deposit_base: 1000000, // 1.0 USDC
security_deposit_bps: 5000, // 0.5%
};

require_valid_parameters(&params).unwrap();
Expand Down
51 changes: 46 additions & 5 deletions solana/target/idl/matching_engine.json
Original file line number Diff line number Diff line change
Expand Up @@ -2356,10 +2356,16 @@
"fields": [
{
"name": "id",
"docs": [
"Monotonically increasing identifier for auction configs."
],
"type": "u32"
},
{
"name": "parameters",
"docs": [
"Auction parameters, which are validated by [crate::utils::auction::require_valid_parameters]."
],
"type": {
"defined": "AuctionParameters"
}
Expand Down Expand Up @@ -2707,6 +2713,21 @@
{
"name": "minOfferDeltaBps",
"type": "u32"
},
{
"name": "securityDepositBase",
"docs": [
"The base security deposit, which will the the additional amount an auction participant must",
"deposit to participate in an auction."
],
"type": "u64"
},
{
"name": "securityDepositBps",
"docs": [
"Additional security deposit based on the notional of the order amount."
],
"type": "u32"
}
]
}
Expand Down Expand Up @@ -3095,6 +3116,10 @@
"code": 6004,
"name": "OwnerOrAssistantOnly"
},
{
"code": 6016,
"name": "U64Overflow"
},
{
"code": 6032,
"name": "SameEndpoint"
Expand Down Expand Up @@ -3165,23 +3190,39 @@
},
{
"code": 6260,
"name": "InvalidAuctionDuration"
"name": "ZeroDuration"
},
{
"code": 6262,
"name": "InvalidAuctionGracePeriod"
"name": "ZeroGracePeriod"
},
{
"code": 6263,
"name": "ZeroPenaltyPeriod"
},
{
"code": 6264,
"name": "UserPenaltyTooLarge"
"name": "UserPenaltyRewardBpsTooLarge",
"msg": "Value exceeds 1000000"
},
{
"code": 6266,
"name": "InitialPenaltyTooLarge"
"name": "InitialPenaltyBpsTooLarge",
"msg": "Value exceeds 1000000"
},
{
"code": 6268,
"name": "MinOfferDeltaTooLarge"
"name": "MinOfferDeltaBpsTooLarge",
"msg": "Value exceeds 1000000"
},
{
"code": 6270,
"name": "ZeroSecurityDepositBase"
},
{
"code": 6271,
"name": "SecurityDepositBpsTooLarge",
"msg": "Value exceeds 1000000"
},
{
"code": 6514,
Expand Down
Loading

0 comments on commit 0bc8834

Please sign in to comment.