Skip to content
This repository was archived by the owner on Jun 16, 2025. It is now read-only.

Commit e7a9873

Browse files
authored
solana: overhaul local fills (#122)
Co-authored-by: A5 Pickle <[email protected]>
1 parent 44ae258 commit e7a9873

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

75 files changed

+5604
-2434
lines changed

solana/Anchor.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ cluster = "Localnet"
3131
wallet = "ts/tests/keys/pFCBP4bhqdSsrWUVTgqhPsLrfEdChBK17vgFM7TxjxQ.json"
3232

3333
[scripts]
34-
test = "npx ts-mocha -p ./tsconfig.json -t 1000000 ts/tests/[0-9]*.ts"
34+
test = "npx ts-mocha -p ./tsconfig.json -t 1000000 --exit ts/tests/[0-9]*.ts"
3535

3636
[test]
3737
startup_wait = 20000

solana/programs/matching-engine/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ idl-build = [
3131
common.workspace = true
3232
wormhole-solana-utils.workspace = true
3333

34-
anchor-lang = { workspace = true, features = ["derive", "init-if-needed"] }
34+
anchor-lang = { workspace = true, features = ["event-cpi", "init-if-needed"] }
3535
anchor-spl.workspace = true
3636
solana-program.workspace = true
3737

solana/programs/matching-engine/src/composite/mod.rs

Lines changed: 68 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,16 @@ use std::ops::{Deref, DerefMut};
33
use crate::{
44
error::MatchingEngineError,
55
state::{
6-
Auction, AuctionStatus, Custodian, MessageProtocol, PreparedOrderResponse, RouterEndpoint,
6+
Auction, AuctionStatus, Custodian, FastFillSequencer, MessageProtocol,
7+
PreparedOrderResponse, ReservedFastFillSequence, RouterEndpoint,
78
},
89
utils::{self, VaaDigest},
910
};
1011
use anchor_lang::prelude::*;
1112
use anchor_spl::token;
1213
use common::{
1314
admin::utils::{assistant::only_authorized, ownable::only_owner},
14-
messages::raw::{LiquidityLayerMessage, LiquidityLayerPayload},
15+
messages::raw::LiquidityLayerMessage,
1516
wormhole_cctp_solana::{
1617
cctp::{message_transmitter_program, token_messenger_minter_program},
1718
wormhole::{core_bridge_program, VaaAccount},
@@ -66,7 +67,8 @@ pub struct LiquidityLayerVaa<'info> {
6667
let vaa = VaaAccount::load(&vaa)?;
6768
6869
// Is it a legitimate LL message?
69-
LiquidityLayerPayload::try_from(vaa.payload()).map_err(|_| MatchingEngineError::InvalidVaa)?;
70+
LiquidityLayerMessage::try_from(vaa.payload())
71+
.map_err(|_| MatchingEngineError::InvalidVaa)?;
7072
7173
// Done.
7274
true
@@ -368,6 +370,18 @@ pub struct ExecuteOrder<'info> {
368370
)]
369371
pub fast_vaa: LiquidityLayerVaa<'info>,
370372

373+
#[account(
374+
constraint = {
375+
let info = active_auction.info.as_ref().unwrap();
376+
377+
require!(
378+
!info.within_auction_duration(&active_auction.config),
379+
MatchingEngineError::AuctionPeriodNotExpired
380+
);
381+
382+
true
383+
}
384+
)]
371385
pub active_auction: ActiveAuction<'info>,
372386

373387
/// CHECK: Must be a token account, whose mint is [common::USDC_MINT].
@@ -519,9 +533,9 @@ pub struct ClosePreparedOrderResponse<'info> {
519533
close = by,
520534
seeds = [
521535
PreparedOrderResponse::SEED_PREFIX,
522-
order_response.fast_vaa_hash.as_ref()
536+
order_response.seeds.fast_vaa_hash.as_ref()
523537
],
524-
bump = order_response.bump,
538+
bump = order_response.seeds.bump,
525539
)]
526540
pub order_response: Box<Account<'info, PreparedOrderResponse>>,
527541

@@ -539,10 +553,58 @@ pub struct ClosePreparedOrderResponse<'info> {
539553

540554
impl<'info> VaaDigest for ClosePreparedOrderResponse<'info> {
541555
fn digest(&self) -> [u8; 32] {
542-
self.order_response.fast_vaa_hash
556+
self.order_response.seeds.fast_vaa_hash
543557
}
544558
}
545559

