Skip to content
Closed
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
5,942 changes: 5,083 additions & 859 deletions solana/Cargo.lock

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions solana/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,12 @@ hex = "0.4.3"
ruint = "1.9.0"
cfg-if = "1.0"
hex-literal = "0.4.1"
bytemuck = "1.13.0"

wormhole-svm-shim = { git = "https://github.com/wormholelabs-xyz/wormhole.git", rev = "32cb65dd9ae11547f0e57d106b6974dc8ed5f52d" }
wormhole-svm-definitions = { git = "https://github.com/wormholelabs-xyz/wormhole.git", rev = "32cb65dd9ae11547f0e57d106b6974dc8ed5f52d", features = ["borsh"] }



[profile.release]
overflow-checks = true
Expand Down
24 changes: 23 additions & 1 deletion solana/programs/matching-engine/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,33 @@ anchor-spl.workspace = true
solana-program.workspace = true

hex.workspace = true
bytemuck.workspace = true
ruint.workspace = true
cfg-if.workspace = true
wormhole-svm-definitions.workspace = true
wormhole-svm-shim.workspace = true
wormhole-io.workspace = true

[dev-dependencies]
hex-literal.workspace = true
solana-program-test = "1.18.15"
solana-sdk = "1.18.15"
serde_json = "1.0.138"
bincode = "1.3.3"
solana-cli-output = "1.18.15"
base64 = "0.22.1"
lazy_static = "1.4.0"
bs58 = "0.5.0"
serde = { version = "1.0.212", features = ["derive"] }
secp256k1 = {version = "0.30.0", features = ["rand", "hashes", "std", "global-context", "recovery"] }
num-traits = "0.2.16"
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
tracing-log = "0.2.0"
once_cell = "1.8"
anyhow = "1.0.97"
wormhole-svm-shim.workspace = true
wormhole-svm-definitions.workspace = true

[lints]
workspace = true
workspace = true
16 changes: 16 additions & 0 deletions solana/programs/matching-engine/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,22 @@ pub enum MatchingEngineError {
CannotCloseAuctionYet = 0x500,
AuctionHistoryNotFull = 0x502,
AuctionHistoryFull = 0x504,

InvalidVerifyVaaShimProgram = 0x600,

// Fallback matching engine errors
AccountAlreadyInitialized = 0x700,
AccountNotWritable = 0x702,
BorshDeserializationError = 0x704,
InvalidPda = 0x706,
AccountDataTooSmall = 0x708,
InvalidProgram = 0x70a,
TokenTransferFailed = 0x70c,
InvalidMint = 0x70e,

#[msg("From and to router endpoints are the same")]
SameEndpoints = 0x800,
InvalidCctpMessage = 0x802,
}

#[cfg(test)]
Expand Down
2 changes: 2 additions & 0 deletions solana/programs/matching-engine/src/fallback/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
mod processor;
pub use processor::*;
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
use crate::state::Custodian;
use anchor_lang::prelude::*;
use common::wormhole_cctp_solana::{
cctp::token_messenger_minter_program::cpi::{
deposit_for_burn_with_caller, DepositForBurnWithCaller, DepositForBurnWithCallerParams,
},
cpi::BurnAndPublishArgs,
};
use solana_program::program::invoke_signed_unchecked;
use wormhole_svm_definitions::solana::{
CORE_BRIDGE_CONFIG, CORE_BRIDGE_FEE_COLLECTOR, CORE_BRIDGE_PROGRAM_ID,
POST_MESSAGE_SHIM_EVENT_AUTHORITY, POST_MESSAGE_SHIM_PROGRAM_ID,
};
use wormhole_svm_definitions::{
find_emitter_sequence_address, find_shim_message_address, solana::Finality,
};
use wormhole_svm_shim::post_message;

// This is a helper struct to make it easier to pass in the accounts for the post_message instruction.
pub struct PostMessageAccounts {
pub emitter: Pubkey,
pub payer: Pubkey,
derived: PostMessageDerivedAccounts,
}

impl PostMessageAccounts {
pub fn new(emitter: Pubkey, payer: Pubkey) -> Self {
Self {
emitter,
payer,
derived: Self::get_derived_accounts(&emitter),
}
}
fn get_derived_accounts(emitter: &Pubkey) -> PostMessageDerivedAccounts {
PostMessageDerivedAccounts {
message: find_shim_message_address(emitter, &POST_MESSAGE_SHIM_PROGRAM_ID).0,
sequence: find_emitter_sequence_address(emitter, &CORE_BRIDGE_PROGRAM_ID).0,
}
}
}

pub struct PostMessageDerivedAccounts {
pub message: Pubkey,
pub sequence: Pubkey,
}

