Skip to content
This repository was archived by the owner on Jun 16, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion solana/Anchor.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ cluster = "Localnet"
wallet = "ts/tests/keys/pFCBP4bhqdSsrWUVTgqhPsLrfEdChBK17vgFM7TxjxQ.json"

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

[test]
startup_wait = 20000
Expand Down
2 changes: 1 addition & 1 deletion solana/programs/matching-engine/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ idl-build = [
common.workspace = true
wormhole-solana-utils.workspace = true

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

Expand Down
74 changes: 68 additions & 6 deletions solana/programs/matching-engine/src/composite/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,16 @@ use std::ops::{Deref, DerefMut};
use crate::{
error::MatchingEngineError,
state::{
Auction, AuctionStatus, Custodian, MessageProtocol, PreparedOrderResponse, RouterEndpoint,
Auction, AuctionStatus, Custodian, FastFillSequencer, MessageProtocol,
PreparedOrderResponse, ReservedFastFillSequence, RouterEndpoint,
},
utils::{self, VaaDigest},
};
use anchor_lang::prelude::*;
use anchor_spl::token;
use common::{
admin::utils::{assistant::only_authorized, ownable::only_owner},
messages::raw::{LiquidityLayerMessage, LiquidityLayerPayload},
messages::raw::LiquidityLayerMessage,
wormhole_cctp_solana::{
cctp::{message_transmitter_program, token_messenger_minter_program},
wormhole::{core_bridge_program, VaaAccount},
Expand Down Expand Up @@ -66,7 +67,8 @@ pub struct LiquidityLayerVaa<'info> {
let vaa = VaaAccount::load(&vaa)?;

// Is it a legitimate LL message?
LiquidityLayerPayload::try_from(vaa.payload()).map_err(|_| MatchingEngineError::InvalidVaa)?;
LiquidityLayerMessage::try_from(vaa.payload())
.map_err(|_| MatchingEngineError::InvalidVaa)?;

// Done.
true
Expand Down Expand Up @@ -368,6 +370,18 @@ pub struct ExecuteOrder<'info> {
)]
pub fast_vaa: LiquidityLayerVaa<'info>,

#[account(
constraint = {
let info = active_auction.info.as_ref().unwrap();

require!(
!info.within_auction_duration(&active_auction.config),
MatchingEngineError::AuctionPeriodNotExpired
);

true
}
)]
pub active_auction: ActiveAuction<'info>,

/// CHECK: Must be a token account, whose mint is [common::USDC_MINT].
Expand Down Expand Up @@ -519,9 +533,9 @@ pub struct ClosePreparedOrderResponse<'info> {
close = by,
seeds = [
PreparedOrderResponse::SEED_PREFIX,
order_response.fast_vaa_hash.as_ref()
order_response.seeds.fast_vaa_hash.as_ref()
],
bump = order_response.bump,
bump = order_response.seeds.bump,
)]
pub order_response: Box<Account<'info, PreparedOrderResponse>>,

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

impl<'info> VaaDigest for ClosePreparedOrderResponse<'info> {
fn digest(&self) -> [u8; 32] {
self.order_response.fast_vaa_hash
self.order_response.seeds.fast_vaa_hash
}
}

#[derive(Accounts)]
pub struct ReserveFastFillSequence<'info> {
#[account(mut)]
payer: Signer<'info>,

pub fast_order_path: FastOrderPath<'info>,

/// This sequencer determines the next reserved sequence. If it does not exist for a given
/// source chain and sender, it will be created.
///
/// Auction participants may want to consider pricing the creation of this account into their
/// offer prices by checking whether this sequencer already exists for those orders destined for
/// Solana.
#[account(
init_if_needed,
payer = payer,
space = 8 + FastFillSequencer::INIT_SPACE,
seeds = [
FastFillSequencer::SEED_PREFIX,
&fast_order_path.fast_vaa.load_unchecked().emitter_chain().to_be_bytes(),
&{
let vaa = fast_order_path.fast_vaa.load_unchecked();
LiquidityLayerMessage::try_from(vaa.payload())
.unwrap()
.to_fast_market_order_unchecked().sender()
},
],
bump,
)]
pub sequencer: Box<Account<'info, FastFillSequencer>>,