560+
#[derive(Accounts)]
561+
pub struct ReserveFastFillSequence<'info> {
562+
#[account(mut)]
563+
payer: Signer<'info>,
564+
565+
pub fast_order_path: FastOrderPath<'info>,
566+
567+
/// This sequencer determines the next reserved sequence. If it does not exist for a given
568+
/// source chain and sender, it will be created.
569+
///
570+
/// Auction participants may want to consider pricing the creation of this account into their
571+
/// offer prices by checking whether this sequencer already exists for those orders destined for
572+
/// Solana.
573+
#[account(
574+
init_if_needed,
575+
payer = payer,
576+
space = 8 + FastFillSequencer::INIT_SPACE,
577+
seeds = [
578+
FastFillSequencer::SEED_PREFIX,
579+
&fast_order_path.fast_vaa.load_unchecked().emitter_chain().to_be_bytes(),
580+
&{
581+
let vaa = fast_order_path.fast_vaa.load_unchecked();
582+
LiquidityLayerMessage::try_from(vaa.payload())
583+
.unwrap()
584+
.to_fast_market_order_unchecked().sender()
585+
},
586+
],
587+
bump,
588+
)]
589+
pub sequencer: Box<Account<'info, FastFillSequencer>>,
590+
591+
/// This account will be used to determine the sequence of the next fast fill. When a local
592+
/// order is executed or an non-existent auction is settled, this account will be closed.
593+
#[account(
594+
init,
595+
payer = payer,
596+
space = 8 + ReservedFastFillSequence::INIT_SPACE,
597+
seeds = [
598+
ReservedFastFillSequence::SEED_PREFIX,
599+
fast_order_path.fast_vaa.load_unchecked().digest().as_ref(),
600+
],
601+
bump,
602+
)]
603+
pub reserved: Box<Account<'info, ReservedFastFillSequence>>,
604+
605+
system_program: Program<'info, System>,
606+
}
607+
546608
/// NOTE: Keep this at the end in case Wormhole removes the need for these accounts.
547609
#[derive(Accounts)]
548610
pub struct RequiredSysvars<'info> {

solana/programs/matching-engine/src/error.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,6 @@ pub enum MatchingEngineError {
5959

6060
FastMarketOrderExpired = 0x400,
6161
OfferPriceTooHigh = 0x402,
62-
InvalidEmitterForFastFill = 0x406,
6362
AuctionNotActive = 0x408,
6463
AuctionPeriodExpired = 0x40a,
6564
AuctionPeriodNotExpired = 0x40c,
@@ -69,6 +68,18 @@ pub enum MatchingEngineError {
6968
AuctionNotSettled = 0x420,
7069
ExecutorNotPreparedBy = 0x422,
7170
InvalidOfferToken = 0x424,
71+
FastFillTooLarge = 0x426,
72+
AuctionExists = 0x428,
73+
AccountNotAuction = 0x429,
74+
BestOfferTokenMismatch = 0x42a,
75+
BestOfferTokenRequired = 0x42c,
76+
PreparedByMismatch = 0x42e,
77+
PreparedOrderResponseNotRequired = 0x42f,
78+
AuctionConfigNotRequired = 0x430,
79+
BestOfferTokenNotRequired = 0x431,
80+
FastFillAlreadyRedeemed = 0x434,
81+
FastFillNotRedeemed = 0x435,
82+
ReservedSequenceMismatch = 0x438,
7283

7384
CannotCloseAuctionYet = 0x500,
7485
AuctionHistoryNotFull = 0x502,

solana/programs/matching-engine/src/events/auction_settled.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::state::MessageProtocol;
12
use anchor_lang::prelude::*;
23

34
#[event]
@@ -12,4 +13,7 @@ pub struct AuctionSettled {
1213
/// Token account's new balance. If there was no auction, this balance will be of the fee
1314
/// recipient token account.
1415
pub token_balance_after: u64,
16+
17+
/// This value will only be some if there was no active auction.
18+
pub with_execute: Option<MessageProtocol>,
1519
}

solana/programs/matching-engine/src/events/auction_updated.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ pub struct AuctionUpdated {
99
pub vaa: Option<Pubkey>,
1010
pub source_chain: u16,
1111
pub target_protocol: MessageProtocol,
12+
pub redeemer_message_len: u32,
13+
1214
pub end_slot: u64,
1315
pub best_offer_token: Pubkey,
1416
pub token_balance_before: u64,
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
use anchor_lang::prelude::*;
2+
3+
#[event]
4+
pub struct FastFillRedeemed {
5+
pub prepared_by: Pubkey,
6+
pub fast_fill: Pubkey,
7+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
use crate::state::FastFillSeeds;
2+
use anchor_lang::prelude::*;
3+
4+
#[event]
5+
pub struct FastFillSequenceReserved {
6+
pub fast_vaa_hash: [u8; 32],
7+
pub fast_fill_seeds: FastFillSeeds,
8+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
use crate::state::{FastFillInfo, FastFillSeeds};
2+
use anchor_lang::prelude::*;
3+
4+
#[event]
5+
pub struct LocalFastOrderFilled {
6+
pub seeds: FastFillSeeds,
7+
pub info: FastFillInfo,
8+
pub auction: Option<Pubkey>,
9+
}

solana/programs/matching-engine/src/events/mod.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,15 @@ pub use auction_updated::*;
77
mod enacted;
88
pub use enacted::*;
99

10+
mod fast_fill_redeemed;
11+
pub use fast_fill_redeemed::*;
12+
13+
mod fast_fill_sequence_reserved;
14+
pub use fast_fill_sequence_reserved::*;
15+
16+
mod filled_local_fast_order;
17+
pub use filled_local_fast_order::*;
18+
1019
mod order_executed;
1120
pub use order_executed::*;
1221

0 commit comments

Comments
 (0)