pub fn burn_and_post<'info>(
cctp_ctx: CpiContext<'_, '_, '_, 'info, DepositForBurnWithCaller<'info>>,
burn_and_publish_args: BurnAndPublishArgs,
post_message_accounts: PostMessageAccounts,
account_infos: &[AccountInfo],
) -> Result<()> {
let BurnAndPublishArgs {
burn_source: _,
destination_caller,
destination_cctp_domain,
amount,
mint_recipient,
wormhole_message_nonce,
payload,
} = burn_and_publish_args;

// Post message to the shim program
let post_message_ix = post_message::PostMessage {
program_id: &POST_MESSAGE_SHIM_PROGRAM_ID,
accounts: post_message::PostMessageAccounts {
emitter: &post_message_accounts.emitter,
payer: &post_message_accounts.payer,
wormhole_program_id: &CORE_BRIDGE_PROGRAM_ID,
derived: post_message::PostMessageDerivedAccounts {
message: Some(&post_message_accounts.derived.message),
sequence: Some(&post_message_accounts.derived.sequence),
core_bridge_config: Some(&CORE_BRIDGE_CONFIG),
fee_collector: Some(&CORE_BRIDGE_FEE_COLLECTOR),
event_authority: Some(&POST_MESSAGE_SHIM_EVENT_AUTHORITY),
},
},
data: post_message::PostMessageData::new(
wormhole_message_nonce,
Finality::Finalized,
&payload,
)
.unwrap(),
}
.instruction();

invoke_signed_unchecked(&post_message_ix, account_infos, &[Custodian::SIGNER_SEEDS])?;

// Deposit for burn
deposit_for_burn_with_caller(
cctp_ctx,
DepositForBurnWithCallerParams {
amount,
destination_domain: destination_cctp_domain,
mint_recipient,
destination_caller,
},
)?;
Ok(())
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
use crate::state::FastMarketOrder;
use anchor_lang::prelude::*;
use solana_program::instruction::Instruction;
use solana_program::program_error::ProgramError;

use super::helpers::check_account_length;
use super::FallbackMatchingEngineInstruction;

pub struct CloseFastMarketOrderAccounts<'ix> {
/// The fast market order account created from the initialise fast market order instruction
pub fast_market_order: &'ix Pubkey,
/// The account that will receive the refund. CHECK: Must be a signer.
/// CHECK: Must match the close account refund recipient in the fast market order account
pub close_account_refund_recipient: &'ix Pubkey,
}

impl<'ix> CloseFastMarketOrderAccounts<'ix> {
pub fn to_account_metas(&self) -> Vec<AccountMeta> {
vec![
AccountMeta::new(*self.fast_market_order, false),
AccountMeta::new(*self.close_account_refund_recipient, true),
]
}
}

pub struct CloseFastMarketOrder<'ix> {
pub program_id: &'ix Pubkey,
pub accounts: CloseFastMarketOrderAccounts<'ix>,
}

impl CloseFastMarketOrder<'_> {
pub fn instruction(&self) -> Instruction {
Instruction {
program_id: *self.program_id,
accounts: self.accounts.to_account_metas(),
data: FallbackMatchingEngineInstruction::CloseFastMarketOrder.to_vec(),
}
}
}

/// Closes the fast market order and transfers the lamports from the fast market order to the close account refund recipient
///
/// # Arguments
///
/// * `accounts` - The accounts of the fast market order and the close account refund recipient
///
/// # Returns
///
/// Result<()>
pub fn close_fast_market_order(accounts: &[AccountInfo]) -> Result<()> {
check_account_length(accounts, 2)?;

let fast_market_order = &accounts[0];
let close_account_refund_recipient = &accounts[1];

if !close_account_refund_recipient.is_signer {
msg!("Refund recipient (account #2) is not a signer");
return Err(ProgramError::InvalidAccountData.into());
}

let fast_market_order_data =
FastMarketOrder::try_deserialize(&mut &fast_market_order.data.borrow()[..])?;
if fast_market_order_data.close_account_refund_recipient
!= close_account_refund_recipient.key().as_ref()
{
return Err(ProgramError::InvalidAccountData.into()).map_err(|e: Error| {
e.with_pubkeys((
Pubkey::from(fast_market_order_data.close_account_refund_recipient),
close_account_refund_recipient.key(),
))
});
}

// Transfer the lamports from the fast market order to the close account refund recipient
let mut fast_market_order_lamports = fast_market_order.lamports.borrow_mut();
**close_account_refund_recipient.lamports.borrow_mut() =
(**close_account_refund_recipient.lamports.borrow())
.saturating_add(**fast_market_order_lamports);
**fast_market_order_lamports = 0;

Ok(())
}
Loading
Loading