/// This account will be used to determine the sequence of the next fast fill. When a local
/// order is executed or an non-existent auction is settled, this account will be closed.
#[account(
init,
payer = payer,
space = 8 + ReservedFastFillSequence::INIT_SPACE,
seeds = [
ReservedFastFillSequence::SEED_PREFIX,
fast_order_path.fast_vaa.load_unchecked().digest().as_ref(),
],
bump,
)]
pub reserved: Box<Account<'info, ReservedFastFillSequence>>,

system_program: Program<'info, System>,
}

/// NOTE: Keep this at the end in case Wormhole removes the need for these accounts.
#[derive(Accounts)]
pub struct RequiredSysvars<'info> {
Expand Down
13 changes: 12 additions & 1 deletion solana/programs/matching-engine/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ pub enum MatchingEngineError {

FastMarketOrderExpired = 0x400,
OfferPriceTooHigh = 0x402,
InvalidEmitterForFastFill = 0x406,
AuctionNotActive = 0x408,
AuctionPeriodExpired = 0x40a,
AuctionPeriodNotExpired = 0x40c,
Expand All @@ -69,6 +68,18 @@ pub enum MatchingEngineError {
AuctionNotSettled = 0x420,
ExecutorNotPreparedBy = 0x422,
InvalidOfferToken = 0x424,
FastFillTooLarge = 0x426,
AuctionExists = 0x428,
AccountNotAuction = 0x429,
BestOfferTokenMismatch = 0x42a,
BestOfferTokenRequired = 0x42c,
PreparedByMismatch = 0x42e,
PreparedOrderResponseNotRequired = 0x42f,
AuctionConfigNotRequired = 0x430,
BestOfferTokenNotRequired = 0x431,
FastFillAlreadyRedeemed = 0x434,
FastFillNotRedeemed = 0x435,
ReservedSequenceMismatch = 0x438,

CannotCloseAuctionYet = 0x500,
AuctionHistoryNotFull = 0x502,
Expand Down
4 changes: 4 additions & 0 deletions solana/programs/matching-engine/src/events/auction_settled.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::state::MessageProtocol;
use anchor_lang::prelude::*;

#[event]
Expand All @@ -12,4 +13,7 @@ pub struct AuctionSettled {
/// Token account's new balance. If there was no auction, this balance will be of the fee
/// recipient token account.
pub token_balance_after: u64,

/// This value will only be some if there was no active auction.
pub with_execute: Option<MessageProtocol>,
}
2 changes: 2 additions & 0 deletions solana/programs/matching-engine/src/events/auction_updated.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ pub struct AuctionUpdated {
pub vaa: Option<Pubkey>,
pub source_chain: u16,
pub target_protocol: MessageProtocol,
pub redeemer_message_len: u32,

pub end_slot: u64,
pub best_offer_token: Pubkey,
pub token_balance_before: u64,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
use anchor_lang::prelude::*;

#[event]
pub struct FastFillRedeemed {
pub prepared_by: Pubkey,
pub fast_fill: Pubkey,
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
use crate::state::FastFillSeeds;
use anchor_lang::prelude::*;

#[event]
pub struct FastFillSequenceReserved {
pub fast_vaa_hash: [u8; 32],
pub fast_fill_seeds: FastFillSeeds,
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
use crate::state::{FastFillInfo, FastFillSeeds};
use anchor_lang::prelude::*;

#[event]
pub struct LocalFastOrderFilled {
pub seeds: FastFillSeeds,
pub info: FastFillInfo,
pub auction: Option<Pubkey>,
}
9 changes: 9 additions & 0 deletions solana/programs/matching-engine/src/events/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@ pub use auction_updated::*;
mod enacted;
pub use enacted::*;

mod fast_fill_redeemed;
pub use fast_fill_redeemed::*;

mod fast_fill_sequence_reserved;
pub use fast_fill_sequence_reserved::*;

mod filled_local_fast_order;
pub use filled_local_fast_order::*;

mod order_executed;
pub use order_executed::*;

Expand Down
Loading