diff --git a/solana/Cargo.toml b/solana/Cargo.toml
index fc20f657a..d1a435385 100644
--- a/solana/Cargo.toml
+++ b/solana/Cargo.toml
@@ -58,3 +58,6 @@ codegen-units = 1
opt-level = 3
incremental = false
codegen-units = 1
+
+[workspace.lints.clippy]
+cast_possible_truncation = "deny"
\ No newline at end of file
diff --git a/solana/Makefile b/solana/Makefile
index 4c2d57426..738412dd7 100644
--- a/solana/Makefile
+++ b/solana/Makefile
@@ -1,27 +1,27 @@
-SOLANA_CLI="v1.16.16"
-ANCHOR_CLI="v0.28.0"
-
out_mainnet=artifacts-mainnet
out_testnet=artifacts-testnet
out_localnet=artifacts-localnet
-.PHONY: all clean check build test lint ci cargo-test
-
+.PHONY: all
all: check
+.PHONY: check
check:
- cargo check --all-features
+ cargo check --workspace --all-targets --all-features
+.PHONY: clean
clean:
anchor clean
rm -rf node_modules artifacts-mainnet artifacts-testnet artifacts-localnet ts/tests/artifacts
-node_modules:
+node_modules: package-lock.json
npm ci
-cargo-test $(NETWORK):
- cargo test --features "$(NETWORK)" --no-default-features
+.PHONY: cargo-test
+cargo-test:
+ cargo test --workspace --all-targets --features $(NETWORK)
+.PHONY: build
build: $(out_$(NETWORK))
$(out_$(NETWORK)): cargo-test
ifdef out_$(NETWORK)
@@ -30,6 +30,7 @@ ifdef out_$(NETWORK)
cp target/deploy/*.so $(out_$(NETWORK))/
endif
+.PHONY: test
test: node_modules
NETWORK=localnet $(MAKE) cargo-test
NETWORK=testnet $(MAKE) cargo-test
@@ -41,13 +42,12 @@ test: node_modules
anchor build --arch sbf -- --features integration-test
anchor test --skip-build
+.PHONY: clippy
+clippy:
+ cargo clippy --workspace --no-deps --all-targets --features $(NETWORK) -- -Dwarnings
+
+.PHONY: lint
lint:
cargo fmt --check
- cargo clippy --no-deps --all-targets --features testnet -- -D warnings
- cargo clippy --no-deps --all-targets --features localnet -- -D warnings
-
-ci:
- DOCKER_BUILDKIT=1 docker build -f Dockerfile.ci \
- --build-arg SOLANA_CLI=$(SOLANA_CLI) \
- --build-arg ANCHOR_CLI=$(ANCHOR_CLI) \
- .
+ NETWORK=localnet $(MAKE) clippy
+ NETWORK=testnet $(MAKE) clippy
\ No newline at end of file
diff --git a/solana/modules/common/src/admin/utils/assistant.rs b/solana/modules/common/src/admin/utils/assistant.rs
index a3dcb2ef1..576e474c8 100644
--- a/solana/modules/common/src/admin/utils/assistant.rs
+++ b/solana/modules/common/src/admin/utils/assistant.rs
@@ -1,23 +1,41 @@
use crate::admin::OwnerAssistant;
use anchor_lang::prelude::*;
-pub fn only_owner_assistant(acct: &Account, owner_assistant: &Pubkey) -> bool
+pub fn only_owner_assistant(
+ acct: &Account,
+ owner_assistant: &Signer,
+ custom_error: Error,
+) -> Result
where
A: OwnerAssistant + Clone + AccountSerialize + AccountDeserialize,
{
- acct.owner_assistant() == owner_assistant
+ if acct.owner_assistant() == &owner_assistant.key() {
+ Ok(true)
+ } else {
+ Err(custom_error.with_pubkeys((*acct.owner_assistant(), owner_assistant.key())))
+ }
}
-pub fn only_authorized(acct: &Account, owner_or_assistant: &Pubkey) -> bool
+pub fn only_authorized(
+ acct: &Account,
+ owner_or_assistant: &Signer,
+ custom_error: Error,
+) -> Result
where
A: OwnerAssistant + Clone + AccountSerialize + AccountDeserialize,
{
- acct.owner() == owner_or_assistant || acct.owner_assistant() == owner_or_assistant
+ if acct.owner() == &owner_or_assistant.key()
+ || acct.owner_assistant() == &owner_or_assistant.key()
+ {
+ Ok(true)
+ } else {
+ Err(custom_error)
+ }
}
-pub fn transfer_owner_assistant(acct: &mut Account, new_assistant: &Pubkey)
+pub fn transfer_owner_assistant(acct: &mut Account, new_assistant: &AccountInfo)
where
A: OwnerAssistant + Clone + AccountSerialize + AccountDeserialize,
{
- *acct.owner_assistant_mut() = *new_assistant;
+ *acct.owner_assistant_mut() = new_assistant.key();
}
diff --git a/solana/modules/common/src/admin/utils/ownable.rs b/solana/modules/common/src/admin/utils/ownable.rs
index d88ed64c9..73e2b6f15 100644
--- a/solana/modules/common/src/admin/utils/ownable.rs
+++ b/solana/modules/common/src/admin/utils/ownable.rs
@@ -1,16 +1,20 @@
use crate::admin::Ownable;
use anchor_lang::prelude::*;
-pub fn only_owner(acct: &Account, owner: &Pubkey) -> bool
+pub fn only_owner(acct: &Account, owner: &Signer, custom_error: Error) -> Result
where
A: Ownable + Clone + AccountSerialize + AccountDeserialize,
{
- *acct.owner() == *owner
+ if acct.owner() == &owner.key() {
+ Ok(true)
+ } else {
+ Err(custom_error.with_pubkeys((*acct.owner(), owner.key())))
+ }
}
-pub fn transfer_ownership(acct: &mut Account, new_owner: &Pubkey)
+pub fn transfer_ownership(acct: &mut Account, new_owner: &AccountInfo)
where
A: Ownable + Clone + AccountSerialize + AccountDeserialize,
{
- *acct.owner_mut() = *new_owner;
+ *acct.owner_mut() = new_owner.key();
}
diff --git a/solana/programs/matching-engine/Cargo.toml b/solana/programs/matching-engine/Cargo.toml
index 03e71dac4..0bafbbdc8 100644
--- a/solana/programs/matching-engine/Cargo.toml
+++ b/solana/programs/matching-engine/Cargo.toml
@@ -34,4 +34,7 @@ ruint.workspace = true
cfg-if.workspace = true
[dev-dependencies]
-hex-literal.workspace = true
\ No newline at end of file
+hex-literal.workspace = true
+
+[lints]
+workspace = true
\ No newline at end of file
diff --git a/solana/programs/matching-engine/src/composite/mod.rs b/solana/programs/matching-engine/src/composite/mod.rs
new file mode 100644
index 000000000..6e322607f
--- /dev/null
+++ b/solana/programs/matching-engine/src/composite/mod.rs
@@ -0,0 +1,550 @@
+use std::ops::{Deref, DerefMut};
+
+use crate::{
+ error::MatchingEngineError,
+ state::{
+ Auction, AuctionStatus, Custodian, MessageProtocol, PreparedOrderResponse, 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},
+ wormhole_cctp_solana::{
+ cctp::{message_transmitter_program, token_messenger_minter_program},
+ wormhole::{core_bridge_program, VaaAccount},
+ },
+};
+
+#[derive(Accounts)]
+pub struct Usdc<'info> {
+ /// CHECK: This address must equal [USDC_MINT](common::constants::USDC_MINT).
+ #[account(address = common::constants::USDC_MINT)]
+ pub mint: AccountInfo<'info>,
+}
+
+impl<'info> Deref for Usdc<'info> {
+ type Target = AccountInfo<'info>;
+
+ fn deref(&self) -> &Self::Target {
+ &self.mint
+ }
+}
+
+/// Mint recipient token account, which is encoded as the mint recipient in the CCTP message.
+/// The CCTP Token Messenger Minter program will transfer the amount encoded in the CCTP message
+/// from its custody account to this account.
+///
+/// CHECK: Mutable. Seeds must be \["custody"\].
+///
+/// NOTE: This account must be encoded as the mint recipient in the CCTP message.
+#[derive(Accounts)]
+pub struct CctpMintRecipientMut<'info> {
+ #[account(
+ mut,
+ address = crate::cctp_mint_recipient::id()
+ )]
+ pub mint_recipient: Box>,
+}
+
+impl<'info> Deref for CctpMintRecipientMut<'info> {
+ type Target = Account<'info, token::TokenAccount>;
+
+ fn deref(&self) -> &Self::Target {
+ &self.mint_recipient
+ }
+}
+
+#[derive(Accounts)]
+pub struct LiquidityLayerVaa<'info> {
+ /// CHECK: This VAA account must be a posted VAA from the Wormhole Core Bridge program.
+ #[account(
+ constraint = {
+ // NOTE: This load performs an owner check.
+ let vaa = VaaAccount::load(&vaa)?;
+
+ // Is it a legitimate LL message?
+ LiquidityLayerPayload::try_from(vaa.payload()).map_err(|_| MatchingEngineError::InvalidVaa)?;
+
+ // Done.
+ true
+ }
+ )]
+ pub vaa: AccountInfo<'info>,
+}
+
+impl<'info> LiquidityLayerVaa<'info> {
+ pub fn load_unchecked(&self) -> VaaAccount<'_> {
+ VaaAccount::load_unchecked(self)
+ }
+}
+
+impl<'info> Deref for LiquidityLayerVaa<'info> {
+ type Target = AccountInfo<'info>;
+
+ fn deref(&self) -> &Self::Target {
+ &self.vaa
+ }
+}
+
+#[derive(Accounts)]
+pub struct CheckedCustodian<'info> {
+ #[account(
+ seeds = [Custodian::SEED_PREFIX],
+ bump = Custodian::BUMP,
+ )]
+ pub custodian: Account<'info, Custodian>,
+}
+
+impl<'info> Deref for CheckedCustodian<'info> {
+ type Target = Account<'info, Custodian>;
+
+ fn deref(&self) -> &Self::Target {
+ &self.custodian
+ }
+}
+
+#[derive(Accounts)]
+pub struct CheckedMutCustodian<'info> {
+ #[account(
+ mut,
+ seeds = [Custodian::SEED_PREFIX],
+ bump = Custodian::BUMP,
+ )]
+ pub custodian: Account<'info, Custodian>,
+}
+
+impl<'info> Deref for CheckedMutCustodian<'info> {
+ type Target = Account<'info, Custodian>;
+
+ fn deref(&self) -> &Self::Target {
+ &self.custodian
+ }
+}
+
+impl<'info> DerefMut for CheckedMutCustodian<'info> {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.custodian
+ }
+}
+
+#[derive(Accounts)]
+pub struct OwnerOnly<'info> {
+ #[account(
+ constraint = only_owner(
+ &custodian,
+ &owner,
+ error!(MatchingEngineError::OwnerOnly)
+ )?
+ )]
+ pub owner: Signer<'info>,
+
+ pub custodian: CheckedCustodian<'info>,
+}
+
+#[derive(Accounts)]
+pub struct OwnerOnlyMut<'info> {
+ #[account(
+ constraint = only_owner(
+ &custodian,
+ &owner,
+ error!(MatchingEngineError::OwnerOnly)
+ )?
+ )]
+ pub owner: Signer<'info>,
+
+ pub custodian: CheckedMutCustodian<'info>,
+}
+
+#[derive(Accounts)]
+pub struct Admin<'info> {
+ #[account(
+ constraint = only_authorized(
+ &custodian,
+ &owner_or_assistant,
+ error!(MatchingEngineError::OwnerOrAssistantOnly)
+ )?
+ )]
+ pub owner_or_assistant: Signer<'info>,
+
+ pub custodian: CheckedCustodian<'info>,
+}
+
+#[derive(Accounts)]
+pub struct AdminMut<'info> {
+ #[account(
+ constraint = only_authorized(
+ &custodian,
+ &owner_or_assistant,
+ error!(MatchingEngineError::OwnerOrAssistantOnly)
+ )?
+ )]
+ pub owner_or_assistant: Signer<'info>,
+
+ pub custodian: CheckedMutCustodian<'info>,
+}
+
+#[derive(Accounts)]
+pub struct LocalTokenRouter<'info> {
+ /// CHECK: Must be an executable (the Token Router program), whose ID will be used to derive the
+ /// emitter (router endpoint) address.
+ #[account(executable)]
+ pub token_router_program: AccountInfo<'info>,
+
+ /// CHECK: The Token Router program's emitter PDA (a.k.a. its custodian) will have account data.
+ #[account(
+ seeds = [b"emitter"],
+ bump,
+ seeds::program = token_router_program,
+ owner = token_router_program.key() @ MatchingEngineError::InvalidEndpoint,
+ constraint = !token_router_emitter.data_is_empty() @ MatchingEngineError::InvalidEndpoint,
+ )]
+ pub token_router_emitter: AccountInfo<'info>,
+
+ #[account(
+ associated_token::mint = common::constants::USDC_MINT,
+ associated_token::authority = token_router_emitter,
+ )]
+ pub token_router_mint_recipient: Account<'info, token::TokenAccount>,
+}
+
+#[derive(Accounts)]
+pub struct ExistingMutRouterEndpoint<'info> {
+ #[account(
+ mut,
+ seeds = [
+ RouterEndpoint::SEED_PREFIX,
+ &endpoint.chain.to_be_bytes()
+ ],
+ bump = endpoint.bump,
+ )]
+ pub endpoint: Account<'info, RouterEndpoint>,
+}
+
+impl<'info> Deref for ExistingMutRouterEndpoint<'info> {
+ type Target = Account<'info, RouterEndpoint>;
+
+ fn deref(&self) -> &Self::Target {
+ &self.endpoint
+ }
+}
+
+impl<'info> DerefMut for ExistingMutRouterEndpoint<'info> {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.endpoint
+ }
+}
+
+#[derive(Accounts)]
+pub struct LiveRouterEndpoint<'info> {
+ #[account(
+ seeds = [
+ RouterEndpoint::SEED_PREFIX,
+ &endpoint.chain.to_be_bytes()
+ ],
+ bump = endpoint.bump,
+ constraint = {
+ endpoint.protocol != MessageProtocol::None
+ } @ MatchingEngineError::EndpointDisabled,
+ )]
+ pub endpoint: Box>,
+}
+
+impl<'info> Deref for LiveRouterEndpoint<'info> {
+ type Target = Account<'info, RouterEndpoint>;
+
+ fn deref(&self) -> &Self::Target {
+ &self.endpoint
+ }
+}
+
+#[derive(Accounts)]
+pub struct FastOrderPath<'info> {
+ #[account(
+ constraint = {
+ let vaa = fast_vaa.load_unchecked();
+ require_eq!(
+ from.chain,
+ vaa.emitter_chain(),
+ MatchingEngineError::ErrInvalidSourceRouter
+ );
+ require!(
+ from.address == vaa.emitter_address(),
+ MatchingEngineError::ErrInvalidSourceRouter
+ );
+
+ let message = LiquidityLayerMessage::try_from(vaa.payload()).unwrap();
+ let order = message
+ .fast_market_order()
+ .ok_or(MatchingEngineError::NotFastMarketOrder)?;
+ require_eq!(
+ to.chain,
+ order.target_chain(),
+ MatchingEngineError::ErrInvalidTargetRouter
+ );
+
+ true
+ }
+ )]
+ pub fast_vaa: LiquidityLayerVaa<'info>,
+
+ pub from: LiveRouterEndpoint<'info>,
+
+ #[account(constraint = from.chain != to.chain @ MatchingEngineError::SameEndpoint)]
+ pub to: LiveRouterEndpoint<'info>,
+}
+
+#[derive(Accounts)]
+pub struct ActiveAuction<'info> {
+ #[account(
+ mut,
+ seeds = [
+ Auction::SEED_PREFIX,
+ auction.vaa_hash.as_ref(),
+ ],
+ bump = auction.bump,
+ constraint = matches!(auction.status, AuctionStatus::Active) @ MatchingEngineError::AuctionNotActive,
+ )]
+ pub auction: Box>,
+
+ #[account(
+ mut,
+ seeds = [
+ crate::AUCTION_CUSTODY_TOKEN_SEED_PREFIX,
+ auction.key().as_ref(),
+ ],
+ bump = auction.info.as_ref().unwrap().custody_token_bump,
+ )]
+ pub custody_token: Account<'info, anchor_spl::token::TokenAccount>,
+
+ #[account(
+ constraint = {
+ require_eq!(
+ auction.info.as_ref().unwrap().config_id,
+ config.id,
+ MatchingEngineError::AuctionConfigMismatch
+ );
+ true
+ },
+ )]
+ pub config: Account<'info, crate::state::AuctionConfig>,
+
+ /// CHECK: Mutable. Must have the same key in auction data.
+ #[account(
+ mut,
+ address = auction.info.as_ref().unwrap().best_offer_token,
+ )]
+ pub best_offer_token: AccountInfo<'info>,
+}
+
+impl<'info> VaaDigest for ActiveAuction<'info> {
+ fn digest(&self) -> [u8; 32] {
+ self.auction.vaa_hash
+ }
+}
+
+impl<'info> Deref for ActiveAuction<'info> {
+ type Target = Account<'info, Auction>;
+
+ fn deref(&self) -> &Self::Target {
+ &self.auction
+ }
+}
+
+impl<'info> DerefMut for ActiveAuction<'info> {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.auction
+ }
+}
+
+#[derive(Accounts)]
+pub struct ExecuteOrder<'info> {
+ /// CHECK: Must be owned by the Wormhole Core Bridge program.
+ #[account(
+ constraint = utils::require_vaa_hash_equals(&active_auction, &fast_vaa.load_unchecked())?
+ )]
+ pub fast_vaa: LiquidityLayerVaa<'info>,
+
+ pub active_auction: ActiveAuction<'info>,
+
+ pub to_router_endpoint: LiveRouterEndpoint<'info>,
+
+ /// CHECK: Must be a token account, whose mint is [common::constants::USDC_MINT].
+ #[account(mut)]
+ pub executor_token: AccountInfo<'info>,
+
+ /// CHECK: Mutable. Must equal [initial_offer](Auction::initial_offer).
+ #[account(
+ mut,
+ address = active_auction.info.as_ref().unwrap().initial_offer_token,
+ )]
+ pub initial_offer_token: AccountInfo<'info>,
+}
+
+#[derive(Accounts)]
+pub struct WormholePublishMessage<'info> {
+ /// CHECK: Seeds must be \["Bridge"\] (Wormhole Core Bridge program).
+ #[account(mut)]
+ pub config: AccountInfo<'info>,
+
+ /// CHECK: Seeds must be \["Sequence"\, custodian] (Wormhole Core Bridge program).
+ #[account(mut)]
+ pub emitter_sequence: AccountInfo<'info>,
+
+ /// CHECK: Seeds must be \["fee_collector"\] (Wormhole Core Bridge program).
+ #[account(mut)]
+ pub fee_collector: AccountInfo<'info>,
+
+ pub core_bridge_program: Program<'info, core_bridge_program::CoreBridge>,
+}
+
+#[derive(Accounts)]
+pub struct CctpDepositForBurn<'info> {
+ pub burn_source: CctpMintRecipientMut<'info>,
+
+ /// Circle-supported mint.
+ ///
+ /// CHECK: Mutable. This token account's mint must be the same as the one found in the CCTP
+ /// Token Messenger Minter program's local token account.
+ #[account(mut)]
+ pub mint: AccountInfo<'info>,
+
+ /// CHECK: Seeds must be \["sender_authority"\] (CCTP Token Messenger Minter program).
+ pub token_messenger_minter_sender_authority: AccountInfo<'info>,
+
+ /// CHECK: Mutable. Seeds must be \["message_transmitter"\] (CCTP Message Transmitter program).
+ #[account(mut)]
+ pub message_transmitter_config: AccountInfo<'info>,
+
+ /// CHECK: Seeds must be \["token_messenger"\] (CCTP Token Messenger Minter program).
+ pub token_messenger: AccountInfo<'info>,
+
+ /// CHECK: Seeds must be \["remote_token_messenger"\, remote_domain.to_string()] (CCTP Token
+ /// Messenger Minter program).
+ pub remote_token_messenger: AccountInfo<'info>,
+
+ /// CHECK Seeds must be \["token_minter"\] (CCTP Token Messenger Minter program).
+ pub token_minter: AccountInfo<'info>,
+
+ /// Local token account, which this program uses to validate the `mint` used to burn.
+ ///
+ /// CHECK: Mutable. Seeds must be \["local_token", mint\] (CCTP Token Messenger Minter program).
+ #[account(mut)]
+ pub local_token: AccountInfo<'info>,
+
+ /// CHECK: Seeds must be \["__event_authority"\] (CCTP Token Messenger Minter program).
+ pub token_messenger_minter_event_authority: AccountInfo<'info>,
+
+ pub token_messenger_minter_program:
+ Program<'info, token_messenger_minter_program::TokenMessengerMinter>,
+ pub message_transmitter_program:
+ Program<'info, message_transmitter_program::MessageTransmitter>,
+}
+
+#[derive(Accounts)]
+pub struct CctpReceiveMessage<'info> {
+ pub mint_recipient: CctpMintRecipientMut<'info>,
+
+ /// CHECK: Seeds must be \["message_transmitter_authority"\] (CCTP Message Transmitter program).
+ pub message_transmitter_authority: AccountInfo<'info>,
+
+ /// CHECK: Seeds must be \["message_transmitter"\] (CCTP Message Transmitter program).
+ pub message_transmitter_config: AccountInfo<'info>,
+
+ /// CHECK: Mutable. Seeds must be \["used_nonces", remote_domain.to_string(),
+ /// first_nonce.to_string()\] (CCTP Message Transmitter program).
+ #[account(mut)]
+ pub used_nonces: AccountInfo<'info>,
+
+ /// CHECK: Seeds must be \["__event_authority"\] (CCTP Message Transmitter program)).
+ pub message_transmitter_event_authority: AccountInfo<'info>,
+
+ /// CHECK: Seeds must be \["token_messenger"\] (CCTP Token Messenger Minter program).
+ pub token_messenger: AccountInfo<'info>,
+
+ /// CHECK: Seeds must be \["remote_token_messenger"\, remote_domain.to_string()] (CCTP Token
+ /// Messenger Minter program).
+ pub remote_token_messenger: AccountInfo<'info>,
+
+ /// CHECK: Seeds must be \["token_minter"\] (CCTP Token Messenger Minter program).
+ pub token_minter: AccountInfo<'info>,
+
+ /// Token Messenger Minter's Local Token account. This program uses the mint of this account to
+ /// validate the `mint_recipient` token account's mint.
+ ///
+ /// CHECK: Mutable. Seeds must be \["local_token", mint\] (CCTP Token Messenger Minter program).
+ #[account(mut)]
+ pub local_token: AccountInfo<'info>,
+
+ /// CHECK: Seeds must be \["token_pair", remote_domain.to_string(), remote_token_address\] (CCTP
+ /// Token Messenger Minter program).
+ pub token_pair: AccountInfo<'info>,
+
+ /// CHECK: Mutable. Seeds must be \["custody", mint\] (CCTP Token Messenger Minter program).
+ #[account(mut)]
+ pub token_messenger_minter_custody_token: AccountInfo<'info>,
+
+ /// CHECK: Seeds must be \["__event_authority"\] (CCTP Token Messenger Minter program).
+ pub token_messenger_minter_event_authority: AccountInfo<'info>,
+
+ pub token_messenger_minter_program:
+ Program<'info, token_messenger_minter_program::TokenMessengerMinter>,
+ pub message_transmitter_program:
+ Program<'info, message_transmitter_program::MessageTransmitter>,
+}
+
+#[derive(Accounts)]
+pub struct ClosePreparedOrderResponse<'info> {
+ /// CHECK: Must equal the prepared_by field in the prepared order response.
+ #[account(
+ mut,
+ address = order_response.prepared_by,
+ )]
+ pub by: AccountInfo<'info>,
+
+ #[account(
+ mut,
+ close = by,
+ seeds = [
+ PreparedOrderResponse::SEED_PREFIX,
+ order_response.fast_vaa_hash.as_ref()
+ ],
+ bump = order_response.bump,
+ )]
+ pub order_response: Box>,
+
+ /// CHECK: Seeds must be \["prepared-custody"\, prepared_order_response.key()].
+ #[account(
+ mut,
+ seeds = [
+ crate::PREPARED_CUSTODY_TOKEN_SEED_PREFIX,
+ order_response.key().as_ref(),
+ ],
+ bump,
+ )]
+ pub custody_token: AccountInfo<'info>,
+}
+
+impl<'info> VaaDigest for ClosePreparedOrderResponse<'info> {
+ fn digest(&self) -> [u8; 32] {
+ self.order_response.fast_vaa_hash
+ }
+}
+
+/// NOTE: Keep this at the end in case Wormhole removes the need for these accounts.
+#[derive(Accounts)]
+pub struct RequiredSysvars<'info> {
+ /// Wormhole Core Bridge needs the clock sysvar based on its legacy implementation.
+ ///
+ /// CHECK: Must equal clock ID.
+ #[account(address = solana_program::sysvar::clock::id())]
+ pub clock: AccountInfo<'info>,
+
+ /// Wormhole Core Bridge needs the rent sysvar based on its legacy implementation.
+ ///
+ /// CHECK: Must equal rent ID.
+ #[account(address = solana_program::sysvar::rent::id())]
+ pub rent: AccountInfo<'info>,
+}
diff --git a/solana/programs/matching-engine/src/error.rs b/solana/programs/matching-engine/src/error.rs
index 7897b55fa..e11419779 100644
--- a/solana/programs/matching-engine/src/error.rs
+++ b/solana/programs/matching-engine/src/error.rs
@@ -11,9 +11,6 @@ pub enum MatchingEngineError {
#[msg("OwnerOrAssistantOnly")]
OwnerOrAssistantOnly = 0x4,
- #[msg("InvalidCustodyToken")]
- InvalidCustodyToken = 0x6,
-
#[msg("CpiDisallowed")]
CpiDisallowed = 0x8,
@@ -35,9 +32,6 @@ pub enum MatchingEngineError {
#[msg("ImmutableProgram")]
ImmutableProgram = 0x102,
- #[msg("NotUsdc")]
- NotUsdc = 0x103,
-
#[msg("InvalidNewOwner")]
InvalidNewOwner = 0x202,
@@ -146,8 +140,8 @@ pub enum MatchingEngineError {
#[msg("MismatchedVaaHash")]
MismatchedVaaHash,
- #[msg("BestOfferTokenMismatch")]
- BestOfferTokenMismatch,
+ #[msg("ExecutorTokenMismatch")]
+ ExecutorTokenMismatch,
#[msg("InitialOfferTokenMismatch")]
InitialOfferTokenMismatch,
diff --git a/solana/programs/matching-engine/src/lib.rs b/solana/programs/matching-engine/src/lib.rs
index 1b145c988..5f454a09e 100644
--- a/solana/programs/matching-engine/src/lib.rs
+++ b/solana/programs/matching-engine/src/lib.rs
@@ -3,10 +3,12 @@
pub mod cctp_mint_recipient;
-pub mod error;
+mod composite;
+
+mod error;
mod processor;
-pub(crate) use processor::*;
+use processor::*;
pub mod state;
@@ -25,6 +27,10 @@ cfg_if::cfg_if! {
}
}
+const AUCTION_CUSTODY_TOKEN_SEED_PREFIX: &[u8] = b"auction-custody";
+const LOCAL_CUSTODY_TOKEN_SEED_PREFIX: &[u8] = b"local-custody";
+const PREPARED_CUSTODY_TOKEN_SEED_PREFIX: &[u8] = b"prepared-custody";
+
#[program]
pub mod matching_engine {
use super::*;
@@ -52,14 +58,6 @@ pub mod matching_engine {
processor::settle_auction_none_local(ctx)
}
- pub fn settle_auction_active_cctp(ctx: Context) -> Result<()> {
- processor::settle_auction_active_cctp(ctx)
- }
-
- pub fn settle_auction_active_local(ctx: Context) -> Result<()> {
- processor::settle_auction_active_local(ctx)
- }
-
/// This instruction is be used to generate your program's config.
/// And for convenience, we will store Wormhole-related PDAs in the
/// config so we can verify these accounts with a simple == constraint.
diff --git a/solana/programs/matching-engine/src/processor/admin/close_proposal.rs b/solana/programs/matching-engine/src/processor/admin/close_proposal.rs
index 6c5146615..a61148b1a 100644
--- a/solana/programs/matching-engine/src/processor/admin/close_proposal.rs
+++ b/solana/programs/matching-engine/src/processor/admin/close_proposal.rs
@@ -1,9 +1,9 @@
-use crate::state::{custodian::*, Proposal};
+use crate::{composite::*, state::Proposal};
use anchor_lang::prelude::*;
#[derive(Accounts)]
pub struct CloseProposal<'info> {
- admin: OwnerCustodian<'info>,
+ admin: OwnerOnly<'info>,
/// CHECK: This account must equal proposal.by pubkey.
#[account(
@@ -17,7 +17,7 @@ pub struct CloseProposal<'info> {
close = proposed_by,
seeds = [
Proposal::SEED_PREFIX,
- proposal.id.to_be_bytes().as_ref(),
+ &proposal.id.to_be_bytes(),
],
bump = proposal.bump,
constraint = proposal.slot_enacted_at.is_none() @ ErrorCode::InstructionMissing, // TODO: add err ProposalAlreadyEnacted
diff --git a/solana/programs/matching-engine/src/processor/admin/initialize.rs b/solana/programs/matching-engine/src/processor/admin/initialize.rs
index fca48a9c2..19930bc22 100644
--- a/solana/programs/matching-engine/src/processor/admin/initialize.rs
+++ b/solana/programs/matching-engine/src/processor/admin/initialize.rs
@@ -1,4 +1,5 @@
use crate::{
+ composite::*,
error::MatchingEngineError,
state::{AuctionConfig, Custodian},
};
@@ -32,7 +33,7 @@ pub struct Initialize<'info> {
space = 8 + AuctionConfig::INIT_SPACE,
seeds = [
AuctionConfig::SEED_PREFIX,
- u32::default().to_be_bytes().as_ref()
+ &u32::default().to_be_bytes()
],
bump,
)]
@@ -58,22 +59,21 @@ pub struct Initialize<'info> {
fee_recipient: AccountInfo<'info>,
#[account(
- associated_token::mint = mint,
+ associated_token::mint = usdc,
associated_token::authority = fee_recipient,
)]
fee_recipient_token: Account<'info, token::TokenAccount>,
#[account(
- init,
+ init_if_needed,
payer = owner,
- associated_token::mint = mint,
+ associated_token::mint = usdc,
associated_token::authority = custodian,
- address = crate::cctp_mint_recipient::id() @ MatchingEngineError::InvalidCustodyToken,
+ address = crate::cctp_mint_recipient::id(),
)]
cctp_mint_recipient: Account<'info, token::TokenAccount>,
- #[account(address = common::constants::USDC_MINT @ MatchingEngineError::NotUsdc)]
- mint: Account<'info, token::Mint>,
+ usdc: Usdc<'info>,
/// We use the program data to make sure this owner is the upgrade authority (the true owner,
/// who deployed this program).
diff --git a/solana/programs/matching-engine/src/processor/admin/ownership_transfer_request/cancel.rs b/solana/programs/matching-engine/src/processor/admin/ownership_transfer_request/cancel.rs
index d4d2fc0a7..372605464 100644
--- a/solana/programs/matching-engine/src/processor/admin/ownership_transfer_request/cancel.rs
+++ b/solana/programs/matching-engine/src/processor/admin/ownership_transfer_request/cancel.rs
@@ -1,9 +1,9 @@
-use crate::state::custodian::*;
+use crate::composite::*;
use anchor_lang::prelude::*;
#[derive(Accounts)]
pub struct CancelOwnershipTransferRequest<'info> {
- admin: OwnerMutCustodian<'info>,
+ admin: OwnerOnlyMut<'info>,
}
pub fn cancel_ownership_transfer_request(
diff --git a/solana/programs/matching-engine/src/processor/admin/ownership_transfer_request/submit.rs b/solana/programs/matching-engine/src/processor/admin/ownership_transfer_request/submit.rs
index cb33003d4..4eec99b80 100644
--- a/solana/programs/matching-engine/src/processor/admin/ownership_transfer_request/submit.rs
+++ b/solana/programs/matching-engine/src/processor/admin/ownership_transfer_request/submit.rs
@@ -1,9 +1,9 @@
-use crate::{error::MatchingEngineError, state::custodian::*};
+use crate::{composite::*, error::MatchingEngineError};
use anchor_lang::prelude::*;
#[derive(Accounts)]
pub struct SubmitOwnershipTransferRequest<'info> {
- admin: OwnerMutCustodian<'info>,
+ admin: OwnerOnlyMut<'info>,
/// New Owner.
///
diff --git a/solana/programs/matching-engine/src/processor/admin/propose/auction_parameters.rs b/solana/programs/matching-engine/src/processor/admin/propose/auction_parameters.rs
index 6869e733a..4d69904ee 100644
--- a/solana/programs/matching-engine/src/processor/admin/propose/auction_parameters.rs
+++ b/solana/programs/matching-engine/src/processor/admin/propose/auction_parameters.rs
@@ -1,4 +1,7 @@
-use crate::state::{custodian::*, AuctionParameters, Proposal, ProposalAction};
+use crate::{
+ composite::*,
+ state::{AuctionParameters, Proposal, ProposalAction},
+};
use anchor_lang::prelude::*;
#[derive(Accounts)]
@@ -6,7 +9,7 @@ pub struct ProposeAuctionParameters<'info> {
#[account(mut)]
payer: Signer<'info>,
- admin: AdminMutCustodian<'info>,
+ admin: AdminMut<'info>,
#[account(
init,
@@ -14,7 +17,7 @@ pub struct ProposeAuctionParameters<'info> {
space = 8 + Proposal::INIT_SPACE,
seeds = [
Proposal::SEED_PREFIX,
- admin.custodian.next_proposal_id.to_be_bytes().as_ref()
+ &admin.custodian.next_proposal_id.to_be_bytes()
],
bump,
)]
diff --git a/solana/programs/matching-engine/src/processor/admin/router_endpoint/add/cctp.rs b/solana/programs/matching-engine/src/processor/admin/router_endpoint/add/cctp.rs
index 0def9b781..f9ea47eb0 100644
--- a/solana/programs/matching-engine/src/processor/admin/router_endpoint/add/cctp.rs
+++ b/solana/programs/matching-engine/src/processor/admin/router_endpoint/add/cctp.rs
@@ -1,11 +1,14 @@
use crate::{
- state::{custodian::*, router_endpoint::*},
+ composite::*,
+ state::RouterEndpoint,
utils::{self, admin::AddCctpRouterEndpointArgs},
};
use anchor_lang::prelude::*;
+use anchor_spl::token;
use common::wormhole_cctp_solana::{
cctp::token_messenger_minter_program::{self, RemoteTokenMessenger},
utils::ExternalAccount,
+ wormhole::SOLANA_CHAIN,
};
#[derive(Accounts)]
@@ -14,7 +17,7 @@ pub struct AddCctpRouterEndpoint<'info> {
#[account(mut)]
payer: Signer<'info>,
- admin: AdminCustodian<'info>,
+ admin: Admin<'info>,
#[account(
init,
@@ -28,6 +31,36 @@ pub struct AddCctpRouterEndpoint<'info> {
)]
router_endpoint: Account<'info, RouterEndpoint>,
+ /// Local router endpoint PDA.
+ ///
+ /// NOTE: This account may not exist yet. But we need to pass it since it will be the owner of
+ /// the local custody token account.
+ ///
+ /// CHECK: Seeds must be \["endpoint", SOLANA_CHAIN.to_be_bytes()].
+ #[account(
+ seeds = [
+ RouterEndpoint::SEED_PREFIX,
+ &SOLANA_CHAIN.to_be_bytes()
+ ],
+ bump,
+ )]
+ local_router_endpoint: AccountInfo<'info>,
+
+ #[account(
+ init,
+ payer = payer,
+ token::mint = usdc,
+ token::authority = local_router_endpoint,
+ seeds = [
+ crate::LOCAL_CUSTODY_TOKEN_SEED_PREFIX,
+ &args.chain.to_be_bytes(),
+ ],
+ bump,
+ )]
+ local_custody_token: Box>,
+
+ usdc: Usdc<'info>,
+
/// CHECK: Seeds must be \["remote_token_messenger"\, remote_domain.to_string()] (CCTP Token
/// Messenger Minter program).
#[account(
@@ -40,6 +73,7 @@ pub struct AddCctpRouterEndpoint<'info> {
)]
remote_token_messenger: Account<'info, ExternalAccount>,
+ token_program: Program<'info, token::Token>,
system_program: Program<'info, System>,
}
diff --git a/solana/programs/matching-engine/src/processor/admin/router_endpoint/add/local.rs b/solana/programs/matching-engine/src/processor/admin/router_endpoint/add/local.rs
index 5e484cf94..cb714d2e8 100644
--- a/solana/programs/matching-engine/src/processor/admin/router_endpoint/add/local.rs
+++ b/solana/programs/matching-engine/src/processor/admin/router_endpoint/add/local.rs
@@ -1,8 +1,4 @@
-use crate::{
- processor::admin::router_endpoint::local_token_router::*,
- state::{custodian::*, RouterEndpoint},
- utils,
-};
+use crate::{composite::*, state::RouterEndpoint, utils};
use anchor_lang::prelude::*;
use common::wormhole_cctp_solana::wormhole::SOLANA_CHAIN;
@@ -11,7 +7,7 @@ pub struct AddLocalRouterEndpoint<'info> {
#[account(mut)]
payer: Signer<'info>,
- admin: AdminCustodian<'info>,
+ admin: Admin<'info>,
#[account(
init,
diff --git a/solana/programs/matching-engine/src/processor/admin/router_endpoint/disable.rs b/solana/programs/matching-engine/src/processor/admin/router_endpoint/disable.rs
index 249c28747..ef5c1c5cf 100644
--- a/solana/programs/matching-engine/src/processor/admin/router_endpoint/disable.rs
+++ b/solana/programs/matching-engine/src/processor/admin/router_endpoint/disable.rs
@@ -1,15 +1,15 @@
-use crate::state::{custodian::*, router_endpoint::*, MessageProtocol};
+use crate::{composite::*, state::MessageProtocol};
use anchor_lang::prelude::*;
#[derive(Accounts)]
pub struct DisableRouterEndpoint<'info> {
- admin: OwnerCustodian<'info>,
+ admin: OwnerOnly<'info>,
router_endpoint: ExistingMutRouterEndpoint<'info>,
}
pub fn disable_router_endpoint(ctx: Context) -> Result<()> {
- let endpoint = &mut ctx.accounts.router_endpoint.inner;
+ let endpoint = &mut ctx.accounts.router_endpoint;
endpoint.protocol = MessageProtocol::None;
endpoint.address = Default::default();
endpoint.mint_recipient = Default::default();
diff --git a/solana/programs/matching-engine/src/processor/admin/router_endpoint/mod.rs b/solana/programs/matching-engine/src/processor/admin/router_endpoint/mod.rs
index 7eb6dba78..5cb1fe565 100644
--- a/solana/programs/matching-engine/src/processor/admin/router_endpoint/mod.rs
+++ b/solana/programs/matching-engine/src/processor/admin/router_endpoint/mod.rs
@@ -6,32 +6,3 @@ pub use disable::*;
mod update;
pub use update::*;
-
-pub mod local_token_router {
- use crate::error::MatchingEngineError;
- use anchor_lang::prelude::*;
-
- #[derive(Accounts)]
- pub struct LocalTokenRouter<'info> {
- /// CHECK: Must be an executable (the Token Router program), whose ID will be used to derive the
- /// emitter (router endpoint) address.
- #[account(executable)]
- pub token_router_program: AccountInfo<'info>,
-
- /// CHECK: The Token Router program's emitter PDA (a.k.a. its custodian) will have account data.
- #[account(
- seeds = [b"emitter"],
- bump,
- seeds::program = token_router_program,
- owner = token_router_program.key() @ MatchingEngineError::InvalidEndpoint,
- constraint = !token_router_emitter.data_is_empty() @ MatchingEngineError::InvalidEndpoint,
- )]
- pub token_router_emitter: AccountInfo<'info>,
-
- #[account(
- associated_token::mint = common::constants::USDC_MINT,
- associated_token::authority = token_router_emitter,
- )]
- pub token_router_mint_recipient: Account<'info, anchor_spl::token::TokenAccount>,
- }
-}
diff --git a/solana/programs/matching-engine/src/processor/admin/router_endpoint/update/cctp.rs b/solana/programs/matching-engine/src/processor/admin/router_endpoint/update/cctp.rs
index 4b9450b51..2970ca6c3 100644
--- a/solana/programs/matching-engine/src/processor/admin/router_endpoint/update/cctp.rs
+++ b/solana/programs/matching-engine/src/processor/admin/router_endpoint/update/cctp.rs
@@ -1,5 +1,5 @@
use crate::{
- state::{custodian::*, router_endpoint::*},
+ composite::*,
utils::{self, admin::AddCctpRouterEndpointArgs},
};
use anchor_lang::prelude::*;
@@ -11,7 +11,7 @@ use common::wormhole_cctp_solana::{
#[derive(Accounts)]
#[instruction(args: AddCctpRouterEndpointArgs)]
pub struct UpdateCctpRouterEndpoint<'info> {
- admin: OwnerCustodian<'info>,
+ admin: OwnerOnly<'info>,
router_endpoint: ExistingMutRouterEndpoint<'info>,
@@ -32,9 +32,5 @@ pub fn update_cctp_router_endpoint(
ctx: Context,
args: AddCctpRouterEndpointArgs,
) -> Result<()> {
- utils::admin::handle_add_cctp_router_endpoint(
- &mut ctx.accounts.router_endpoint.inner,
- args,
- None,
- )
+ utils::admin::handle_add_cctp_router_endpoint(&mut ctx.accounts.router_endpoint, args, None)
}
diff --git a/solana/programs/matching-engine/src/processor/admin/router_endpoint/update/local.rs b/solana/programs/matching-engine/src/processor/admin/router_endpoint/update/local.rs
index e7fda5c46..edfa11511 100644
--- a/solana/programs/matching-engine/src/processor/admin/router_endpoint/update/local.rs
+++ b/solana/programs/matching-engine/src/processor/admin/router_endpoint/update/local.rs
@@ -1,20 +1,15 @@
-use crate::{
- error::MatchingEngineError,
- processor::admin::router_endpoint::local_token_router::*,
- state::{custodian::*, router_endpoint::*},
- utils,
-};
+use crate::{composite::*, error::MatchingEngineError, utils};
use anchor_lang::prelude::*;
use common::wormhole_cctp_solana::wormhole::SOLANA_CHAIN;
#[derive(Accounts)]
pub struct UpdateLocalRouterEndpoint<'info> {
- admin: OwnerCustodian<'info>,
+ admin: OwnerOnly<'info>,
#[account(
constraint = {
require_eq!(
- router_endpoint.inner.chain,
+ router_endpoint.chain,
SOLANA_CHAIN,
MatchingEngineError::InvalidChain
);
@@ -28,7 +23,7 @@ pub struct UpdateLocalRouterEndpoint<'info> {
pub fn update_local_router_endpoint(ctx: Context) -> Result<()> {
utils::admin::handle_add_local_router_endpoint(
- &mut ctx.accounts.router_endpoint.inner,
+ &mut ctx.accounts.router_endpoint,
&ctx.accounts.local.token_router_program,
&ctx.accounts.local.token_router_emitter,
&ctx.accounts.local.token_router_mint_recipient,
diff --git a/solana/programs/matching-engine/src/processor/admin/update/auction_parameters.rs b/solana/programs/matching-engine/src/processor/admin/update/auction_parameters.rs
index fef29551f..259792f00 100644
--- a/solana/programs/matching-engine/src/processor/admin/update/auction_parameters.rs
+++ b/solana/programs/matching-engine/src/processor/admin/update/auction_parameters.rs
@@ -1,6 +1,6 @@
use crate::{
+ composite::*,
error::MatchingEngineError,
- state::custodian::*,
state::{AuctionConfig, Proposal, ProposalAction},
};
use anchor_lang::prelude::*;
@@ -10,13 +10,13 @@ pub struct UpdateAuctionParameters<'info> {
#[account(mut)]
payer: Signer<'info>,
- admin: OwnerMutCustodian<'info>,
+ admin: OwnerOnlyMut<'info>,
#[account(
mut,
seeds = [
Proposal::SEED_PREFIX,
- proposal.id.to_be_bytes().as_ref(),
+ &proposal.id.to_be_bytes(),
],
bump = proposal.bump,
constraint = {
diff --git a/solana/programs/matching-engine/src/processor/admin/update/fee_recipient_token.rs b/solana/programs/matching-engine/src/processor/admin/update/fee_recipient_token.rs
index a3f66124c..68a87a340 100644
--- a/solana/programs/matching-engine/src/processor/admin/update/fee_recipient_token.rs
+++ b/solana/programs/matching-engine/src/processor/admin/update/fee_recipient_token.rs
@@ -1,24 +1,10 @@
-use crate::{error::MatchingEngineError, state::Custodian};
+use crate::{composite::*, error::MatchingEngineError};
use anchor_lang::prelude::*;
use anchor_spl::token;
-use common::admin::utils::assistant::only_authorized;
#[derive(Accounts)]
pub struct UpdateFeeRecipient<'info> {
- #[account(
- mut,
- constraint = {
- only_authorized(&custodian, &owner_or_assistant.key())
- } @ MatchingEngineError::OwnerOrAssistantOnly,
- )]
- owner_or_assistant: Signer<'info>,
-
- #[account(
- mut,
- seeds = [Custodian::SEED_PREFIX],
- bump = Custodian::BUMP,
- )]
- custodian: Account<'info, Custodian>,
+ admin: AdminMut<'info>,
#[account(
associated_token::mint = common::constants::USDC_MINT,
@@ -39,7 +25,7 @@ pub struct UpdateFeeRecipient<'info> {
pub fn update_fee_recipient(ctx: Context) -> Result<()> {
// Update the fee_recipient key.
- ctx.accounts.custodian.fee_recipient_token = ctx.accounts.new_fee_recipient_token.key();
+ ctx.accounts.admin.custodian.fee_recipient_token = ctx.accounts.new_fee_recipient_token.key();
Ok(())
}
diff --git a/solana/programs/matching-engine/src/processor/admin/update/owner_assistant.rs b/solana/programs/matching-engine/src/processor/admin/update/owner_assistant.rs
index 1aa7a27d6..2d80271a1 100644
--- a/solana/programs/matching-engine/src/processor/admin/update/owner_assistant.rs
+++ b/solana/programs/matching-engine/src/processor/admin/update/owner_assistant.rs
@@ -1,19 +1,10 @@
-use crate::{error::MatchingEngineError, state::Custodian};
+use crate::{composite::*, error::MatchingEngineError};
use anchor_lang::prelude::*;
use common::admin::utils::assistant;
#[derive(Accounts)]
pub struct UpdateOwnerAssistant<'info> {
- /// Owner of the program set in the [`OwnerConfig`] account.
- owner: Signer<'info>,
-
- #[account(
- mut,
- seeds = [Custodian::SEED_PREFIX],
- bump = Custodian::BUMP,
- has_one = owner @ MatchingEngineError::OwnerOnly,
- )]
- custodian: Account<'info, Custodian>,
+ admin: OwnerOnlyMut<'info>,
/// New Assistant.
///
@@ -28,8 +19,8 @@ pub struct UpdateOwnerAssistant<'info> {
pub fn update_owner_assistant(ctx: Context) -> Result<()> {
assistant::transfer_owner_assistant(
- &mut ctx.accounts.custodian,
- &ctx.accounts.new_owner_assistant.key(),
+ &mut ctx.accounts.admin.custodian,
+ &ctx.accounts.new_owner_assistant,
);
// Done.
diff --git a/solana/programs/matching-engine/src/processor/auction/execute_fast_order/cctp.rs b/solana/programs/matching-engine/src/processor/auction/execute_fast_order/cctp.rs
index 5a2fa57f6..00dbd4f78 100644
--- a/solana/programs/matching-engine/src/processor/auction/execute_fast_order/cctp.rs
+++ b/solana/programs/matching-engine/src/processor/auction/execute_fast_order/cctp.rs
@@ -1,18 +1,11 @@
use crate::{
+ composite::*,
error::MatchingEngineError,
- state::{Auction, AuctionConfig, Custodian, MessageProtocol, PayerSequence, RouterEndpoint},
- utils,
+ state::{Auction, Custodian, MessageProtocol, PayerSequence},
};
use anchor_lang::prelude::*;
use anchor_spl::token;
-use common::{
- wormhole_cctp_solana::{
- self,
- cctp::{message_transmitter_program, token_messenger_minter_program},
- wormhole::{core_bridge_program, VaaAccount},
- },
- wormhole_io::TypePrefixedPayload,
-};
+use common::{wormhole_cctp_solana, wormhole_io::TypePrefixedPayload};
/// Accounts required for [execute_fast_order_cctp].
#[derive(Accounts)]
@@ -32,88 +25,13 @@ pub struct ExecuteFastOrderCctp<'info> {
)]
payer_sequence: Account<'info, PayerSequence>,
- /// This program's Wormhole (Core Bridge) emitter authority. This is also the burn-source
- /// authority for CCTP transfers.
- ///
- /// CHECK: Seeds must be \["emitter"\].
- #[account(
- seeds = [Custodian::SEED_PREFIX],
- bump = Custodian::BUMP,
- )]
- custodian: AccountInfo<'info>,
-
- auction_config: Box>,
-
- /// CHECK: Must be owned by the Wormhole Core Bridge program.
- #[account(owner = core_bridge_program::id())]
- fast_vaa: AccountInfo<'info>,
-
- #[account(
- mut,
- seeds = [
- Auction::SEED_PREFIX,
- VaaAccount::load(&fast_vaa)?.digest().as_ref()
- ],
- bump = auction.bump,
- constraint = utils::is_valid_active_auction(
- &auction_config,
- &auction,
- Some(best_offer_token.key()),
- Some(initial_offer_token.key()),
- )?
- )]
- auction: Box>,
-
- #[account(
- seeds = [
- RouterEndpoint::SEED_PREFIX,
- to_router_endpoint.chain.to_be_bytes().as_ref(),
- ],
- bump = to_router_endpoint.bump,
- )]
- to_router_endpoint: Account<'info, RouterEndpoint>,
-
- #[account(
- mut,
- token::mint = mint,
- )]
- executor_token: Box>,
-
- /// CHECK: Mutable. Must equal [best_offer](Auction::best_offer).
- #[account(mut)]
- best_offer_token: AccountInfo<'info>,
-
- /// CHECK: Mutable. Must equal [initial_offer](Auction::initial_offer).
- #[account(mut)]
- initial_offer_token: AccountInfo<'info>,
-
- /// Also the burn_source token account.
- ///
- /// CHECK: Mutable. Seeds must be \["custody"\].
- #[account(
- mut,
- address = crate::cctp_mint_recipient::id() @ MatchingEngineError::InvalidCustodyToken,
- )]
- cctp_mint_recipient: AccountInfo<'info>,
-
- /// Circle-supported mint.
- ///
- /// CHECK: Mutable. This token account's mint must be the same as the one found in the CCTP
- /// Token Messenger Minter program's local token account.
- #[account(mut)]
- mint: AccountInfo<'info>,
-
- /// CHECK: Seeds must be \["Bridge"\] (Wormhole Core Bridge program).
- #[account(mut)]
- core_bridge_config: UncheckedAccount<'info>,
-
/// CHECK: Mutable. Seeds must be \["core-msg", payer, payer_sequence.value\].
#[account(
mut,
seeds = [
common::constants::CORE_MESSAGE_SEED_PREFIX,
payer.key().as_ref(),
- payer_sequence.value.to_be_bytes().as_ref(),
+ &payer_sequence.value.to_be_bytes(),
],
bump,
)]
@@ -125,65 +43,29 @@ pub struct ExecuteFastOrderCctp<'info> {
seeds = [
common::constants::CCTP_MESSAGE_SEED_PREFIX,
payer.key().as_ref(),
- payer_sequence.value.to_be_bytes().as_ref(),
+ &payer_sequence.value.to_be_bytes(),
],
bump,
)]
cctp_message: AccountInfo<'info>,
- /// CHECK: Seeds must be \["Sequence"\, custodian] (Wormhole Core Bridge program).
- #[account(mut)]
- core_emitter_sequence: UncheckedAccount<'info>,
-
- /// CHECK: Seeds must be \["fee_collector"\] (Wormhole Core Bridge program).
- #[account(mut)]
- core_fee_collector: UncheckedAccount<'info>,
-
- /// CHECK: Seeds must be \["sender_authority"\] (CCTP Token Messenger Minter program).
- token_messenger_minter_sender_authority: UncheckedAccount<'info>,
-
- /// CHECK: Mutable. Seeds must be \["message_transmitter"\] (CCTP Message Transmitter program).
- #[account(mut)]
- message_transmitter_config: UncheckedAccount<'info>,
-
- /// CHECK: Seeds must be \["token_messenger"\] (CCTP Token Messenger Minter program).
- token_messenger: UncheckedAccount<'info>,
-
- /// CHECK: Seeds must be \["remote_token_messenger"\, remote_domain.to_string()] (CCTP Token
- /// Messenger Minter program).
- remote_token_messenger: UncheckedAccount<'info>,
+ custodian: CheckedCustodian<'info>,
- /// CHECK Seeds must be \["token_minter"\] (CCTP Token Messenger Minter program).
- token_minter: UncheckedAccount<'info>,
+ execute_order: ExecuteOrder<'info>,
- /// Local token account, which this program uses to validate the `mint` used to burn.
- ///
- /// CHECK: Mutable. Seeds must be \["local_token", mint\] (CCTP Token Messenger Minter program).
- #[account(mut)]
- local_token: UncheckedAccount<'info>,
+ wormhole: WormholePublishMessage<'info>,
- /// CHECK: Seeds must be \["__event_authority"\] (CCTP Token Messenger Minter program).
- token_messenger_minter_event_authority: UncheckedAccount<'info>,
+ cctp: CctpDepositForBurn<'info>,
- core_bridge_program: Program<'info, core_bridge_program::CoreBridge>,
- token_messenger_minter_program:
- Program<'info, token_messenger_minter_program::TokenMessengerMinter>,
- message_transmitter_program: Program<'info, message_transmitter_program::MessageTransmitter>,
system_program: Program<'info, System>,
token_program: Program<'info, token::Token>,
- /// CHECK: Wormhole Core Bridge needs the clock sysvar based on its legacy implementation.
- #[account(address = solana_program::sysvar::clock::id())]
- clock: AccountInfo<'info>,
-
- /// CHECK: Wormhole Core Bridge needs the rent sysvar based on its legacy implementation.
- #[account(address = solana_program::sysvar::rent::id())]
- rent: AccountInfo<'info>,
+ sysvars: RequiredSysvars<'info>,
}
/// TODO: add docstring
pub fn execute_fast_order_cctp(ctx: Context) -> Result<()> {
- match ctx.accounts.to_router_endpoint.protocol {
+ match ctx.accounts.execute_order.to_router_endpoint.protocol {
MessageProtocol::Cctp { domain } => handle_execute_fast_order_cctp(ctx, domain),
_ => err!(MatchingEngineError::InvalidCctpEndpoint),
}
@@ -193,20 +75,14 @@ pub fn handle_execute_fast_order_cctp(
ctx: Context,
destination_cctp_domain: u32,
) -> Result<()> {
- let super::PreparedFastExecution {
+ let super::PreparedOrderExecution {
user_amount: amount,
fill,
sequence_seed,
- } = super::prepare_fast_execution(super::PrepareFastExecution {
- custodian: &ctx.accounts.custodian,
- auction_config: &ctx.accounts.auction_config,
- fast_vaa: &ctx.accounts.fast_vaa,
- auction: &mut ctx.accounts.auction,
- cctp_mint_recipient: &ctx.accounts.cctp_mint_recipient,
- executor_token: &ctx.accounts.executor_token,
- best_offer_token: &ctx.accounts.best_offer_token,
- initial_offer_token: &ctx.accounts.initial_offer_token,
+ } = super::prepare_order_execution(super::PrepareFastExecution {
+ execute_order: &mut ctx.accounts.execute_order,
payer_sequence: &mut ctx.accounts.payer_sequence,
+ dst_token: &ctx.accounts.cctp.burn_source,
token_program: &ctx.accounts.token_program,
})?;
@@ -214,6 +90,7 @@ pub fn handle_execute_fast_order_cctp(
wormhole_cctp_solana::cpi::burn_and_publish(
CpiContext::new_with_signer(
ctx.accounts
+ .cctp
.token_messenger_minter_program
.to_account_info(),
wormhole_cctp_solana::cpi::DepositForBurnWithCaller {
@@ -221,31 +98,36 @@ pub fn handle_execute_fast_order_cctp(
payer: ctx.accounts.payer.to_account_info(),
token_messenger_minter_sender_authority: ctx
.accounts
+ .cctp
.token_messenger_minter_sender_authority
.to_account_info(),
- burn_token: ctx.accounts.cctp_mint_recipient.to_account_info(),
+ burn_token: ctx.accounts.cctp.burn_source.to_account_info(),
message_transmitter_config: ctx
.accounts
+ .cctp
.message_transmitter_config
.to_account_info(),
- token_messenger: ctx.accounts.token_messenger.to_account_info(),
- remote_token_messenger: ctx.accounts.remote_token_messenger.to_account_info(),
- token_minter: ctx.accounts.token_minter.to_account_info(),
- local_token: ctx.accounts.local_token.to_account_info(),
- mint: ctx.accounts.mint.to_account_info(),
+ token_messenger: ctx.accounts.cctp.token_messenger.to_account_info(),
+ remote_token_messenger: ctx.accounts.cctp.remote_token_messenger.to_account_info(),
+ token_minter: ctx.accounts.cctp.token_minter.to_account_info(),
+ local_token: ctx.accounts.cctp.local_token.to_account_info(),
+ mint: ctx.accounts.cctp.mint.to_account_info(),
cctp_message: ctx.accounts.cctp_message.to_account_info(),
message_transmitter_program: ctx
.accounts
+ .cctp
.message_transmitter_program
.to_account_info(),
token_messenger_minter_program: ctx
.accounts
+ .cctp
.token_messenger_minter_program
.to_account_info(),
token_program: ctx.accounts.token_program.to_account_info(),
system_program: ctx.accounts.system_program.to_account_info(),
event_authority: ctx
.accounts
+ .cctp
.token_messenger_minter_event_authority
.to_account_info(),
},
@@ -260,17 +142,17 @@ pub fn handle_execute_fast_order_cctp(
],
),
CpiContext::new_with_signer(
- ctx.accounts.core_bridge_program.to_account_info(),
+ ctx.accounts.wormhole.core_bridge_program.to_account_info(),
wormhole_cctp_solana::cpi::PostMessage {
payer: ctx.accounts.payer.to_account_info(),
message: ctx.accounts.core_message.to_account_info(),
emitter: ctx.accounts.custodian.to_account_info(),
- config: ctx.accounts.core_bridge_config.to_account_info(),
- emitter_sequence: ctx.accounts.core_emitter_sequence.to_account_info(),
- fee_collector: ctx.accounts.core_fee_collector.to_account_info(),
+ config: ctx.accounts.wormhole.config.to_account_info(),
+ emitter_sequence: ctx.accounts.wormhole.emitter_sequence.to_account_info(),
+ fee_collector: ctx.accounts.wormhole.fee_collector.to_account_info(),
system_program: ctx.accounts.system_program.to_account_info(),
- clock: ctx.accounts.clock.to_account_info(),
- rent: ctx.accounts.rent.to_account_info(),
+ clock: ctx.accounts.sysvars.clock.to_account_info(),
+ rent: ctx.accounts.sysvars.rent.to_account_info(),
},
&[
Custodian::SIGNER_SEEDS,
@@ -284,14 +166,37 @@ pub fn handle_execute_fast_order_cctp(
),
wormhole_cctp_solana::cpi::BurnAndPublishArgs {
burn_source: None,
- destination_caller: ctx.accounts.to_router_endpoint.address,
+ destination_caller: ctx.accounts.execute_order.to_router_endpoint.address,
destination_cctp_domain,
amount,
- mint_recipient: ctx.accounts.to_router_endpoint.mint_recipient,
+ mint_recipient: ctx.accounts.execute_order.to_router_endpoint.mint_recipient,
wormhole_message_nonce: common::constants::WORMHOLE_MESSAGE_NONCE,
payload: fill.to_vec_payload(),
},
)?;
- Ok(())
+ // Finally close the account since it is no longer needed.
+ token::close_account(CpiContext::new_with_signer(
+ ctx.accounts.token_program.to_account_info(),
+ token::CloseAccount {
+ account: ctx
+ .accounts
+ .execute_order
+ .active_auction
+ .custody_token
+ .to_account_info(),
+ destination: ctx.accounts.payer.to_account_info(),
+ authority: ctx
+ .accounts
+ .execute_order
+ .active_auction
+ .auction
+ .to_account_info(),
+ },
+ &[&[
+ Auction::SEED_PREFIX,
+ ctx.accounts.execute_order.active_auction.vaa_hash.as_ref(),
+ &[ctx.accounts.execute_order.active_auction.bump],
+ ]],
+ ))
}
diff --git a/solana/programs/matching-engine/src/processor/auction/execute_fast_order/local.rs b/solana/programs/matching-engine/src/processor/auction/execute_fast_order/local.rs
index 0deeafa50..610ce0049 100644
--- a/solana/programs/matching-engine/src/processor/auction/execute_fast_order/local.rs
+++ b/solana/programs/matching-engine/src/processor/auction/execute_fast_order/local.rs
@@ -1,14 +1,10 @@
use crate::{
- error::MatchingEngineError,
- state::{Auction, AuctionConfig, Custodian, MessageProtocol, PayerSequence, RouterEndpoint},
+ composite::*,
+ state::{Auction, PayerSequence},
utils,
};
use anchor_lang::prelude::*;
use anchor_spl::token;
-use common::{
- wormhole_cctp_solana::wormhole::{core_bridge_program, VaaAccount, SOLANA_CHAIN},
- wormhole_io::TypePrefixedPayload,
-};
#[derive(Accounts)]
pub struct ExecuteFastOrderLocal<'info> {
@@ -27,155 +23,93 @@ pub struct ExecuteFastOrderLocal<'info> {
)]
payer_sequence: Account<'info, PayerSequence>,
- /// This program's Wormhole (Core Bridge) emitter authority. This is also the burn-source
- /// authority for CCTP transfers.
- ///
- /// CHECK: Seeds must be \["emitter"\].
- #[account(
- seeds = [Custodian::SEED_PREFIX],
- bump = Custodian::BUMP,
- )]
- custodian: AccountInfo<'info>,
-
- auction_config: Box>,
-
- /// CHECK: Must be owned by the Wormhole Core Bridge program.
- #[account(owner = core_bridge_program::id())]
- fast_vaa: AccountInfo<'info>,
-
+ /// CHECK: Mutable. Seeds must be \["msg", payer, payer_sequence.value\].
#[account(
mut,
seeds = [
- Auction::SEED_PREFIX,
- VaaAccount::load(&fast_vaa)?.digest().as_ref()
- ],
- bump = auction.bump,
- constraint = utils::is_valid_active_auction(
- &auction_config,
- &auction,
- Some(best_offer_token.key()),
- Some(initial_offer_token.key()),
- )?
- )]
- auction: Box>,
-
- #[account(
- seeds = [
- RouterEndpoint::SEED_PREFIX,
- SOLANA_CHAIN.to_be_bytes().as_ref(),
+ common::constants::CORE_MESSAGE_SEED_PREFIX,
+ payer.key().as_ref(),
+ &payer_sequence.value.to_be_bytes(),
],
- bump = to_router_endpoint.bump,
- constraint = to_router_endpoint.protocol != MessageProtocol::None @ MatchingEngineError::EndpointDisabled,
- )]
- to_router_endpoint: Account<'info, RouterEndpoint>,
-
- #[account(
- mut,
- token::mint = common::constants::USDC_MINT,
+ bump,
)]
- executor_token: Box>,
+ core_message: AccountInfo<'info>,
- /// CHECK: Mutable. Must equal [best_offer](Auction::best_offer).
- #[account(mut)]
- best_offer_token: AccountInfo<'info>,
+ custodian: CheckedCustodian<'info>,
- /// CHECK: Mutable. Must equal [initial_offer](Auction::initial_offer).
- #[account(mut)]
- initial_offer_token: AccountInfo<'info>,
-
- /// Also the burn_source token account.
- ///
- /// CHECK: Mutable. Seeds must be \["custody"\].
- #[account(
- mut,
- address = crate::cctp_mint_recipient::id() @ MatchingEngineError::InvalidCustodyToken,
- )]
- cctp_mint_recipient: AccountInfo<'info>,
+ #[account(constraint = utils::require_local_endpoint(&execute_order.to_router_endpoint)?)]
+ execute_order: ExecuteOrder<'info>,
- /// CHECK: Seeds must be \["Bridge"\] (Wormhole Core Bridge program).
- #[account(mut)]
- core_bridge_config: UncheckedAccount<'info>,
+ wormhole: WormholePublishMessage<'info>,
- /// CHECK: Mutable. Seeds must be \["msg", payer, payer_sequence.value\].
#[account(
mut,
seeds = [
- common::constants::CORE_MESSAGE_SEED_PREFIX,
- payer.key().as_ref(),
- payer_sequence.value.to_be_bytes().as_ref(),
+ crate::LOCAL_CUSTODY_TOKEN_SEED_PREFIX,
+ &execute_order.fast_vaa.load_unchecked().emitter_chain().to_be_bytes(),
],
bump,
)]
- core_message: AccountInfo<'info>,
+ local_custody_token: Box>,
- /// CHECK: Seeds must be \["Sequence"\, custodian] (Wormhole Core Bridge program).
- #[account(mut)]
- core_emitter_sequence: UncheckedAccount<'info>,
-
- /// CHECK: Seeds must be \["fee_collector"\] (Wormhole Core Bridge program).
- #[account(mut)]
- core_fee_collector: UncheckedAccount<'info>,
-
- core_bridge_program: Program<'info, core_bridge_program::CoreBridge>,
system_program: Program<'info, System>,
token_program: Program<'info, token::Token>,
- /// CHECK: Wormhole Core Bridge needs the clock sysvar based on its legacy implementation.
- #[account(address = solana_program::sysvar::clock::id())]
- clock: AccountInfo<'info>,
-
- /// CHECK: Wormhole Core Bridge needs the rent sysvar based on its legacy implementation.
- #[account(address = solana_program::sysvar::rent::id())]
- rent: AccountInfo<'info>,
+ sysvars: RequiredSysvars<'info>,
}
pub fn execute_fast_order_local(ctx: Context) -> Result<()> {
- let super::PreparedFastExecution {
+ let super::PreparedOrderExecution {
user_amount: amount,
fill,
sequence_seed,
- } = super::prepare_fast_execution(super::PrepareFastExecution {
- custodian: &ctx.accounts.custodian,
- auction_config: &ctx.accounts.auction_config,
- fast_vaa: &ctx.accounts.fast_vaa,
- auction: &mut ctx.accounts.auction,
- cctp_mint_recipient: &ctx.accounts.cctp_mint_recipient,
- executor_token: &ctx.accounts.executor_token,
- best_offer_token: &ctx.accounts.best_offer_token,
- initial_offer_token: &ctx.accounts.initial_offer_token,
+ } = super::prepare_order_execution(super::PrepareFastExecution {
+ execute_order: &mut ctx.accounts.execute_order,
payer_sequence: &mut ctx.accounts.payer_sequence,
+ dst_token: &ctx.accounts.local_custody_token,
token_program: &ctx.accounts.token_program,
})?;
// Publish message via Core Bridge.
- core_bridge_program::cpi::post_message(
- CpiContext::new_with_signer(
- ctx.accounts.core_bridge_program.to_account_info(),
- core_bridge_program::cpi::PostMessage {
- payer: ctx.accounts.payer.to_account_info(),
- message: ctx.accounts.core_message.to_account_info(),
- emitter: ctx.accounts.custodian.to_account_info(),
- config: ctx.accounts.core_bridge_config.to_account_info(),
- emitter_sequence: ctx.accounts.core_emitter_sequence.to_account_info(),
- fee_collector: ctx.accounts.core_fee_collector.to_account_info(),
- system_program: ctx.accounts.system_program.to_account_info(),
- clock: ctx.accounts.clock.to_account_info(),
- rent: ctx.accounts.rent.to_account_info(),
- },
- &[
- Custodian::SIGNER_SEEDS,
- &[
- common::constants::CORE_MESSAGE_SEED_PREFIX,
- ctx.accounts.payer.key().as_ref(),
- sequence_seed.as_ref(),
- &[ctx.bumps.core_message],
- ],
- ],
- ),
- core_bridge_program::cpi::PostMessageArgs {
- nonce: common::constants::WORMHOLE_MESSAGE_NONCE,
- payload: common::messages::FastFill { amount, fill }.to_vec_payload(),
- commitment: core_bridge_program::Commitment::Finalized,
+ //
+ // NOTE: We cannot close the custody account yet because the user needs to be able to retrieve
+ // the funds when they complete the fast fill.
+ utils::wormhole::post_matching_engine_message(
+ utils::wormhole::PostMatchingEngineMessage {
+ wormhole: &ctx.accounts.wormhole,
+ core_message: &ctx.accounts.core_message,
+ custodian: &ctx.accounts.custodian,
+ payer: &ctx.accounts.payer,
+ system_program: &ctx.accounts.system_program,
+ sysvars: &ctx.accounts.sysvars,
},
- )
+ common::messages::FastFill { amount, fill },
+ &sequence_seed,
+ ctx.bumps.core_message,
+ )?;
+
+ // Finally close the account since it is no longer needed.
+ token::close_account(CpiContext::new_with_signer(
+ ctx.accounts.token_program.to_account_info(),
+ token::CloseAccount {
+ account: ctx
+ .accounts
+ .execute_order
+ .active_auction
+ .custody_token
+ .to_account_info(),
+ destination: ctx.accounts.payer.to_account_info(),
+ authority: ctx
+ .accounts
+ .execute_order
+ .active_auction
+ .auction
+ .to_account_info(),
+ },
+ &[&[
+ Auction::SEED_PREFIX,
+ ctx.accounts.execute_order.active_auction.vaa_hash.as_ref(),
+ &[ctx.accounts.execute_order.active_auction.bump],
+ ]],
+ ))
}
diff --git a/solana/programs/matching-engine/src/processor/auction/execute_fast_order/mod.rs b/solana/programs/matching-engine/src/processor/auction/execute_fast_order/mod.rs
index 2959cb904..d33e995db 100644
--- a/solana/programs/matching-engine/src/processor/auction/execute_fast_order/mod.rs
+++ b/solana/programs/matching-engine/src/processor/auction/execute_fast_order/mod.rs
@@ -5,85 +5,93 @@ mod local;
pub use local::*;
use crate::{
+ composite::*,
error::MatchingEngineError,
- state::{Auction, AuctionConfig, AuctionStatus, Custodian, PayerSequence},
+ state::{Auction, AuctionStatus, PayerSequence},
utils::{self, auction::DepositPenalty},
};
use anchor_lang::prelude::*;
use anchor_spl::token;
-use common::{
- messages::{raw::LiquidityLayerPayload, Fill},
- wormhole_cctp_solana::wormhole::VaaAccount,
-};
+use common::messages::{raw::LiquidityLayerPayload, Fill};
struct PrepareFastExecution<'ctx, 'info> {
- custodian: &'ctx AccountInfo<'info>,
- auction_config: &'ctx Account<'info, AuctionConfig>,
- fast_vaa: &'ctx AccountInfo<'info>,
- auction: &'ctx mut Box>,
- cctp_mint_recipient: &'ctx AccountInfo<'info>,
- executor_token: &'ctx Account<'info, token::TokenAccount>,
- best_offer_token: &'ctx AccountInfo<'info>,
- initial_offer_token: &'ctx AccountInfo<'info>,
+ execute_order: &'ctx mut ExecuteOrder<'info>,
payer_sequence: &'ctx mut Account<'info, PayerSequence>,
+ dst_token: &'ctx Account<'info, token::TokenAccount>,
token_program: &'ctx Program<'info, token::Token>,
}
-struct PreparedFastExecution {
+struct PreparedOrderExecution {
pub user_amount: u64,
pub fill: Fill,
pub sequence_seed: [u8; 8],
}
-fn prepare_fast_execution(accounts: PrepareFastExecution) -> Result {
+fn prepare_order_execution(accounts: PrepareFastExecution) -> Result {
let PrepareFastExecution {
- custodian,
- auction_config,
- fast_vaa,
- auction,
- cctp_mint_recipient,
- executor_token,
- best_offer_token,
- initial_offer_token,
+ execute_order,
payer_sequence,
+ dst_token,
token_program,
} = accounts;
- // Create zero copy reference to `FastMarketOrder` payload.
- let fast_vaa = VaaAccount::load_unchecked(fast_vaa);
+ let ExecuteOrder {
+ fast_vaa,
+ active_auction,
+ to_router_endpoint: _,
+ executor_token,
+ initial_offer_token,
+ } = execute_order;
+
+ let fast_vaa = fast_vaa.load_unchecked();
let order = LiquidityLayerPayload::try_from(fast_vaa.payload())
.map_err(|_| MatchingEngineError::InvalidVaa)?
.message()
.to_fast_market_order_unchecked();
+ let ActiveAuction {
+ auction,
+ custody_token,
+ config,
+ best_offer_token,
+ } = active_auction;
+
+ // Create zero copy reference to `FastMarketOrder` payload.
+
let (user_amount, new_status) = {
let auction_info = auction.info.as_ref().unwrap();
- let current_slot = Clock::get().map(|clock| clock.slot)?;
+ let current_slot = Clock::get().unwrap().slot;
require!(
- current_slot > auction_info.auction_end_slot(auction_config),
+ auction_info.end_early || current_slot > auction_info.auction_end_slot(config),
MatchingEngineError::AuctionPeriodNotExpired
);
let DepositPenalty {
penalty,
user_reward,
- } = utils::auction::compute_deposit_penalty(auction_config, auction_info, current_slot);
+ } = utils::auction::compute_deposit_penalty(config, auction_info, current_slot);
let mut deposit_and_fee =
auction_info.offer_price + auction_info.security_deposit - user_reward;
+ let auction_signer_seeds = &[
+ Auction::SEED_PREFIX,
+ auction.vaa_hash.as_ref(),
+ &[auction.bump],
+ ];
+
if penalty > 0 && best_offer_token.key() != executor_token.key() {
// Pay the liquidator the penalty.
token::transfer(
CpiContext::new_with_signer(
token_program.to_account_info(),
anchor_spl::token::Transfer {
- from: cctp_mint_recipient.to_account_info(),
+ from: custody_token.to_account_info(),
to: executor_token.to_account_info(),
- authority: custodian.to_account_info(),
+ authority: auction.to_account_info(),
},
- &[Custodian::SIGNER_SEEDS],
+ &[auction_signer_seeds],
),
penalty,
)?;
@@ -98,11 +106,11 @@ fn prepare_fast_execution(accounts: PrepareFastExecution) -> Result Result 0 { Some(penalty) } else { None },
+ },
)
};
// Set the auction status to completed.
auction.status = new_status;
- Ok(PreparedFastExecution {
+ Ok(PreparedOrderExecution {
user_amount,
fill: Fill {
source_chain: fast_vaa.emitter_chain(),
diff --git a/solana/programs/matching-engine/src/processor/auction/offer/improve.rs b/solana/programs/matching-engine/src/processor/auction/offer/improve.rs
index 1ecb8afff..9ba0002e8 100644
--- a/solana/programs/matching-engine/src/processor/auction/offer/improve.rs
+++ b/solana/programs/matching-engine/src/processor/auction/offer/improve.rs
@@ -1,106 +1,93 @@
-use crate::{
- error::MatchingEngineError,
- state::{Auction, AuctionConfig, Custodian},
- utils,
-};
+use crate::{composite::*, error::MatchingEngineError, state::Auction, utils};
use anchor_lang::prelude::*;
use anchor_spl::token;
#[derive(Accounts)]
+#[instruction(offer_price: u64)]
pub struct ImproveOffer<'info> {
- /// This program's Wormhole (Core Bridge) emitter authority. This is also the burn-source
- /// authority for CCTP transfers.
- ///
- /// CHECK: Seeds must be \["emitter"\].
#[account(
- seeds = [Custodian::SEED_PREFIX],
- bump = Custodian::BUMP,
- )]
- custodian: AccountInfo<'info>,
+ constraint = {
+ // This is safe because we know that this is an active auction.
+ let info = active_auction.info.as_ref().unwrap();
- auction_config: Account<'info, AuctionConfig>,
+ require!(
+ !info.end_early
+ && Clock::get().unwrap().slot <= info.auction_end_slot(&active_auction.config),
+ MatchingEngineError::AuctionPeriodExpired
+ );
- offer_authority: Signer<'info>,
+ require!(
+ offer_price
+ <= utils::auction::compute_min_allowed_offer(&active_auction.config, info),
+ MatchingEngineError::CarpingNotAllowed
+ );
- #[account(
- mut,
- seeds = [
- Auction::SEED_PREFIX,
- auction.vaa_hash.as_ref(),
- ],
- bump = auction.bump,
- constraint = utils::is_valid_active_auction(
- &auction_config,
- &auction,
- Some(best_offer_token.key()),
- None,
- )?
+ true
+ }
)]
- auction: Account<'info, Auction>,
+ active_auction: ActiveAuction<'info>,
- #[account(
- mut,
- associated_token::mint = common::constants::USDC_MINT,
- associated_token::authority = offer_authority
- )]
- offer_token: Account<'info, token::TokenAccount>,
-
- /// CHECK: Mutable. Must have the same key in auction data.
- #[account(mut)]
- best_offer_token: AccountInfo<'info>,
+ /// CHECK: Must be a token account, whose mint is `USDC_MINT` and have delegated authority to
+ /// the auction PDA.
+ offer_token: AccountInfo<'info>,
token_program: Program<'info, token::Token>,
}
-pub fn improve_offer(ctx: Context, fee_offer: u64) -> Result<()> {
- let auction_info = ctx.accounts.auction.info.as_mut().unwrap();
-
- {
- let current_slot = Clock::get().map(|clock| clock.slot)?;
- require!(
- current_slot <= auction_info.auction_end_slot(&ctx.accounts.auction_config),
- MatchingEngineError::AuctionPeriodExpired
- );
- }
+pub fn improve_offer(ctx: Context, offer_price: u64) -> Result<()> {
+ let ActiveAuction {
+ auction,
+ custody_token,
+ best_offer_token,
+ config: _,
+ } = &ctx.accounts.active_auction;
- // Make sure the new offer is less than the previous offer.
- require!(
- fee_offer < auction_info.offer_price,
- MatchingEngineError::OfferPriceNotImproved
- );
-
- // This check is safe because we already checked that `fee_offer` is less than `offer_price`.
- {
- let min_offer_delta =
- utils::auction::compute_min_offer_delta(&ctx.accounts.auction_config, auction_info);
- require!(
- auction_info.offer_price - fee_offer >= min_offer_delta,
- MatchingEngineError::CarpingNotAllowed
- );
- }
+ let offer_token = &ctx.accounts.offer_token;
+ let token_program = &ctx.accounts.token_program;
// Transfer funds from the `offer_token` token account to the `best_offer_token` token account,
// but only if the pubkeys are different.
- let offer_token = ctx.accounts.offer_token.key();
- if auction_info.best_offer_token != offer_token {
+ if offer_token.key() != best_offer_token.key() {
+ // These operations will seem silly, but we do this as a safety measure to ensure that
+ // nothing terrible happened with the auction's custody account.
+ let total_deposit = ctx.accounts.active_auction.custody_token.amount;
+
+ let auction_signer_seeds = &[
+ Auction::SEED_PREFIX,
+ auction.vaa_hash.as_ref(),
+ &[auction.bump],
+ ];
+
token::transfer(
CpiContext::new_with_signer(
- ctx.accounts.token_program.to_account_info(),
+ token_program.to_account_info(),
anchor_spl::token::Transfer {
- from: ctx.accounts.offer_token.to_account_info(),
- to: ctx.accounts.best_offer_token.to_account_info(),
- authority: ctx.accounts.custodian.to_account_info(),
+ from: custody_token.to_account_info(),
+ to: best_offer_token.to_account_info(),
+ authority: auction.to_account_info(),
},
- &[Custodian::SIGNER_SEEDS],
+ &[auction_signer_seeds],
),
- auction_info.total_deposit(),
+ total_deposit,
)?;
- // Update the `best_offer` token account and `amount` fields.
- auction_info.best_offer_token = offer_token;
+ token::transfer(
+ CpiContext::new_with_signer(
+ token_program.to_account_info(),
+ anchor_spl::token::Transfer {
+ from: offer_token.to_account_info(),
+ to: custody_token.to_account_info(),
+ authority: auction.to_account_info(),
+ },
+ &[auction_signer_seeds],
+ ),
+ total_deposit,
+ )?;
}
- auction_info.offer_price = fee_offer;
+ let info = ctx.accounts.active_auction.info.as_mut().unwrap();
+ info.best_offer_token = offer_token.key();
+ info.offer_price = offer_price;
Ok(())
}
diff --git a/solana/programs/matching-engine/src/processor/auction/offer/place_initial.rs b/solana/programs/matching-engine/src/processor/auction/offer/place_initial.rs
index 5d02b2b38..bb0d136d9 100644
--- a/solana/programs/matching-engine/src/processor/auction/offer/place_initial.rs
+++ b/solana/programs/matching-engine/src/processor/auction/offer/place_initial.rs
@@ -1,34 +1,20 @@
-use anchor_lang::prelude::*;
-use anchor_spl::token;
-use common::{
- messages::raw::LiquidityLayerPayload,
- wormhole_cctp_solana::wormhole::{core_bridge_program, VaaAccount},
-};
-
use crate::{
+ composite::*,
error::MatchingEngineError,
- state::{
- Auction, AuctionConfig, AuctionInfo, AuctionStatus, Custodian, MessageProtocol,
- RouterEndpoint,
- },
+ state::{Auction, AuctionConfig, AuctionInfo, AuctionStatus},
};
+use anchor_lang::prelude::*;
+use anchor_spl::token;
+use common::messages::raw::LiquidityLayerMessage;
#[derive(Accounts)]
pub struct PlaceInitialOffer<'info> {
#[account(mut)]
payer: Signer<'info>,
- // TODO: add initial_offer authority. Do not require to be owner?
- // initial_offer_authority: Signer<'info>,
- //
- /// This program's Wormhole (Core Bridge) emitter authority.
- ///
- /// CHECK: Seeds must be \["emitter"\].
- #[account(
- seeds = [Custodian::SEED_PREFIX],
- bump = Custodian::BUMP,
- )]
- custodian: Account<'info, Custodian>,
+ /// NOTE: Currently not used for anything. But this account can be used in
+ /// case we need to pause starting auctions.
+ custodian: CheckedCustodian<'info>,
#[account(
constraint = {
@@ -42,9 +28,7 @@ pub struct PlaceInitialOffer<'info> {
)]
auction_config: Account<'info, AuctionConfig>,
- /// CHECK: Must be owned by the Wormhole Core Bridge program.
- #[account(owner = core_bridge_program::id())]
- fast_vaa: AccountInfo<'info>,
+ fast_order_path: FastOrderPath<'info>,
/// This account should only be created once, and should never be changed to
/// init_if_needed. Otherwise someone can game an existing auction.
@@ -54,122 +38,110 @@ pub struct PlaceInitialOffer<'info> {
space = 8 + Auction::INIT_SPACE,
seeds = [
Auction::SEED_PREFIX,
- VaaAccount::load(&fast_vaa)?.digest().as_ref(),
+ fast_order_path.fast_vaa.load_unchecked().digest().as_ref(),
],
bump
)]
auction: Box>,
- #[account(
- seeds = [
- RouterEndpoint::SEED_PREFIX,
- from_router_endpoint.chain.to_be_bytes().as_ref(),
- ],
- bump = from_router_endpoint.bump,
- constraint = from_router_endpoint.protocol != MessageProtocol::None @ MatchingEngineError::EndpointDisabled,
- )]
- from_router_endpoint: Account<'info, RouterEndpoint>,
+ /// CHECK: Must be a token account, whose mint is `USDC_MINT` and have delegated authority to
+ /// the auction PDA.
+ offer_token: AccountInfo<'info>,
#[account(
+ init,
+ payer = payer,
+ token::mint = usdc,
+ token::authority = auction,
seeds = [
- RouterEndpoint::SEED_PREFIX,
- to_router_endpoint.chain.to_be_bytes().as_ref(),
+ crate::AUCTION_CUSTODY_TOKEN_SEED_PREFIX,
+ auction.key().as_ref(),
],
- bump = to_router_endpoint.bump,
- constraint = to_router_endpoint.protocol != MessageProtocol::None @ MatchingEngineError::EndpointDisabled,
+ bump,
)]
- to_router_endpoint: Account<'info, RouterEndpoint>,
+ auction_custody_token: Account<'info, token::TokenAccount>,
- #[account(
- mut,
- associated_token::mint = cctp_mint_recipient.mint,
- associated_token::authority = payer
- )]
- offer_token: Account<'info, token::TokenAccount>,
-
- #[account(
- mut,
- address = crate::cctp_mint_recipient::id() @ MatchingEngineError::InvalidCustodyToken,
- )]
- cctp_mint_recipient: Account<'info, token::TokenAccount>,
+ usdc: Usdc<'info>,
system_program: Program<'info, System>,
token_program: Program<'info, token::Token>,
}
-pub fn place_initial_offer(ctx: Context, fee_offer: u64) -> Result<()> {
+pub fn place_initial_offer(ctx: Context, offer_price: u64) -> Result<()> {
// Create zero copy reference to `FastMarketOrder` payload.
- let fast_vaa = VaaAccount::load(&ctx.accounts.fast_vaa)?;
- let msg = LiquidityLayerPayload::try_from(fast_vaa.payload())
- .map_err(|_| MatchingEngineError::InvalidVaa)?
- .message();
- let fast_order = msg
- .fast_market_order()
- .ok_or(MatchingEngineError::NotFastMarketOrder)?;
+ let fast_vaa = ctx.accounts.fast_order_path.fast_vaa.load_unchecked();
+ let order = LiquidityLayerMessage::try_from(fast_vaa.payload())
+ .unwrap()
+ .to_fast_market_order_unchecked();
let source_chain = fast_vaa.emitter_chain();
// We need to fetch clock values for a couple of operations in this instruction.
- let Clock {
- slot,
- unix_timestamp,
- ..
- } = Clock::get()?;
-
- // Check to see if the deadline has expired.
- let deadline = i64::from(fast_order.deadline());
+ let start_slot = {
+ let Clock {
+ slot,
+ unix_timestamp,
+ ..
+ } = Clock::get().unwrap();
+
+ // Check to see if the deadline has expired.
+ let deadline = i64::from(order.deadline());
+ require!(
+ deadline == 0 || unix_timestamp < deadline,
+ MatchingEngineError::FastMarketOrderExpired,
+ );
+
+ slot
+ };
+
+ let max_fee = order.max_fee();
require!(
- deadline == 0 || unix_timestamp < deadline,
- MatchingEngineError::FastMarketOrderExpired,
+ offer_price <= max_fee,
+ MatchingEngineError::OfferPriceTooHigh
);
- let max_fee = fast_order.max_fee();
- require!(fee_offer <= max_fee, MatchingEngineError::OfferPriceTooHigh);
-
- // Verify that the to and from router endpoints are valid.
- crate::utils::require_valid_router_path(
- &fast_vaa,
- &ctx.accounts.from_router_endpoint,
- &ctx.accounts.to_router_endpoint,
- fast_order.target_chain(),
- )?;
-
// Parse the transfer amount from the VAA.
- let amount_in = fast_order.amount_in();
-
- // Transfer tokens from the offer authority's token account to the custodian.
- token::transfer(
- CpiContext::new_with_signer(
- ctx.accounts.token_program.to_account_info(),
- anchor_spl::token::Transfer {
- from: ctx.accounts.offer_token.to_account_info(),
- to: ctx.accounts.cctp_mint_recipient.to_account_info(),
- authority: ctx.accounts.custodian.to_account_info(),
- },
- &[Custodian::SIGNER_SEEDS],
- ),
- amount_in + max_fee,
- )?;
+ let amount_in = order.amount_in();
// Set up the Auction account for this auction.
let initial_offer_token = ctx.accounts.offer_token.key();
+ let vaa_hash = fast_vaa.digest().0;
ctx.accounts.auction.set_inner(Auction {
bump: ctx.bumps.auction,
- vaa_hash: fast_vaa.digest().0,
+ vaa_hash,
status: AuctionStatus::Active,
info: Some(AuctionInfo {
config_id: ctx.accounts.auction_config.id,
+ custody_token_bump: ctx.bumps.auction_custody_token,
vaa_sequence: fast_vaa.sequence(),
source_chain,
best_offer_token: initial_offer_token,
initial_offer_token,
- start_slot: slot,
+ start_slot,
amount_in,
security_deposit: max_fee,
- offer_price: fee_offer,
+ offer_price,
amount_out: amount_in,
+ end_early: false,
}),
});
- Ok(())
+ // Finally transfer tokens from the offer authority's token account to the
+ // auction's custody account.
+ token::transfer(
+ CpiContext::new_with_signer(
+ ctx.accounts.token_program.to_account_info(),
+ anchor_spl::token::Transfer {
+ from: ctx.accounts.offer_token.to_account_info(),
+ to: ctx.accounts.auction_custody_token.to_account_info(),
+ authority: ctx.accounts.auction.to_account_info(),
+ },
+ &[&[
+ Auction::SEED_PREFIX,
+ vaa_hash.as_ref(),
+ &[ctx.bumps.auction],
+ ]],
+ ),
+ amount_in + max_fee,
+ )
}
diff --git a/solana/programs/matching-engine/src/processor/auction/prepare_settlement/cctp.rs b/solana/programs/matching-engine/src/processor/auction/prepare_settlement/cctp.rs
index 78f85e808..926e507dc 100644
--- a/solana/programs/matching-engine/src/processor/auction/prepare_settlement/cctp.rs
+++ b/solana/programs/matching-engine/src/processor/auction/prepare_settlement/cctp.rs
@@ -1,16 +1,13 @@
use crate::{
+ composite::*,
error::MatchingEngineError,
- state::{Custodian, PreparedOrderResponse},
+ state::{Auction, AuctionStatus, Custodian, PreparedOrderResponse},
};
use anchor_lang::prelude::*;
use anchor_spl::token;
use common::{
messages::raw::{LiquidityLayerDepositMessage, LiquidityLayerMessage},
- wormhole_cctp_solana::{
- self,
- cctp::{message_transmitter_program, token_messenger_minter_program},
- wormhole::{core_bridge_program, VaaAccount},
- },
+ wormhole_cctp_solana::{self, cctp::message_transmitter_program, wormhole::VaaAccount},
};
#[derive(Accounts)]
@@ -18,95 +15,94 @@ pub struct PrepareOrderResponseCctp<'info> {
#[account(mut)]
payer: Signer<'info>,
- /// This program's Wormhole (Core Bridge) emitter authority.
- ///
- /// CHECK: Seeds must be \["emitter"\].
- #[account(
- seeds = [Custodian::SEED_PREFIX],
- bump = Custodian::BUMP,
- )]
- custodian: Box>,
+ custodian: CheckedCustodian<'info>,
- /// CHECK: Must be owned by the Wormhole Core Bridge program. This account will be read via
- /// zero-copy using the [VaaAccount](core_bridge_program::sdk::VaaAccount) reader.
- #[account(owner = core_bridge_program::id())]
- fast_vaa: AccountInfo<'info>,
+ fast_vaa: LiquidityLayerVaa<'info>,
- /// CHECK: Must be owned by the Wormhole Core Bridge program. Ownership check happens in
- /// [verify_vaa_and_mint](wormhole_cctp_solana::cpi::verify_vaa_and_mint).
- finalized_vaa: AccountInfo<'info>,
+ #[account(
+ constraint = {
+ // Fast and finalized VAAs must reconcile with each other.
+ let fast_vaa = fast_vaa.load_unchecked();
+ let finalized_vaa = finalized_vaa.load_unchecked();
+
+ require_eq!(
+ fast_vaa.emitter_chain(),
+ finalized_vaa.emitter_chain(),
+ MatchingEngineError::VaaMismatch
+ );
+ require!(
+ fast_vaa.emitter_address() == finalized_vaa.emitter_address(),
+ MatchingEngineError::VaaMismatch
+ );
+ require_eq!(
+ fast_vaa.sequence(),
+ finalized_vaa.sequence() + 1,
+ MatchingEngineError::VaaMismatch
+ );
+ require!(
+ fast_vaa.timestamp() == finalized_vaa.timestamp(),
+ MatchingEngineError::VaaMismatch
+ );
+
+ // Make sure the finalized VAA is a slow order response encoded in a deposit.
+ let finalized_msg = LiquidityLayerMessage::try_from(finalized_vaa.payload()).unwrap();
+ let deposit = finalized_msg
+ .deposit()
+ .ok_or(MatchingEngineError::InvalidPayloadId)?;
+ let deposit_msg = LiquidityLayerDepositMessage::try_from(deposit.payload())
+ .map_err(|_| error!(MatchingEngineError::InvalidDepositMessage))?;
+ let slow_order_response = deposit_msg
+ .slow_order_response()
+ .ok_or(MatchingEngineError::InvalidDepositPayloadId)?;
+
+ true
+ }
+ )]
+ finalized_vaa: LiquidityLayerVaa<'info>,
#[account(
- init,
+ init_if_needed,
payer = payer,
space = 8 + PreparedOrderResponse::INIT_SPACE,
seeds = [
PreparedOrderResponse::SEED_PREFIX,
- payer.key().as_ref(),
VaaAccount::load(&fast_vaa)?.digest().as_ref()
],
bump,
)]
- prepared_order_response: Account<'info, PreparedOrderResponse>,
-
- /// Mint recipient token account, which is encoded as the mint recipient in the CCTP message.
- /// The CCTP Token Messenger Minter program will transfer the amount encoded in the CCTP message
- /// from its custody account to this account.
- ///
- /// CHECK: Mutable. Seeds must be \["custody"\].
- ///
- /// NOTE: This account must be encoded as the mint recipient in the CCTP message.
+ prepared_order_response: Box>,
+
#[account(
- mut,
- address = crate::cctp_mint_recipient::id() @ MatchingEngineError::InvalidCustodyToken,
+ init_if_needed,
+ payer = payer,
+ token::mint = usdc,
+ token::authority = prepared_order_response,
+ seeds = [
+ crate::PREPARED_CUSTODY_TOKEN_SEED_PREFIX,
+ prepared_order_response.key().as_ref(),
+ ],
+ bump,
)]
- cctp_mint_recipient: AccountInfo<'info>,
-
- /// CHECK: Seeds must be \["message_transmitter_authority"\] (CCTP Message Transmitter program).
- message_transmitter_authority: UncheckedAccount<'info>,
-
- /// CHECK: Seeds must be \["message_transmitter"\] (CCTP Message Transmitter program).
- message_transmitter_config: UncheckedAccount<'info>,
-
- /// CHECK: Mutable. Seeds must be \["used_nonces", remote_domain.to_string(),
- /// first_nonce.to_string()\] (CCTP Message Transmitter program).
- #[account(mut)]
- used_nonces: UncheckedAccount<'info>,
-
- /// CHECK: Seeds must be \["__event_authority"\] (CCTP Message Transmitter program)).
- message_transmitter_event_authority: AccountInfo<'info>,
-
- /// CHECK: Seeds must be \["token_messenger"\] (CCTP Token Messenger Minter program).
- token_messenger: UncheckedAccount<'info>,
-
- /// CHECK: Seeds must be \["remote_token_messenger"\, remote_domain.to_string()] (CCTP Token
- /// Messenger Minter program).
- remote_token_messenger: UncheckedAccount<'info>,
-
- /// CHECK: Seeds must be \["token_minter"\] (CCTP Token Messenger Minter program).
- token_minter: UncheckedAccount<'info>,
+ prepared_custody_token: Box>,
- /// Token Messenger Minter's Local Token account. This program uses the mint of this account to
- /// validate the `mint_recipient` token account's mint.
- ///
- /// CHECK: Mutable. Seeds must be \["local_token", mint\] (CCTP Token Messenger Minter program).
- #[account(mut)]
- local_token: UncheckedAccount<'info>,
+ #[account(
+ mut,
+ constraint = {
+ require_eq!(
+ &auction.status,
+ &AuctionStatus::Active,
+ MatchingEngineError::AuctionNotActive
+ );
+
+ true
+ }
+ )]
+ auction: Option>>,
- /// CHECK: Seeds must be \["token_pair", remote_domain.to_string(), remote_token_address\] (CCTP
- /// Token Messenger Minter program).
- token_pair: UncheckedAccount<'info>,
+ usdc: Usdc<'info>,
- /// CHECK: Mutable. Seeds must be \["custody", mint\] (CCTP Token Messenger Minter program).
- #[account(mut)]
- token_messenger_minter_custody_token: UncheckedAccount<'info>,
+ cctp: CctpReceiveMessage<'info>,
- /// CHECK: Seeds must be \["__event_authority"\] (CCTP Token Messenger Minter program).
- token_messenger_minter_event_authority: AccountInfo<'info>,
-
- token_messenger_minter_program:
- Program<'info, token_messenger_minter_program::TokenMessengerMinter>,
- message_transmitter_program: Program<'info, message_transmitter_program::MessageTransmitter>,
token_program: Program<'info, token::Token>,
system_program: Program<'info, System>,
}
@@ -121,50 +117,68 @@ pub fn prepare_order_response_cctp(
ctx: Context,
args: CctpMessageArgs,
) -> Result<()> {
- let fast_vaa = VaaAccount::load_unchecked(&ctx.accounts.fast_vaa);
+ match ctx.accounts.prepared_order_response.source_chain {
+ 0 => handle_prepare_order_response_cctp(ctx, args),
+ _ => super::prepare_order_response_noop(),
+ }
+}
+fn handle_prepare_order_response_cctp(
+ ctx: Context,
+ args: CctpMessageArgs,
+) -> Result<()> {
let finalized_vaa = wormhole_cctp_solana::cpi::verify_vaa_and_mint(
&ctx.accounts.finalized_vaa,
CpiContext::new_with_signer(
- ctx.accounts.message_transmitter_program.to_account_info(),
+ ctx.accounts
+ .cctp
+ .message_transmitter_program
+ .to_account_info(),
message_transmitter_program::cpi::ReceiveTokenMessengerMinterMessage {
payer: ctx.accounts.payer.to_account_info(),
caller: ctx.accounts.custodian.to_account_info(),
message_transmitter_authority: ctx
.accounts
+ .cctp
.message_transmitter_authority
.to_account_info(),
message_transmitter_config: ctx
.accounts
+ .cctp
.message_transmitter_config
.to_account_info(),
- used_nonces: ctx.accounts.used_nonces.to_account_info(),
+ used_nonces: ctx.accounts.cctp.used_nonces.to_account_info(),
token_messenger_minter_program: ctx
.accounts
+ .cctp
.token_messenger_minter_program
.to_account_info(),
system_program: ctx.accounts.system_program.to_account_info(),
message_transmitter_event_authority: ctx
.accounts
+ .cctp
.message_transmitter_event_authority
.to_account_info(),
message_transmitter_program: ctx
.accounts
+ .cctp
.message_transmitter_program
.to_account_info(),
- token_messenger: ctx.accounts.token_messenger.to_account_info(),
- remote_token_messenger: ctx.accounts.remote_token_messenger.to_account_info(),
- token_minter: ctx.accounts.token_minter.to_account_info(),
- local_token: ctx.accounts.local_token.to_account_info(),
- token_pair: ctx.accounts.token_pair.to_account_info(),
- mint_recipient: ctx.accounts.cctp_mint_recipient.to_account_info(),
+ token_messenger: ctx.accounts.cctp.token_messenger.to_account_info(),
+ remote_token_messenger: ctx.accounts.cctp.remote_token_messenger.to_account_info(),
+ token_minter: ctx.accounts.cctp.token_minter.to_account_info(),
+ local_token: ctx.accounts.cctp.local_token.to_account_info(),
+ token_pair: ctx.accounts.cctp.token_pair.to_account_info(),
+ mint_recipient: ctx.accounts.cctp.mint_recipient.to_account_info(),
custody_token: ctx
.accounts
+ .cctp
.token_messenger_minter_custody_token
.to_account_info(),
token_program: ctx.accounts.token_program.to_account_info(),
token_messenger_minter_event_authority: ctx
.accounts
+ .cctp
.token_messenger_minter_event_authority
.to_account_info(),
},
@@ -176,32 +190,6 @@ pub fn prepare_order_response_cctp(
},
)?;
- // Reconcile fast VAA with finalized VAA.
- let source_chain = {
- let fast_emitter = fast_vaa.emitter_info();
- let finalized_emitter = finalized_vaa.emitter_info();
- require_eq!(
- fast_emitter.chain,
- finalized_emitter.chain,
- MatchingEngineError::VaaMismatch
- );
- require!(
- fast_emitter.address == finalized_emitter.address,
- MatchingEngineError::VaaMismatch
- );
- require_eq!(
- fast_emitter.sequence,
- finalized_emitter.sequence + 1,
- MatchingEngineError::VaaMismatch
- );
- require!(
- fast_vaa.timestamp() == finalized_vaa.timestamp(),
- MatchingEngineError::VaaMismatch
- );
-
- finalized_emitter.chain
- };
-
// This should be infallible because:
// 1. We know that the fast VAA was used to start this auction (using its hash for the
// auction data PDA).
@@ -209,16 +197,18 @@ pub fn prepare_order_response_cctp(
//
// However, we will still process results in case Token Router implementation renders any of
// these assumptions invalid.
- let finalized_msg = LiquidityLayerMessage::try_from(finalized_vaa.payload())
- .map_err(|_| error!(MatchingEngineError::InvalidVaa))?;
- let deposit = finalized_msg
- .deposit()
- .ok_or(MatchingEngineError::InvalidPayloadId)?;
- let deposit_msg = LiquidityLayerDepositMessage::try_from(deposit.payload())
- .map_err(|_| error!(MatchingEngineError::InvalidDepositMessage))?;
- let slow_order_response = deposit_msg
- .slow_order_response()
- .ok_or(MatchingEngineError::InvalidDepositPayloadId)?;
+ let finalized_msg = LiquidityLayerMessage::try_from(finalized_vaa.payload()).unwrap();
+ let deposit = finalized_msg.to_deposit_unchecked();
+ let base_fee = LiquidityLayerDepositMessage::try_from(deposit.payload())
+ .unwrap()
+ .to_slow_order_response_unchecked()
+ .base_fee();
+
+ let fast_vaa = ctx.accounts.fast_vaa.load_unchecked();
+ let amount = LiquidityLayerMessage::try_from(fast_vaa.payload())
+ .unwrap()
+ .to_fast_market_order_unchecked()
+ .amount_in();
// Write to the prepared slow order account, which will be closed by one of the following
// instructions:
@@ -231,10 +221,32 @@ pub fn prepare_order_response_cctp(
bump: ctx.bumps.prepared_order_response,
fast_vaa_hash: fast_vaa.digest().0,
prepared_by: ctx.accounts.payer.key(),
- source_chain,
- base_fee: slow_order_response.base_fee(),
+ source_chain: finalized_vaa.emitter_chain(),
+ base_fee,
});
- // Done.
- Ok(())
+ // This method overrides the offer price with the base fee if there is an active auction. We do
+ // this to ensure that the user does not overpay for a transfer that has already finalized
+ // during an auction.
+ if let Some(auction) = &mut ctx.accounts.auction {
+ msg!("AuctionStatus::Active");
+
+ let info = auction.info.as_mut().unwrap();
+ info.offer_price = base_fee;
+ info.end_early = true;
+ }
+
+ // Finally transfer minted via CCTP to prepared custody token.
+ token::transfer(
+ CpiContext::new_with_signer(
+ ctx.accounts.token_program.to_account_info(),
+ token::Transfer {
+ from: ctx.accounts.cctp.mint_recipient.to_account_info(),
+ to: ctx.accounts.prepared_custody_token.to_account_info(),
+ authority: ctx.accounts.custodian.to_account_info(),
+ },
+ &[Custodian::SIGNER_SEEDS],
+ ),
+ amount,
+ )
}
diff --git a/solana/programs/matching-engine/src/processor/auction/prepare_settlement/mod.rs b/solana/programs/matching-engine/src/processor/auction/prepare_settlement/mod.rs
index c143ed248..cd21ed9e1 100644
--- a/solana/programs/matching-engine/src/processor/auction/prepare_settlement/mod.rs
+++ b/solana/programs/matching-engine/src/processor/auction/prepare_settlement/mod.rs
@@ -1,2 +1,11 @@
mod cctp;
pub use cctp::*;
+
+use anchor_lang::prelude::*;
+
+fn prepare_order_response_noop() -> Result<()> {
+ msg!("Already prepared");
+
+ // No-op.
+ Ok(())
+}
diff --git a/solana/programs/matching-engine/src/processor/auction/settle/active/cctp.rs b/solana/programs/matching-engine/src/processor/auction/settle/active/cctp.rs
deleted file mode 100644
index 87b4fa0b8..000000000
--- a/solana/programs/matching-engine/src/processor/auction/settle/active/cctp.rs
+++ /dev/null
@@ -1,315 +0,0 @@
-use crate::{
- error::MatchingEngineError,
- state::{
- Auction, AuctionConfig, Custodian, MessageProtocol, PayerSequence, PreparedOrderResponse,
- RouterEndpoint,
- },
- utils,
-};
-use anchor_lang::prelude::*;
-use anchor_spl::token;
-use common::{
- wormhole_cctp_solana::{
- self,
- cctp::{message_transmitter_program, token_messenger_minter_program},
- wormhole::core_bridge_program,
- },
- wormhole_io::TypePrefixedPayload,
-};
-
-/// Accounts required for [settle_auction_active_cctp].
-#[derive(Accounts)]
-pub struct SettleAuctionActiveCctp<'info> {
- #[account(
- mut,
- address = prepared_order_response.prepared_by, // TODO: add err
- )]
- payer: Signer<'info>,
-
- #[account(
- init_if_needed,
- payer = payer,
- space = 8 + PayerSequence::INIT_SPACE,
- seeds = [
- PayerSequence::SEED_PREFIX,
- payer.key().as_ref()
- ],
- bump,
- )]
- payer_sequence: Box>,
-
- /// This program's Wormhole (Core Bridge) emitter authority.
- ///
- /// CHECK: Seeds must be \["emitter"\].
- #[account(
- seeds = [Custodian::SEED_PREFIX],
- bump = Custodian::BUMP,
- )]
- custodian: AccountInfo<'info>,
-
- auction_config: Box>,
-
- /// CHECK: Must be owned by the Wormhole Core Bridge program. This account will be read via
- /// zero-copy using the [VaaAccount](core_bridge_program::sdk::VaaAccount) reader.
- #[account(owner = core_bridge_program::id())]
- fast_vaa: AccountInfo<'info>,
-
- #[account(
- mut,
- close = payer,
- seeds = [
- PreparedOrderResponse::SEED_PREFIX,
- payer.key().as_ref(),
- wormhole_cctp_solana::wormhole::VaaAccount::load(&fast_vaa)?.digest().as_ref()
- ],
- bump = prepared_order_response.bump,
- )]
- prepared_order_response: Box>,
-
- /// There should be no account data here because an auction was never created.
- #[account(
- mut,
- seeds = [
- Auction::SEED_PREFIX,
- prepared_order_response.fast_vaa_hash.as_ref(),
- ],
- bump = auction.bump,
- constraint = utils::is_valid_active_auction(
- &auction_config,
- &auction,
- Some(best_offer_token.key()),
- None,
- )?
- )]
- auction: Box>,
-
- /// CHECK: Must equal the best offer token in the auction data account.
- #[account(mut)]
- best_offer_token: AccountInfo<'info>,
-
- /// CHECK: Must be the same mint as the best offer token.
- #[account(mut)]
- executor_token: AccountInfo<'info>,
-
- /// Mint recipient token account, which is encoded as the mint recipient in the CCTP message.
- /// The CCTP Token Messenger Minter program will transfer the amount encoded in the CCTP message
- /// from its custody account to this account.
- ///
- /// CHECK: Mutable. Seeds must be \["custody"\].
- ///
- /// NOTE: This account must be encoded as the mint recipient in the CCTP message.
- #[account(
- mut,
- address = crate::cctp_mint_recipient::id() @ MatchingEngineError::InvalidCustodyToken,
- )]
- cctp_mint_recipient: AccountInfo<'info>,
-
- /// Seeds must be \["endpoint", chain.to_be_bytes()\].
- #[account(
- seeds = [
- RouterEndpoint::SEED_PREFIX,
- to_router_endpoint.chain.to_be_bytes().as_ref(),
- ],
- bump = to_router_endpoint.bump,
- )]
- to_router_endpoint: Box>,
-
- /// Circle-supported mint.
- ///
- /// CHECK: Mutable. This token account's mint must be the same as the one found in the CCTP
- /// Token Messenger Minter program's local token account.
- #[account(mut)]
- mint: AccountInfo<'info>,
-
- /// CHECK: Seeds must be \["Bridge"\] (Wormhole Core Bridge program).
- #[account(mut)]
- core_bridge_config: UncheckedAccount<'info>,
-
- /// CHECK: Mutable. Seeds must be \["core-msg", payer, payer_sequence.value\].
- #[account(
- mut,
- seeds = [
- common::constants::CORE_MESSAGE_SEED_PREFIX,
- payer.key().as_ref(),
- payer_sequence.value.to_be_bytes().as_ref(),
- ],
- bump,
- )]
- core_message: AccountInfo<'info>,
-
- /// CHECK: Mutable. Seeds must be \["cctp-msg", payer, payer_sequence.value\].
- #[account(
- mut,
- seeds = [
- common::constants::CCTP_MESSAGE_SEED_PREFIX,
- payer.key().as_ref(),
- payer_sequence.value.to_be_bytes().as_ref(),
- ],
- bump,
- )]
- cctp_message: AccountInfo<'info>,
-
- /// CHECK: Seeds must be \["Sequence"\, custodian] (Wormhole Core Bridge program).
- #[account(mut)]
- core_emitter_sequence: UncheckedAccount<'info>,
-
- /// CHECK: Seeds must be \["fee_collector"\] (Wormhole Core Bridge program).
- #[account(mut)]
- core_fee_collector: UncheckedAccount<'info>,
-
- /// CHECK: Seeds must be \["sender_authority"\] (CCTP Token Messenger Minter program).
- token_messenger_minter_sender_authority: UncheckedAccount<'info>,
-
- /// CHECK: Mutable. Seeds must be \["message_transmitter"\] (CCTP Message Transmitter program).
- #[account(mut)]
- message_transmitter_config: UncheckedAccount<'info>,
-
- /// CHECK: Seeds must be \["token_messenger"\] (CCTP Token Messenger Minter program).
- token_messenger: UncheckedAccount<'info>,
-
- /// CHECK: Seeds must be \["remote_token_messenger"\, remote_domain.to_string()] (CCTP Token
- /// Messenger Minter program).
- remote_token_messenger: UncheckedAccount<'info>,
-
- /// CHECK Seeds must be \["token_minter"\] (CCTP Token Messenger Minter program).
- token_minter: UncheckedAccount<'info>,
-
- /// Local token account, which this program uses to validate the `mint` used to burn.
- ///
- /// CHECK: Mutable. Seeds must be \["local_token", mint\] (CCTP Token Messenger Minter program).
- #[account(mut)]
- local_token: UncheckedAccount<'info>,
-
- /// CHECK: Seeds must be \["__event_authority"\] (CCTP Token Messenger Minter program).
- token_messenger_minter_event_authority: UncheckedAccount<'info>,
-
- core_bridge_program: Program<'info, core_bridge_program::CoreBridge>,
- token_messenger_minter_program:
- Program<'info, token_messenger_minter_program::TokenMessengerMinter>,
- message_transmitter_program: Program<'info, message_transmitter_program::MessageTransmitter>,
- token_program: Program<'info, token::Token>,
- system_program: Program<'info, System>,
-
- /// CHECK: Wormhole Core Bridge needs the clock sysvar based on its legacy implementation.
- #[account(address = solana_program::sysvar::clock::id())]
- clock: AccountInfo<'info>,
-
- /// CHECK: Wormhole Core Bridge needs the rent sysvar based on its legacy implementation.
- #[account(address = solana_program::sysvar::rent::id())]
- rent: AccountInfo<'info>,
-}
-
-/// TODO: add docstring
-pub fn settle_auction_active_cctp(ctx: Context) -> Result<()> {
- match ctx.accounts.to_router_endpoint.protocol {
- MessageProtocol::Cctp { domain } => handle_settle_auction_active_cctp(ctx, domain),
- _ => err!(MatchingEngineError::InvalidCctpEndpoint),
- }
-}
-
-fn handle_settle_auction_active_cctp(
- ctx: Context,
- destination_cctp_domain: u32,
-) -> Result<()> {
- let super::SettledActive {
- user_amount: amount,
- fill,
- sequence_seed,
- } = super::settle_active_and_prepare_fill(super::SettleActiveAndPrepareFill {
- custodian: &ctx.accounts.custodian,
- auction_config: &ctx.accounts.auction_config,
- fast_vaa: &ctx.accounts.fast_vaa,
- auction: &mut ctx.accounts.auction,
- prepared_order_response: &ctx.accounts.prepared_order_response,
- executor_token: &ctx.accounts.executor_token,
- best_offer_token: &ctx.accounts.best_offer_token,
- cctp_mint_recipient: &ctx.accounts.cctp_mint_recipient,
- payer_sequence: &mut ctx.accounts.payer_sequence,
- token_program: &ctx.accounts.token_program,
- })?;
-
- // This returns the CCTP nonce, but we do not need it.
- wormhole_cctp_solana::cpi::burn_and_publish(
- CpiContext::new_with_signer(
- ctx.accounts
- .token_messenger_minter_program
- .to_account_info(),
- wormhole_cctp_solana::cpi::DepositForBurnWithCaller {
- burn_token_owner: ctx.accounts.custodian.to_account_info(),
- payer: ctx.accounts.payer.to_account_info(),
- token_messenger_minter_sender_authority: ctx
- .accounts
- .token_messenger_minter_sender_authority
- .to_account_info(),
- burn_token: ctx.accounts.cctp_mint_recipient.to_account_info(),
- message_transmitter_config: ctx
- .accounts
- .message_transmitter_config
- .to_account_info(),
- token_messenger: ctx.accounts.token_messenger.to_account_info(),
- remote_token_messenger: ctx.accounts.remote_token_messenger.to_account_info(),
- token_minter: ctx.accounts.token_minter.to_account_info(),
- local_token: ctx.accounts.local_token.to_account_info(),
- mint: ctx.accounts.mint.to_account_info(),
- cctp_message: ctx.accounts.cctp_message.to_account_info(),
- message_transmitter_program: ctx
- .accounts
- .message_transmitter_program
- .to_account_info(),
- token_messenger_minter_program: ctx
- .accounts
- .token_messenger_minter_program
- .to_account_info(),
- token_program: ctx.accounts.token_program.to_account_info(),
- system_program: ctx.accounts.system_program.to_account_info(),
- event_authority: ctx
- .accounts
- .token_messenger_minter_event_authority
- .to_account_info(),
- },
- &[
- Custodian::SIGNER_SEEDS,
- &[
- common::constants::CCTP_MESSAGE_SEED_PREFIX,
- ctx.accounts.payer.key().as_ref(),
- sequence_seed.as_ref(),
- &[ctx.bumps.cctp_message],
- ],
- ],
- ),
- CpiContext::new_with_signer(
- ctx.accounts.core_bridge_program.to_account_info(),
- wormhole_cctp_solana::cpi::PostMessage {
- payer: ctx.accounts.payer.to_account_info(),
- message: ctx.accounts.core_message.to_account_info(),
- emitter: ctx.accounts.custodian.to_account_info(),
- config: ctx.accounts.core_bridge_config.to_account_info(),
- emitter_sequence: ctx.accounts.core_emitter_sequence.to_account_info(),
- fee_collector: ctx.accounts.core_fee_collector.to_account_info(),
- system_program: ctx.accounts.system_program.to_account_info(),
- clock: ctx.accounts.clock.to_account_info(),
- rent: ctx.accounts.rent.to_account_info(),
- },
- &[
- Custodian::SIGNER_SEEDS,
- &[
- common::constants::CORE_MESSAGE_SEED_PREFIX,
- ctx.accounts.payer.key().as_ref(),
- sequence_seed.as_ref(),
- &[ctx.bumps.core_message],
- ],
- ],
- ),
- wormhole_cctp_solana::cpi::BurnAndPublishArgs {
- burn_source: None,
- destination_caller: ctx.accounts.to_router_endpoint.address,
- destination_cctp_domain,
- amount,
- mint_recipient: ctx.accounts.to_router_endpoint.mint_recipient,
- wormhole_message_nonce: common::constants::WORMHOLE_MESSAGE_NONCE,
- payload: fill.to_vec_payload(),
- },
- )?;
-
- Ok(())
-}
diff --git a/solana/programs/matching-engine/src/processor/auction/settle/active/local.rs b/solana/programs/matching-engine/src/processor/auction/settle/active/local.rs
deleted file mode 100644
index 70fac6037..000000000
--- a/solana/programs/matching-engine/src/processor/auction/settle/active/local.rs
+++ /dev/null
@@ -1,201 +0,0 @@
-use crate::{
- error::MatchingEngineError,
- state::{
- Auction, AuctionConfig, Custodian, MessageProtocol, PayerSequence, PreparedOrderResponse,
- RouterEndpoint,
- },
- utils,
-};
-use anchor_lang::prelude::*;
-use anchor_spl::token;
-use common::{
- wormhole_cctp_solana::wormhole::{core_bridge_program, VaaAccount, SOLANA_CHAIN},
- wormhole_io::TypePrefixedPayload,
-};
-
-/// Accounts required for [settle_auction_active_local].
-#[derive(Accounts)]
-pub struct SettleAuctionActiveLocal<'info> {
- #[account(
- mut,
- address = prepared_order_response.prepared_by, // TODO: add err
- )]
- payer: Signer<'info>,
-
- #[account(
- init_if_needed,
- payer = payer,
- space = 8 + PayerSequence::INIT_SPACE,
- seeds = [
- PayerSequence::SEED_PREFIX,
- payer.key().as_ref()
- ],
- bump,
- )]
- payer_sequence: Account<'info, PayerSequence>,
-
- /// This program's Wormhole (Core Bridge) emitter authority.
- ///
- /// CHECK: Seeds must be \["emitter"\].
- #[account(
- seeds = [Custodian::SEED_PREFIX],
- bump = Custodian::BUMP,
- )]
- custodian: AccountInfo<'info>,
-
- auction_config: Box>,
-
- /// CHECK: Must be owned by the Wormhole Core Bridge program. This account will be read via
- /// zero-copy using the [VaaAccount](core_bridge_program::sdk::VaaAccount) reader.
- #[account(owner = core_bridge_program::id())]
- fast_vaa: AccountInfo<'info>,
-
- #[account(
- mut,
- close = payer,
- seeds = [
- PreparedOrderResponse::SEED_PREFIX,
- payer.key().as_ref(),
- VaaAccount::load(&fast_vaa)?.digest().as_ref()
- ],
- bump = prepared_order_response.bump,
- )]
- prepared_order_response: Box>,
-
- /// There should be no account data here because an auction was never created.
- #[account(
- mut,
- seeds = [
- Auction::SEED_PREFIX,
- prepared_order_response.fast_vaa_hash.as_ref(),
- ],
- bump = auction.bump,
- constraint = utils::is_valid_active_auction(
- &auction_config,
- &auction,
- Some(best_offer_token.key()),
- None,
- )?
- )]
- auction: Box>,
-
- /// Seeds must be \["endpoint", chain.to_be_bytes()\].
- #[account(
- seeds = [
- RouterEndpoint::SEED_PREFIX,
- SOLANA_CHAIN.to_be_bytes().as_ref(),
- ],
- bump = to_router_endpoint.bump,
- constraint = to_router_endpoint.protocol != MessageProtocol::None @ MatchingEngineError::EndpointDisabled,
- )]
- to_router_endpoint: Box>,
-
- /// CHECK: Must equal the best offer token in the auction data account.
- #[account(mut)]
- best_offer_token: AccountInfo<'info>,
-
- /// CHECK: Must be the same mint as the best offer token.
- #[account(mut)]
- executor_token: AccountInfo<'info>,
-
- /// Mint recipient token account, which is encoded as the mint recipient in the CCTP message.
- /// The CCTP Token Messenger Minter program will transfer the amount encoded in the CCTP message
- /// from its custody account to this account.
- ///
- /// CHECK: Mutable. Seeds must be \["custody"\].
- ///
- /// NOTE: This account must be encoded as the mint recipient in the CCTP message.
- #[account(
- mut,
- address = crate::cctp_mint_recipient::id() @ MatchingEngineError::InvalidCustodyToken,
- )]
- cctp_mint_recipient: AccountInfo<'info>,
-
- /// CHECK: Seeds must be \["Bridge"\] (Wormhole Core Bridge program).
- #[account(mut)]
- core_bridge_config: UncheckedAccount<'info>,
-
- /// CHECK: Mutable. Seeds must be \["msg", payer, payer_sequence.value\].
- #[account(
- mut,
- seeds = [
- common::constants::CORE_MESSAGE_SEED_PREFIX,
- payer.key().as_ref(),
- payer_sequence.value.to_be_bytes().as_ref(),
- ],
- bump,
- )]
- core_message: AccountInfo<'info>,
-
- /// CHECK: Seeds must be \["Sequence"\, custodian] (Wormhole Core Bridge program).
- #[account(mut)]
- core_emitter_sequence: UncheckedAccount<'info>,
-
- /// CHECK: Seeds must be \["fee_collector"\] (Wormhole Core Bridge program).
- #[account(mut)]
- core_fee_collector: UncheckedAccount<'info>,
-
- core_bridge_program: Program<'info, core_bridge_program::CoreBridge>,
- token_program: Program<'info, token::Token>,
- system_program: Program<'info, System>,
-
- /// CHECK: Wormhole Core Bridge needs the clock sysvar based on its legacy implementation.
- #[account(address = solana_program::sysvar::clock::id())]
- clock: AccountInfo<'info>,
-
- /// CHECK: Wormhole Core Bridge needs the rent sysvar based on its legacy implementation.
- #[account(address = solana_program::sysvar::rent::id())]
- rent: AccountInfo<'info>,
-}
-
-/// TODO: add docstring
-pub fn settle_auction_active_local(ctx: Context) -> Result<()> {
- let super::SettledActive {
- user_amount: amount,
- fill,
- sequence_seed,
- } = super::settle_active_and_prepare_fill(super::SettleActiveAndPrepareFill {
- custodian: &ctx.accounts.custodian,
- auction_config: &ctx.accounts.auction_config,
- fast_vaa: &ctx.accounts.fast_vaa,
- auction: &mut ctx.accounts.auction,
- prepared_order_response: &ctx.accounts.prepared_order_response,
- executor_token: &ctx.accounts.executor_token,
- best_offer_token: &ctx.accounts.best_offer_token,
- cctp_mint_recipient: &ctx.accounts.cctp_mint_recipient,
- payer_sequence: &mut ctx.accounts.payer_sequence,
- token_program: &ctx.accounts.token_program,
- })?;
-
- // Publish message via Core Bridge.
- core_bridge_program::cpi::post_message(
- CpiContext::new_with_signer(
- ctx.accounts.core_bridge_program.to_account_info(),
- core_bridge_program::cpi::PostMessage {
- payer: ctx.accounts.payer.to_account_info(),
- message: ctx.accounts.core_message.to_account_info(),
- emitter: ctx.accounts.custodian.to_account_info(),
- config: ctx.accounts.core_bridge_config.to_account_info(),
- emitter_sequence: ctx.accounts.core_emitter_sequence.to_account_info(),
- fee_collector: ctx.accounts.core_fee_collector.to_account_info(),
- system_program: ctx.accounts.system_program.to_account_info(),
- clock: ctx.accounts.clock.to_account_info(),
- rent: ctx.accounts.rent.to_account_info(),
- },
- &[
- Custodian::SIGNER_SEEDS,
- &[
- common::constants::CORE_MESSAGE_SEED_PREFIX,
- ctx.accounts.payer.key().as_ref(),
- sequence_seed.as_ref(),
- &[ctx.bumps.core_message],
- ],
- ],
- ),
- core_bridge_program::cpi::PostMessageArgs {
- nonce: common::constants::WORMHOLE_MESSAGE_NONCE,
- payload: common::messages::FastFill { amount, fill }.to_vec_payload(),
- commitment: core_bridge_program::Commitment::Finalized,
- },
- )
-}
diff --git a/solana/programs/matching-engine/src/processor/auction/settle/active/mod.rs b/solana/programs/matching-engine/src/processor/auction/settle/active/mod.rs
deleted file mode 100644
index 6ae45c28a..000000000
--- a/solana/programs/matching-engine/src/processor/auction/settle/active/mod.rs
+++ /dev/null
@@ -1,141 +0,0 @@
-mod cctp;
-pub use cctp::*;
-
-mod local;
-pub use local::*;
-
-use crate::{
- state::{
- Auction, AuctionConfig, AuctionStatus, Custodian, PayerSequence, PreparedOrderResponse,
- },
- utils::{self, auction::DepositPenalty},
-};
-use anchor_lang::prelude::*;
-use anchor_spl::token;
-use common::{
- messages::{
- raw::{LiquidityLayerMessage, MessageToVec},
- Fill,
- },
- wormhole_cctp_solana::wormhole::VaaAccount,
-};
-
-struct SettleActiveAndPrepareFill<'ctx, 'info> {
- custodian: &'ctx AccountInfo<'info>,
- auction_config: &'ctx Account<'info, AuctionConfig>,
- fast_vaa: &'ctx AccountInfo<'info>,
- auction: &'ctx mut Account<'info, Auction>,
- prepared_order_response: &'ctx Account<'info, PreparedOrderResponse>,
- executor_token: &'ctx AccountInfo<'info>,
- best_offer_token: &'ctx AccountInfo<'info>,
- cctp_mint_recipient: &'ctx AccountInfo<'info>,
- payer_sequence: &'ctx mut Account<'info, PayerSequence>,
- token_program: &'ctx Program<'info, token::Token>,
-}
-
-struct SettledActive {
- user_amount: u64,
- fill: Fill,
- sequence_seed: [u8; 8],
-}
-
-fn settle_active_and_prepare_fill(
- accounts: SettleActiveAndPrepareFill<'_, '_>,
-) -> Result {
- let SettleActiveAndPrepareFill {
- custodian,
- auction_config,
- fast_vaa,
- auction,
- prepared_order_response,
- executor_token,
- best_offer_token,
- cctp_mint_recipient,
- payer_sequence,
- token_program,
- } = accounts;
-
- let fast_vaa = VaaAccount::load_unchecked(fast_vaa);
- let order = LiquidityLayerMessage::try_from(fast_vaa.payload())
- .unwrap()
- .to_fast_market_order_unchecked();
-
- // This means the slow message beat the fast message. We need to refund the bidder and
- // (potentially) take a penalty for not fulfilling their obligation. The `penalty` CAN be zero
- // in this case, since the auction grace period might not have ended yet.
- let (executor_amount, mut best_offer_amount, user_amount, final_status) = {
- let auction_info = auction.info.as_ref().unwrap();
-
- let DepositPenalty {
- penalty,
- user_reward,
- } = utils::auction::compute_deposit_penalty(
- auction_config,
- auction.info.as_ref().unwrap(),
- Clock::get().map(|clock| clock.slot)?,
- );
-
- // TODO: do math to adjust base fee and reward by amount_out / amount_in.
- let base_fee = accounts.prepared_order_response.base_fee;
-
- // NOTE: The sum of all amounts should be 2 * amount_in + security_deposit.
- // * amount_in + security_deposit comes from the auction participation.
- // * amount_in comes from the inbound transfer.
- (
- penalty + base_fee,
- auction_info.total_deposit() - penalty - user_reward,
- auction_info.amount_in + user_reward - base_fee,
- AuctionStatus::Settled {
- base_fee,
- penalty: Some(penalty),
- },
- )
- };
-
- if executor_token.key() != best_offer_token.key() {
- // Transfer the penalty amount to the caller. The caller also earns the base fee for relaying
- // the slow VAA.
- token::transfer(
- CpiContext::new_with_signer(
- token_program.to_account_info(),
- token::Transfer {
- from: cctp_mint_recipient.to_account_info(),
- to: executor_token.to_account_info(),
- authority: custodian.to_account_info(),
- },
- &[Custodian::SIGNER_SEEDS],
- ),
- executor_amount,
- )?;
- } else {
- best_offer_amount += executor_amount;
- }
-
- // Transfer to the best offer token what he deserves.
- token::transfer(
- CpiContext::new_with_signer(
- token_program.to_account_info(),
- token::Transfer {
- from: cctp_mint_recipient.to_account_info(),
- to: best_offer_token.to_account_info(),
- authority: custodian.to_account_info(),
- },
- &[Custodian::SIGNER_SEEDS],
- ),
- best_offer_amount,
- )?;
-
- // Everyone's whole, set the auction as completed.
- auction.status = final_status;
-
- Ok(SettledActive {
- user_amount,
- fill: Fill {
- source_chain: prepared_order_response.source_chain,
- order_sender: order.sender(),
- redeemer: order.redeemer(),
- redeemer_message: order.message_to_vec().into(),
- },
- sequence_seed: payer_sequence.take_and_uptick().to_be_bytes(),
- })
-}
diff --git a/solana/programs/matching-engine/src/processor/auction/settle/complete.rs b/solana/programs/matching-engine/src/processor/auction/settle/complete.rs
index 7ba42d213..d79a79aa4 100644
--- a/solana/programs/matching-engine/src/processor/auction/settle/complete.rs
+++ b/solana/programs/matching-engine/src/processor/auction/settle/complete.rs
@@ -1,100 +1,168 @@
use crate::{
error::MatchingEngineError,
- state::{Auction, AuctionStatus, Custodian, PreparedOrderResponse},
+ state::{Auction, AuctionStatus, PreparedOrderResponse},
};
use anchor_lang::prelude::*;
use anchor_spl::token;
#[derive(Accounts)]
pub struct SettleAuctionComplete<'info> {
- /// This program's Wormhole (Core Bridge) emitter authority.
- ///
- /// CHECK: Seeds must be \["emitter"\].
+ /// CHECK: To prevent squatters from preparing order responses on behalf of the auction winner,
+ /// we will always reward the owner of the executor token account with the lamports from the
+ /// prepared order response and its custody token account when we close these accounts. This
+ /// means we disregard the `prepared_by` field in the prepared order response.
+ #[account(mut)]
+ executor: AccountInfo<'info>,
+
#[account(
- seeds = [Custodian::SEED_PREFIX],
- bump = Custodian::BUMP,
+ mut,
+ associated_token::mint = best_offer_token.mint,
+ associated_token::authority = executor,
)]
- custodian: Account<'info, Custodian>,
+ executor_token: Account<'info, token::TokenAccount>,
- /// CHECK: Must be the account that created the prepared slow order.
- #[account(mut)]
- prepared_by: AccountInfo<'info>,
+ /// Destination token account, which the redeemer may not own. But because the redeemer is a
+ /// signer and is the one encoded in the Deposit Fill message, he may have the tokens be sent
+ /// to any account he chooses (this one).
+ ///
+ /// CHECK: This token account must already exist.
+ #[account(
+ mut,
+ address = auction.info.as_ref().unwrap().best_offer_token,
+ )]
+ best_offer_token: Account<'info, token::TokenAccount>,
#[account(
mut,
- close = prepared_by,
+ close = executor,
seeds = [
PreparedOrderResponse::SEED_PREFIX,
- prepared_by.key().as_ref(),
prepared_order_response.fast_vaa_hash.as_ref()
],
bump = prepared_order_response.bump,
)]
prepared_order_response: Account<'info, PreparedOrderResponse>,
+ /// CHECK: Seeds must be \["prepared-custody"\, prepared_order_response.key()].
#[account(
+ mut,
seeds = [
- Auction::SEED_PREFIX,
- prepared_order_response.fast_vaa_hash.as_ref(),
+ crate::PREPARED_CUSTODY_TOKEN_SEED_PREFIX,
+ prepared_order_response.key().as_ref(),
],
- bump = auction.bump,
- constraint = {
- require!(
- matches!(auction.status, AuctionStatus::Completed { .. }),
- MatchingEngineError::AuctionNotCompleted,
- );
-
- require_keys_eq!(
- best_offer_token.key(),
- auction.info.as_ref().unwrap().best_offer_token,
- MatchingEngineError::BestOfferTokenMismatch,
- );
- true
- }
+ bump,
)]
- auction: Account<'info, Auction>,
+ prepared_custody_token: AccountInfo<'info>,
- /// Destination token account, which the redeemer may not own. But because the redeemer is a
- /// signer and is the one encoded in the Deposit Fill message, he may have the tokens be sent
- /// to any account he chooses (this one).
- ///
- /// CHECK: This token account must already exist.
- #[account(mut)]
- best_offer_token: AccountInfo<'info>,
-
- /// Mint recipient token account, which is encoded as the mint recipient in the CCTP message.
- /// The CCTP Token Messenger Minter program will transfer the amount encoded in the CCTP message
- /// from its custody account to this account.
- ///
- /// Mutable. Seeds must be \["custody"\].
- ///
- /// NOTE: This account must be encoded as the mint recipient in the CCTP message.
#[account(
mut,
- address = crate::cctp_mint_recipient::id() @ MatchingEngineError::InvalidCustodyToken,
+ seeds = [
+ Auction::SEED_PREFIX,
+ prepared_order_response.fast_vaa_hash.as_ref(),
+ ],
+ bump = auction.bump,
)]
- cctp_mint_recipient: Account<'info, token::TokenAccount>,
+ auction: Account<'info, Auction>,
token_program: Program<'info, token::Token>,
}
pub fn settle_auction_complete(ctx: Context) -> Result<()> {
+ match ctx.accounts.auction.status {
+ AuctionStatus::Completed {
+ slot: _,
+ execute_penalty,
+ } => handle_settle_auction_complete(ctx, execute_penalty),
+ _ => err!(MatchingEngineError::AuctionNotCompleted),
+ }
+}
+
+fn handle_settle_auction_complete(
+ ctx: Context,
+ execute_penalty: Option,
+) -> Result<()> {
+ let prepared_order_response = &ctx.accounts.prepared_order_response;
+ let base_fee = prepared_order_response.base_fee;
+
ctx.accounts.auction.status = AuctionStatus::Settled {
- base_fee: ctx.accounts.prepared_order_response.base_fee,
- penalty: None,
+ base_fee,
+ total_penalty: execute_penalty.map(|v| v + base_fee),
+ };
+
+ let executor_token = &ctx.accounts.executor_token;
+ let best_offer_token = &ctx.accounts.best_offer_token;
+ let prepared_custody_token = &ctx.accounts.prepared_custody_token;
+ let token_program = &ctx.accounts.token_program;
+
+ let prepared_order_response_signer_seeds = &[
+ PreparedOrderResponse::SEED_PREFIX,
+ prepared_order_response.fast_vaa_hash.as_ref(),
+ &[prepared_order_response.bump],
+ ];
+
+ // We may deduct from this account if the winning participant was penalized.
+ let mut repayment = ctx.accounts.auction.info.as_ref().unwrap().amount_in;
+
+ match execute_penalty {
+ None => {
+ // If there is no penalty, we require that the executor token and best offer token be
+ // equal. The winning offer should not be penalized for calling this instruction when he
+ // has executed the order within the grace period.
+ //
+ // By requiring that these pubkeys are equal, we enforce that the owner of the best
+ // offer token gets rewarded the lamports from the prepared order response and its
+ // custody account.
+ require_keys_eq!(
+ executor_token.key(),
+ best_offer_token.key(),
+ MatchingEngineError::ExecutorTokenMismatch
+ )
+ }
+ _ => {
+ if executor_token.key() != best_offer_token.key() {
+ // Because the auction participant was penalized for executing the order late, he
+ // will be deducted the base fee. This base fee will be sent to the executor token
+ // account if it is not the same as the best offer token account.
+ token::transfer(
+ CpiContext::new_with_signer(
+ token_program.to_account_info(),
+ token::Transfer {
+ from: prepared_custody_token.to_account_info(),
+ to: executor_token.to_account_info(),
+ authority: prepared_order_response.to_account_info(),
+ },
+ &[prepared_order_response_signer_seeds],
+ ),
+ base_fee,
+ )?;
+
+ repayment -= base_fee;
+ }
+ }
};
- // Finally transfer the funds back to the highest bidder.
+ // Transfer the funds back to the highest bidder.
token::transfer(
CpiContext::new_with_signer(
- ctx.accounts.token_program.to_account_info(),
+ token_program.to_account_info(),
token::Transfer {
- from: ctx.accounts.cctp_mint_recipient.to_account_info(),
- to: ctx.accounts.best_offer_token.to_account_info(),
- authority: ctx.accounts.custodian.to_account_info(),
+ from: prepared_custody_token.to_account_info(),
+ to: best_offer_token.to_account_info(),
+ authority: prepared_order_response.to_account_info(),
},
- &[Custodian::SIGNER_SEEDS],
+ &[prepared_order_response_signer_seeds],
),
- ctx.accounts.auction.info.as_ref().unwrap().amount_in,
- )
+ repayment,
+ )?;
+
+ // Finally close the prepared custody token account.
+ token::close_account(CpiContext::new_with_signer(
+ token_program.to_account_info(),
+ token::CloseAccount {
+ account: prepared_custody_token.to_account_info(),
+ destination: ctx.accounts.executor.to_account_info(),
+ authority: prepared_order_response.to_account_info(),
+ },
+ &[prepared_order_response_signer_seeds],
+ ))
}
diff --git a/solana/programs/matching-engine/src/processor/auction/settle/mod.rs b/solana/programs/matching-engine/src/processor/auction/settle/mod.rs
index 9f3ec74fe..1d520d9af 100644
--- a/solana/programs/matching-engine/src/processor/auction/settle/mod.rs
+++ b/solana/programs/matching-engine/src/processor/auction/settle/mod.rs
@@ -1,5 +1,5 @@
-mod active;
-pub use active::*;
+// mod active;
+// pub use active::*;
mod complete;
pub use complete::*;
diff --git a/solana/programs/matching-engine/src/processor/auction/settle/none/cctp.rs b/solana/programs/matching-engine/src/processor/auction/settle/none/cctp.rs
index f17969f20..fd6b2b7aa 100644
--- a/solana/programs/matching-engine/src/processor/auction/settle/none/cctp.rs
+++ b/solana/programs/matching-engine/src/processor/auction/settle/none/cctp.rs
@@ -1,27 +1,17 @@
use crate::{
+ composite::*,
error::MatchingEngineError,
- state::{
- Auction, Custodian, MessageProtocol, PayerSequence, PreparedOrderResponse, RouterEndpoint,
- },
+ state::{Auction, Custodian, MessageProtocol, PayerSequence, RouterEndpoint},
+ utils,
};
use anchor_lang::prelude::*;
use anchor_spl::token;
-use common::{
- wormhole_cctp_solana::{
- self,
- cctp::{message_transmitter_program, token_messenger_minter_program},
- wormhole::{core_bridge_program, VaaAccount, SOLANA_CHAIN},
- },
- wormhole_io::TypePrefixedPayload,
-};
+use common::{wormhole_cctp_solana, wormhole_io::TypePrefixedPayload};
/// Accounts required for [settle_auction_none_cctp].
#[derive(Accounts)]
pub struct SettleAuctionNoneCctp<'info> {
- #[account(
- mut,
- address = prepared_order_response.prepared_by, // TODO: add err
- )]
+ #[account(mut)]
payer: Signer<'info>,
#[account(
@@ -36,185 +26,79 @@ pub struct SettleAuctionNoneCctp<'info> {
)]
payer_sequence: Box>,
- /// This program's Wormhole (Core Bridge) emitter authority.
- ///
- /// CHECK: Seeds must be \["emitter"\].
- #[account(
- seeds = [Custodian::SEED_PREFIX],
- bump = Custodian::BUMP,
- has_one = fee_recipient_token @ MatchingEngineError::FeeRecipientTokenMismatch,
- )]
- custodian: Box>,
-
- /// CHECK: Must be owned by the Wormhole Core Bridge program. This account will be read via
- /// zero-copy using the [VaaAccount](core_bridge_program::sdk::VaaAccount) reader.
- #[account(owner = core_bridge_program::id())]
- fast_vaa: AccountInfo<'info>,
-
- // /// CHECK: Must be the account that created the prepared slow order. This account will most
- // /// likely be the same as the payer.
- // #[account(mut)]
- // prepared_by: AccountInfo<'info>,
+ /// CHECK: Mutable. Seeds must be \["core-msg", payer, payer_sequence.value\].
#[account(
mut,
- close = payer,
seeds = [
- PreparedOrderResponse::SEED_PREFIX,
+ common::constants::CORE_MESSAGE_SEED_PREFIX,
payer.key().as_ref(),
- VaaAccount::load(&fast_vaa)?.digest().as_ref()
+ &payer_sequence.value.to_be_bytes(),
],
- bump = prepared_order_response.bump,
+ bump,
)]
- prepared_order_response: Box>,
+ core_message: AccountInfo<'info>,
- /// There should be no account data here because an auction was never created.
+ /// CHECK: Mutable. Seeds must be \["cctp-msg", payer, payer_sequence.value\].
#[account(
- init,
- payer = payer,
- space = 8 + Auction::INIT_SPACE_NO_AUCTION,
+ mut,
seeds = [
- Auction::SEED_PREFIX,
- prepared_order_response.fast_vaa_hash.as_ref(),
+ common::constants::CCTP_MESSAGE_SEED_PREFIX,
+ payer.key().as_ref(),
+ &payer_sequence.value.to_be_bytes(),
],
- bump
+ bump,
)]
- auction: Box>,
+ cctp_message: AccountInfo<'info>,
- /// Mint recipient token account, which is encoded as the mint recipient in the CCTP message.
- /// The CCTP Token Messenger Minter program will transfer the amount encoded in the CCTP message
- /// from its custody account to this account.
- ///
- /// CHECK: Mutable. Seeds must be \["custody"\].
- ///
- /// NOTE: This account must be encoded as the mint recipient in the CCTP message.
- #[account(
- mut,
- address = crate::cctp_mint_recipient::id() @ MatchingEngineError::InvalidCustodyToken,
- )]
- cctp_mint_recipient: AccountInfo<'info>,
+ custodian: CheckedCustodian<'info>,
/// Destination token account, which the redeemer may not own. But because the redeemer is a
/// signer and is the one encoded in the Deposit Fill message, he may have the tokens be sent
/// to any account he chooses (this one).
///
/// CHECK: This token account must already exist.
- #[account(mut)]
- fee_recipient_token: AccountInfo<'info>,
-
- /// Seeds must be \["endpoint", chain.to_be_bytes()\].
- #[account(
- seeds = [
- RouterEndpoint::SEED_PREFIX,
- from_router_endpoint.chain.to_be_bytes().as_ref(),
- ],
- bump = from_router_endpoint.bump,
- )]
- from_router_endpoint: Box>,
-
- /// Seeds must be \["endpoint", chain.to_be_bytes()\].
- #[account(
- seeds = [
- RouterEndpoint::SEED_PREFIX,
- to_router_endpoint.chain.to_be_bytes().as_ref(),
- ],
- bump = to_router_endpoint.bump,
- constraint = {
- to_router_endpoint.chain != SOLANA_CHAIN
- } @ MatchingEngineError::InvalidChain
- )]
- to_router_endpoint: Box>,
-
- /// Circle-supported mint.
- ///
- /// CHECK: Mutable. This token account's mint must be the same as the one found in the CCTP
- /// Token Messenger Minter program's local token account.
#[account(
mut,
- address = common::constants::USDC_MINT,
+ address = custodian.fee_recipient_token,
)]
- mint: AccountInfo<'info>,
-
- /// CHECK: Seeds must be \["Bridge"\] (Wormhole Core Bridge program).
- #[account(mut)]
- core_bridge_config: UncheckedAccount<'info>,
+ fee_recipient_token: AccountInfo<'info>,
- /// CHECK: Mutable. Seeds must be \["core-msg", payer, payer_sequence.value\].
#[account(
- mut,
- seeds = [
- common::constants::CORE_MESSAGE_SEED_PREFIX,
- payer.key().as_ref(),
- payer_sequence.value.to_be_bytes().as_ref(),
- ],
- bump,
+ constraint = utils::require_vaa_hash_equals(
+ &prepared,
+ &fast_order_path.fast_vaa.load_unchecked()
+ )?,
)]
- core_message: AccountInfo<'info>,
+ prepared: ClosePreparedOrderResponse<'info>,
- /// CHECK: Mutable. Seeds must be \["cctp-msg", payer, payer_sequence.value\].
+ fast_order_path: FastOrderPath<'info>,
+
+ /// There should be no account data here because an auction was never created.
#[account(
- mut,
+ init,
+ payer = payer,
+ space = 8 + Auction::INIT_SPACE_NO_AUCTION,
seeds = [
- common::constants::CCTP_MESSAGE_SEED_PREFIX,
- payer.key().as_ref(),
- payer_sequence.value.to_be_bytes().as_ref(),
+ Auction::SEED_PREFIX,
+ prepared.order_response.fast_vaa_hash.as_ref(),
],
- bump,
+ bump
)]
- cctp_message: AccountInfo<'info>,
-
- /// CHECK: Seeds must be \["Sequence"\, custodian] (Wormhole Core Bridge program).
- #[account(mut)]
- core_emitter_sequence: UncheckedAccount<'info>,
-
- /// CHECK: Seeds must be \["fee_collector"\] (Wormhole Core Bridge program).
- #[account(mut)]
- core_fee_collector: UncheckedAccount<'info>,
-
- /// CHECK: Seeds must be \["sender_authority"\] (CCTP Token Messenger Minter program).
- token_messenger_minter_sender_authority: UncheckedAccount<'info>,
-
- /// CHECK: Mutable. Seeds must be \["message_transmitter"\] (CCTP Message Transmitter program).
- #[account(mut)]
- message_transmitter_config: UncheckedAccount<'info>,
-
- /// CHECK: Seeds must be \["token_messenger"\] (CCTP Token Messenger Minter program).
- token_messenger: UncheckedAccount<'info>,
-
- /// CHECK: Seeds must be \["remote_token_messenger"\, remote_domain.to_string()] (CCTP Token
- /// Messenger Minter program).
- remote_token_messenger: UncheckedAccount<'info>,
-
- /// CHECK Seeds must be \["token_minter"\] (CCTP Token Messenger Minter program).
- token_minter: UncheckedAccount<'info>,
+ auction: Box>,
- /// Local token account, which this program uses to validate the `mint` used to burn.
- ///
- /// CHECK: Mutable. Seeds must be \["local_token", mint\] (CCTP Token Messenger Minter program).
- #[account(mut)]
- local_token: UncheckedAccount<'info>,
+ wormhole: WormholePublishMessage<'info>,
- /// CHECK: Seeds must be \["__event_authority"\] (CCTP Token Messenger Minter program).
- token_messenger_minter_event_authority: UncheckedAccount<'info>,
+ cctp: CctpDepositForBurn<'info>,
- core_bridge_program: Program<'info, core_bridge_program::CoreBridge>,
- token_messenger_minter_program:
- Program<'info, token_messenger_minter_program::TokenMessengerMinter>,
- message_transmitter_program: Program<'info, message_transmitter_program::MessageTransmitter>,
token_program: Program<'info, token::Token>,
system_program: Program<'info, System>,
- /// CHECK: Wormhole Core Bridge needs the clock sysvar based on its legacy implementation.
- #[account(address = solana_program::sysvar::clock::id())]
- clock: AccountInfo<'info>,
-
- /// CHECK: Wormhole Core Bridge needs the rent sysvar based on its legacy implementation.
- #[account(address = solana_program::sysvar::rent::id())]
- rent: AccountInfo<'info>,
+ sysvars: RequiredSysvars<'info>,
}
/// TODO: add docstring
pub fn settle_auction_none_cctp(ctx: Context) -> Result<()> {
- match ctx.accounts.to_router_endpoint.protocol {
+ match ctx.accounts.fast_order_path.to.protocol {
MessageProtocol::Cctp { domain } => handle_settle_auction_none_cctp(ctx, domain),
_ => err!(MatchingEngineError::InvalidCctpEndpoint),
}
@@ -231,24 +115,31 @@ fn handle_settle_auction_none_cctp(
sequence_seed,
} = super::settle_none_and_prepare_fill(
super::SettleNoneAndPrepareFill {
- custodian: &ctx.accounts.custodian,
- prepared_order_response: &ctx.accounts.prepared_order_response,
- fast_vaa: &ctx.accounts.fast_vaa,
+ payer_sequence: &mut ctx.accounts.payer_sequence,
+ fast_vaa: &ctx.accounts.fast_order_path.fast_vaa,
+ prepared_order_response: &ctx.accounts.prepared.order_response,
+ prepared_custody_token: &ctx.accounts.prepared.custody_token,
auction: &mut ctx.accounts.auction,
- from_router_endpoint: &ctx.accounts.from_router_endpoint,
- to_router_endpoint: &ctx.accounts.to_router_endpoint,
fee_recipient_token: &ctx.accounts.fee_recipient_token,
- cctp_mint_recipient: &ctx.accounts.cctp_mint_recipient,
- payer_sequence: &mut ctx.accounts.payer_sequence,
+ dst_token: &ctx.accounts.cctp.burn_source,
token_program: &ctx.accounts.token_program,
},
ctx.bumps.auction,
)?;
+ let RouterEndpoint {
+ bump: _,
+ chain: _,
+ address: destination_caller,
+ mint_recipient,
+ protocol: _,
+ } = ctx.accounts.fast_order_path.to.as_ref();
+
// This returns the CCTP nonce, but we do not need it.
wormhole_cctp_solana::cpi::burn_and_publish(
CpiContext::new_with_signer(
ctx.accounts
+ .cctp
.token_messenger_minter_program
.to_account_info(),
wormhole_cctp_solana::cpi::DepositForBurnWithCaller {
@@ -256,31 +147,36 @@ fn handle_settle_auction_none_cctp(
payer: ctx.accounts.payer.to_account_info(),
token_messenger_minter_sender_authority: ctx
.accounts
+ .cctp
.token_messenger_minter_sender_authority
.to_account_info(),
- burn_token: ctx.accounts.cctp_mint_recipient.to_account_info(),
+ burn_token: ctx.accounts.cctp.burn_source.to_account_info(),
message_transmitter_config: ctx
.accounts
+ .cctp
.message_transmitter_config
.to_account_info(),
- token_messenger: ctx.accounts.token_messenger.to_account_info(),
- remote_token_messenger: ctx.accounts.remote_token_messenger.to_account_info(),
- token_minter: ctx.accounts.token_minter.to_account_info(),
- local_token: ctx.accounts.local_token.to_account_info(),
- mint: ctx.accounts.mint.to_account_info(),
+ token_messenger: ctx.accounts.cctp.token_messenger.to_account_info(),
+ remote_token_messenger: ctx.accounts.cctp.remote_token_messenger.to_account_info(),
+ token_minter: ctx.accounts.cctp.token_minter.to_account_info(),
+ local_token: ctx.accounts.cctp.local_token.to_account_info(),
+ mint: ctx.accounts.cctp.mint.to_account_info(),
cctp_message: ctx.accounts.cctp_message.to_account_info(),
message_transmitter_program: ctx
.accounts
+ .cctp
.message_transmitter_program
.to_account_info(),
token_messenger_minter_program: ctx
.accounts
+ .cctp
.token_messenger_minter_program
.to_account_info(),
token_program: ctx.accounts.token_program.to_account_info(),
system_program: ctx.accounts.system_program.to_account_info(),
event_authority: ctx
.accounts
+ .cctp
.token_messenger_minter_event_authority
.to_account_info(),
},
@@ -295,17 +191,17 @@ fn handle_settle_auction_none_cctp(
],
),
CpiContext::new_with_signer(
- ctx.accounts.core_bridge_program.to_account_info(),
+ ctx.accounts.wormhole.core_bridge_program.to_account_info(),
wormhole_cctp_solana::cpi::PostMessage {
payer: ctx.accounts.payer.to_account_info(),
message: ctx.accounts.core_message.to_account_info(),
emitter: ctx.accounts.custodian.to_account_info(),
- config: ctx.accounts.core_bridge_config.to_account_info(),
- emitter_sequence: ctx.accounts.core_emitter_sequence.to_account_info(),
- fee_collector: ctx.accounts.core_fee_collector.to_account_info(),
+ config: ctx.accounts.wormhole.config.to_account_info(),
+ emitter_sequence: ctx.accounts.wormhole.emitter_sequence.to_account_info(),
+ fee_collector: ctx.accounts.wormhole.fee_collector.to_account_info(),
system_program: ctx.accounts.system_program.to_account_info(),
- clock: ctx.accounts.clock.to_account_info(),
- rent: ctx.accounts.rent.to_account_info(),
+ clock: ctx.accounts.sysvars.clock.to_account_info(),
+ rent: ctx.accounts.sysvars.rent.to_account_info(),
},
&[
Custodian::SIGNER_SEEDS,
@@ -319,11 +215,10 @@ fn handle_settle_auction_none_cctp(
),
wormhole_cctp_solana::cpi::BurnAndPublishArgs {
burn_source: None,
- destination_caller: ctx.accounts.to_router_endpoint.address,
+ destination_caller: *destination_caller,
destination_cctp_domain,
amount,
- // TODO: add mint recipient to the router endpoint account to future proof this?
- mint_recipient: ctx.accounts.to_router_endpoint.address,
+ mint_recipient: *mint_recipient,
wormhole_message_nonce: common::constants::WORMHOLE_MESSAGE_NONCE,
payload: fill.to_vec_payload(),
},
diff --git a/solana/programs/matching-engine/src/processor/auction/settle/none/local.rs b/solana/programs/matching-engine/src/processor/auction/settle/none/local.rs
index 8dd77f7b7..f554fe7df 100644
--- a/solana/programs/matching-engine/src/processor/auction/settle/none/local.rs
+++ b/solana/programs/matching-engine/src/processor/auction/settle/none/local.rs
@@ -1,23 +1,15 @@
use crate::{
- error::MatchingEngineError,
- state::{
- Auction, Custodian, MessageProtocol, PayerSequence, PreparedOrderResponse, RouterEndpoint,
- },
+ composite::*,
+ state::{Auction, PayerSequence},
+ utils,
};
use anchor_lang::prelude::*;
use anchor_spl::token;
-use common::{
- wormhole_cctp_solana::wormhole::{core_bridge_program, VaaAccount, SOLANA_CHAIN},
- wormhole_io::TypePrefixedPayload,
-};
/// Accounts required for [settle_auction_none_local].
#[derive(Accounts)]
pub struct SettleAuctionNoneLocal<'info> {
- #[account(
- mut,
- address = prepared_order_response.prepared_by, // TODO: add err
- )]
+ #[account(mut)]
payer: Signer<'info>,
#[account(
@@ -32,127 +24,73 @@ pub struct SettleAuctionNoneLocal<'info> {
)]
payer_sequence: Box>,
- /// This program's Wormhole (Core Bridge) emitter authority.
- ///
- /// CHECK: Seeds must be \["emitter"\].
- #[account(
- seeds = [Custodian::SEED_PREFIX],
- bump = Custodian::BUMP,
- has_one = fee_recipient_token @ MatchingEngineError::FeeRecipientTokenMismatch,
- )]
- custodian: Box>,
-
- /// CHECK: Must be owned by the Wormhole Core Bridge program. This account will be read via
- /// zero-copy using the [VaaAccount](core_bridge_program::sdk::VaaAccount) reader.
- #[account(owner = core_bridge_program::id())]
- fast_vaa: AccountInfo<'info>,
-
+ /// CHECK: Mutable. Seeds must be \["msg", payer, payer_sequence.value\].
#[account(
mut,
- close = payer,
seeds = [
- PreparedOrderResponse::SEED_PREFIX,
+ common::constants::CORE_MESSAGE_SEED_PREFIX,
payer.key().as_ref(),
- VaaAccount::load(&fast_vaa)?.digest().as_ref()
- ],
- bump = prepared_order_response.bump,
- )]
- prepared_order_response: Account<'info, PreparedOrderResponse>,
-
- /// There should be no account data here because an auction was never created.
- #[account(
- init,
- payer = payer,
- space = 8 + Auction::INIT_SPACE_NO_AUCTION,
- seeds = [
- Auction::SEED_PREFIX,
- prepared_order_response.fast_vaa_hash.as_ref(),
+ payer_sequence.value.to_be_bytes().as_ref(),
],
- bump
+ bump,
)]
- auction: Box>,
+ core_message: AccountInfo<'info>,
- /// Mint recipient token account, which is encoded as the mint recipient in the CCTP message.
- /// The CCTP Token Messenger Minter program will transfer the amount encoded in the CCTP message
- /// from its custody account to this account.
- ///
- /// CHECK: Mutable. Seeds must be \["custody"\].
- ///
- /// NOTE: This account must be encoded as the mint recipient in the CCTP message.
- #[account(
- mut,
- address = crate::cctp_mint_recipient::id() @ MatchingEngineError::InvalidCustodyToken,
- )]
- cctp_mint_recipient: AccountInfo<'info>,
+ custodian: CheckedCustodian<'info>,
/// Destination token account, which the redeemer may not own. But because the redeemer is a
/// signer and is the one encoded in the Deposit Fill message, he may have the tokens be sent
/// to any account he chooses (this one).
///
/// CHECK: This token account must already exist.
- #[account(mut)]
+ #[account(
+ mut,
+ address = custodian.fee_recipient_token,
+ )]
fee_recipient_token: AccountInfo<'info>,
- /// Seeds must be \["endpoint", chain.to_be_bytes()\].
#[account(
- seeds = [
- RouterEndpoint::SEED_PREFIX,
- from_router_endpoint.chain.to_be_bytes().as_ref(),
- ],
- bump = from_router_endpoint.bump,
- constraint = from_router_endpoint.protocol != MessageProtocol::None @ MatchingEngineError::EndpointDisabled,
- constraint = {
- from_router_endpoint.chain != SOLANA_CHAIN
- } @ MatchingEngineError::InvalidChain
+ constraint = utils::require_vaa_hash_equals(
+ &prepared,
+ &fast_order_path.fast_vaa.load_unchecked()
+ )?,
+ )]
+ prepared: ClosePreparedOrderResponse<'info>,
+
+ #[account(
+ constraint = utils::require_local_endpoint(&fast_order_path.to)?,
)]
- from_router_endpoint: Box>,
+ fast_order_path: FastOrderPath<'info>,
- /// Seeds must be \["endpoint", chain.to_be_bytes()\].
+ /// There should be no account data here because an auction was never created.
#[account(
+ init,
+ payer = payer,
+ space = 8 + Auction::INIT_SPACE_NO_AUCTION,
seeds = [
- RouterEndpoint::SEED_PREFIX,
- SOLANA_CHAIN.to_be_bytes().as_ref(),
+ Auction::SEED_PREFIX,
+ prepared.order_response.fast_vaa_hash.as_ref(),
],
- bump = to_router_endpoint.bump,
- constraint = to_router_endpoint.protocol != MessageProtocol::None @ MatchingEngineError::EndpointDisabled,
+ bump,
)]
- to_router_endpoint: Box>,
+ auction: Box>,
- /// CHECK: Seeds must be \["Bridge"\] (Wormhole Core Bridge program).
- #[account(mut)]
- core_bridge_config: UncheckedAccount<'info>,
+ wormhole: WormholePublishMessage<'info>,
- /// CHECK: Mutable. Seeds must be \["msg", payer, payer_sequence.value\].
#[account(
mut,
seeds = [
- common::constants::CORE_MESSAGE_SEED_PREFIX,
- payer.key().as_ref(),
- payer_sequence.value.to_be_bytes().as_ref(),
+ crate::LOCAL_CUSTODY_TOKEN_SEED_PREFIX,
+ &fast_order_path.fast_vaa.load_unchecked().emitter_chain().to_be_bytes(),
],
bump,
)]
- core_message: AccountInfo<'info>,
-
- /// CHECK: Seeds must be \["Sequence"\, custodian] (Wormhole Core Bridge program).
- #[account(mut)]
- core_emitter_sequence: UncheckedAccount<'info>,
+ local_custody_token: Box>,
- /// CHECK: Seeds must be \["fee_collector"\] (Wormhole Core Bridge program).
- #[account(mut)]
- core_fee_collector: UncheckedAccount<'info>,
-
- core_bridge_program: Program<'info, core_bridge_program::CoreBridge>,
token_program: Program<'info, token::Token>,
system_program: Program<'info, System>,
- /// CHECK: Wormhole Core Bridge needs the clock sysvar based on its legacy implementation.
- #[account(address = solana_program::sysvar::clock::id())]
- clock: AccountInfo<'info>,
-
- /// CHECK: Wormhole Core Bridge needs the rent sysvar based on its legacy implementation.
- #[account(address = solana_program::sysvar::rent::id())]
- rent: AccountInfo<'info>,
+ sysvars: RequiredSysvars<'info>,
}
/// TODO: add docstring
@@ -163,49 +101,29 @@ pub fn settle_auction_none_local(ctx: Context) -> Result
sequence_seed,
} = super::settle_none_and_prepare_fill(
super::SettleNoneAndPrepareFill {
- custodian: &ctx.accounts.custodian,
- prepared_order_response: &ctx.accounts.prepared_order_response,
- fast_vaa: &ctx.accounts.fast_vaa,
+ payer_sequence: &mut ctx.accounts.payer_sequence,
+ fast_vaa: &ctx.accounts.fast_order_path.fast_vaa,
+ prepared_order_response: &ctx.accounts.prepared.order_response,
+ prepared_custody_token: &ctx.accounts.prepared.custody_token,
auction: &mut ctx.accounts.auction,
- from_router_endpoint: &ctx.accounts.from_router_endpoint,
- to_router_endpoint: &ctx.accounts.to_router_endpoint,
fee_recipient_token: &ctx.accounts.fee_recipient_token,
- cctp_mint_recipient: &ctx.accounts.cctp_mint_recipient,
- payer_sequence: &mut ctx.accounts.payer_sequence,
+ dst_token: &ctx.accounts.local_custody_token,
token_program: &ctx.accounts.token_program,
},
ctx.bumps.auction,
)?;
- // Publish message via Core Bridge.
- core_bridge_program::cpi::post_message(
- CpiContext::new_with_signer(
- ctx.accounts.core_bridge_program.to_account_info(),
- core_bridge_program::cpi::PostMessage {
- payer: ctx.accounts.payer.to_account_info(),
- message: ctx.accounts.core_message.to_account_info(),
- emitter: ctx.accounts.custodian.to_account_info(),
- config: ctx.accounts.core_bridge_config.to_account_info(),
- emitter_sequence: ctx.accounts.core_emitter_sequence.to_account_info(),
- fee_collector: ctx.accounts.core_fee_collector.to_account_info(),
- system_program: ctx.accounts.system_program.to_account_info(),
- clock: ctx.accounts.clock.to_account_info(),
- rent: ctx.accounts.rent.to_account_info(),
- },
- &[
- Custodian::SIGNER_SEEDS,
- &[
- common::constants::CORE_MESSAGE_SEED_PREFIX,
- ctx.accounts.payer.key().as_ref(),
- sequence_seed.as_ref(),
- &[ctx.bumps.core_message],
- ],
- ],
- ),
- core_bridge_program::cpi::PostMessageArgs {
- nonce: common::constants::WORMHOLE_MESSAGE_NONCE,
- payload: common::messages::FastFill { amount, fill }.to_vec_payload(),
- commitment: core_bridge_program::Commitment::Finalized,
+ utils::wormhole::post_matching_engine_message(
+ utils::wormhole::PostMatchingEngineMessage {
+ wormhole: &ctx.accounts.wormhole,
+ core_message: &ctx.accounts.core_message,
+ custodian: &ctx.accounts.custodian,
+ payer: &ctx.accounts.payer,
+ system_program: &ctx.accounts.system_program,
+ sysvars: &ctx.accounts.sysvars,
},
+ common::messages::FastFill { amount, fill },
+ &sequence_seed,
+ ctx.bumps.core_message,
)
}
diff --git a/solana/programs/matching-engine/src/processor/auction/settle/none/mod.rs b/solana/programs/matching-engine/src/processor/auction/settle/none/mod.rs
index 37216fbb1..28795af0a 100644
--- a/solana/programs/matching-engine/src/processor/auction/settle/none/mod.rs
+++ b/solana/programs/matching-engine/src/processor/auction/settle/none/mod.rs
@@ -4,29 +4,25 @@ pub use cctp::*;
mod local;
pub use local::*;
-use crate::state::{
- Auction, AuctionStatus, Custodian, PayerSequence, PreparedOrderResponse, RouterEndpoint,
+use crate::{
+ composite::*,
+ state::{Auction, AuctionStatus, PayerSequence, PreparedOrderResponse},
};
use anchor_lang::prelude::*;
use anchor_spl::token;
-use common::{
- messages::{
- raw::{LiquidityLayerMessage, MessageToVec},
- Fill,
- },
- wormhole_cctp_solana::wormhole::VaaAccount,
+use common::messages::{
+ raw::{LiquidityLayerMessage, MessageToVec},
+ Fill,
};
struct SettleNoneAndPrepareFill<'ctx, 'info> {
- custodian: &'ctx Account<'info, Custodian>,
+ payer_sequence: &'ctx mut Account<'info, PayerSequence>,
+ fast_vaa: &'ctx LiquidityLayerVaa<'info>,
prepared_order_response: &'ctx Account<'info, PreparedOrderResponse>,
- fast_vaa: &'ctx AccountInfo<'info>,
+ prepared_custody_token: &'ctx AccountInfo<'info>,
auction: &'ctx mut Account<'info, Auction>,
- from_router_endpoint: &'ctx Account<'info, RouterEndpoint>,
- to_router_endpoint: &'ctx Account<'info, RouterEndpoint>,
fee_recipient_token: &'ctx AccountInfo<'info>,
- cctp_mint_recipient: &'ctx AccountInfo<'info>,
- payer_sequence: &'ctx mut Account<'info, PayerSequence>,
+ dst_token: &'ctx Account<'info, token::TokenAccount>,
token_program: &'ctx Program<'info, token::Token>,
}
@@ -41,31 +37,26 @@ fn settle_none_and_prepare_fill(
auction_bump_seed: u8,
) -> Result {
let SettleNoneAndPrepareFill {
- custodian,
- prepared_order_response,
+ payer_sequence,
fast_vaa,
+ prepared_order_response,
+ prepared_custody_token,
auction,
- from_router_endpoint,
- to_router_endpoint,
fee_recipient_token,
- cctp_mint_recipient,
- payer_sequence,
+ dst_token,
token_program,
} = accounts;
- let fast_vaa = VaaAccount::load_unchecked(fast_vaa);
+ let fast_vaa = fast_vaa.load_unchecked();
let order = LiquidityLayerMessage::try_from(fast_vaa.payload())
.unwrap()
.to_fast_market_order_unchecked();
- // NOTE: We need to verify the router path, since an auction was never created and this check is
- // done in the `place_initial_offer` instruction.
- crate::utils::require_valid_router_path(
- &fast_vaa,
- from_router_endpoint,
- to_router_endpoint,
- order.target_chain(),
- )?;
+ let prepared_order_response_signer_seeds = &[
+ PreparedOrderResponse::SEED_PREFIX,
+ prepared_order_response.fast_vaa_hash.as_ref(),
+ &[prepared_order_response.bump],
+ ];
// Pay the `fee_recipient` the base fee. This ensures that the protocol relayer is paid for
// relaying slow VAAs that do not have an associated auction. This prevents the protocol relayer
@@ -75,15 +66,29 @@ fn settle_none_and_prepare_fill(
CpiContext::new_with_signer(
token_program.to_account_info(),
token::Transfer {
- from: cctp_mint_recipient.to_account_info(),
+ from: prepared_custody_token.to_account_info(),
to: fee_recipient_token.to_account_info(),
- authority: custodian.to_account_info(),
+ authority: prepared_order_response.to_account_info(),
},
- &[Custodian::SIGNER_SEEDS],
+ &[prepared_order_response_signer_seeds],
),
base_fee,
)?;
+ let user_amount = order.amount_in() - base_fee;
+ token::transfer(
+ CpiContext::new_with_signer(
+ token_program.to_account_info(),
+ token::Transfer {
+ from: prepared_custody_token.to_account_info(),
+ to: dst_token.to_account_info(),
+ authority: prepared_order_response.to_account_info(),
+ },
+ &[prepared_order_response_signer_seeds],
+ ),
+ user_amount,
+ )?;
+
// This is a necessary security check. This will prevent a relayer from starting an auction with
// the fast transfer VAA, even though the slow relayer already delivered the slow VAA. Not
// setting this could lead to trapped funds (which would require an upgrade to fix).
@@ -92,13 +97,13 @@ fn settle_none_and_prepare_fill(
vaa_hash: fast_vaa.digest().0,
status: AuctionStatus::Settled {
base_fee,
- penalty: None,
+ total_penalty: None,
},
info: None,
});
Ok(SettledNone {
- user_amount: order.amount_in() - base_fee,
+ user_amount,
fill: Fill {
source_chain: prepared_order_response.source_chain,
order_sender: order.sender(),
diff --git a/solana/programs/matching-engine/src/processor/complete_fast_fill.rs b/solana/programs/matching-engine/src/processor/complete_fast_fill.rs
index 1c281ddf9..a8ee3f1dc 100644
--- a/solana/programs/matching-engine/src/processor/complete_fast_fill.rs
+++ b/solana/programs/matching-engine/src/processor/complete_fast_fill.rs
@@ -1,13 +1,13 @@
+use crate::{
+ composite::*,
+ error::MatchingEngineError,
+ state::{RedeemedFastFill, RouterEndpoint},
+};
use anchor_lang::prelude::*;
use anchor_spl::token;
use common::{
messages::raw::LiquidityLayerMessage,
- wormhole_cctp_solana::wormhole::{core_bridge_program, VaaAccount, SOLANA_CHAIN},
-};
-
-use crate::{
- error::MatchingEngineError,
- state::{router_endpoint::*, Custodian, LiveRouterEndpoint, RedeemedFastFill},
+ wormhole_cctp_solana::wormhole::{VaaAccount, SOLANA_CHAIN},
};
/// Accounts required for [complete_fast_fill].
@@ -16,12 +16,27 @@ pub struct CompleteFastFill<'info> {
#[account(mut)]
payer: Signer<'info>,
- custodian: Account<'info, Custodian>,
+ custodian: CheckedCustodian<'info>,
+
+ #[account(
+ constraint = {
+ // Make sure that this VAA was emitted from the matching engine.
+ let vaa = fast_fill_vaa.load_unchecked();
+ require_eq!(
+ vaa.emitter_chain(),
+ SOLANA_CHAIN,
+ MatchingEngineError::InvalidEmitterForFastFill
+ );
+ require_keys_eq!(
+ Pubkey::from(vaa.emitter_address()),
+ custodian.key(),
+ MatchingEngineError::InvalidEmitterForFastFill
+ );
- /// CHECK: Must be owned by the Wormhole Core Bridge program. This account will be read via
- /// zero-copy using the [VaaAccount](core_bridge_program::sdk::VaaAccount) reader.
- #[account(owner = core_bridge_program::id())]
- vaa: AccountInfo<'info>,
+ true
+ }
+ )]
+ fast_fill_vaa: LiquidityLayerVaa<'info>,
#[account(
init,
@@ -29,7 +44,7 @@ pub struct CompleteFastFill<'info> {
space = 8 + RedeemedFastFill::INIT_SPACE,
seeds = [
RedeemedFastFill::SEED_PREFIX,
- VaaAccount::load(&vaa)?.digest().as_ref()
+ VaaAccount::load(&fast_fill_vaa)?.digest().as_ref()
],
bump
)]
@@ -40,7 +55,7 @@ pub struct CompleteFastFill<'info> {
#[account(
mut,
- token::mint = cctp_mint_recipient.mint,
+ token::mint = local_custody_token.mint,
token::authority = token_router_emitter,
)]
token_router_custody_token: Account<'info, token::TokenAccount>,
@@ -57,16 +72,25 @@ pub struct CompleteFastFill<'info> {
)]
router_endpoint: LiveRouterEndpoint<'info>,
- /// Mint recipient token account, which is encoded as the mint recipient in the CCTP message.
- /// The CCTP Token Messenger Minter program will transfer the amount encoded in the CCTP message
- /// from its custody account to this account.
- ///
- /// Mutable. Seeds must be \["custody"\].
#[account(
mut,
- address = crate::cctp_mint_recipient::id() @ MatchingEngineError::InvalidCustodyToken,
+ seeds = [
+ crate::LOCAL_CUSTODY_TOKEN_SEED_PREFIX,
+ {
+ let vaa = fast_fill_vaa.load_unchecked();
+ let msg = LiquidityLayerMessage::try_from(vaa.payload()).unwrap();
+
+ // Is this a fast fill?
+ let fast_fill = msg
+ .fast_fill()
+ .ok_or(MatchingEngineError::InvalidPayloadId)?;
+
+ &fast_fill.fill().source_chain().to_be_bytes()
+ },
+ ],
+ bump,
)]
- cctp_mint_recipient: Account<'info, token::TokenAccount>,
+ local_custody_token: Box>,
token_program: Program<'info, token::Token>,
system_program: Program<'info, System>,
@@ -74,49 +98,33 @@ pub struct CompleteFastFill<'info> {
/// TODO: docstring
pub fn complete_fast_fill(ctx: Context) -> Result<()> {
- let vaa = VaaAccount::load_unchecked(&ctx.accounts.vaa);
-
- // Emitter must be the matching engine (this program).
- {
- let emitter = vaa.emitter_info();
- require_eq!(
- emitter.chain,
- SOLANA_CHAIN,
- MatchingEngineError::InvalidEmitterForFastFill
- );
- require_keys_eq!(
- Pubkey::from(emitter.address),
- ctx.accounts.custodian.key(),
- MatchingEngineError::InvalidEmitterForFastFill
- );
-
- // Fill redeemed fast fill data.
- ctx.accounts.redeemed_fast_fill.set_inner(RedeemedFastFill {
- bump: ctx.bumps.redeemed_fast_fill,
- vaa_hash: vaa.digest().0,
- sequence: emitter.sequence,
- });
- }
-
- // Check whether this message is a deposit message we recognize.
- let msg = LiquidityLayerMessage::try_from(vaa.payload())
- .map_err(|_| error!(MatchingEngineError::InvalidVaa))?;
-
- // Is this a fast fill?
- let fast_fill = msg
- .fast_fill()
- .ok_or(MatchingEngineError::InvalidPayloadId)?;
+ let vaa = ctx.accounts.fast_fill_vaa.load_unchecked();
+
+ // Fill redeemed fast fill data.
+ ctx.accounts.redeemed_fast_fill.set_inner(RedeemedFastFill {
+ bump: ctx.bumps.redeemed_fast_fill,
+ vaa_hash: vaa.digest().0,
+ sequence: vaa.sequence(),
+ });
+
+ let fast_fill = LiquidityLayerMessage::try_from(vaa.payload())
+ .unwrap()
+ .to_fast_fill_unchecked();
// Finally transfer to local token router's token account.
token::transfer(
CpiContext::new_with_signer(
ctx.accounts.token_program.to_account_info(),
token::Transfer {
- from: ctx.accounts.cctp_mint_recipient.to_account_info(),
+ from: ctx.accounts.local_custody_token.to_account_info(),
to: ctx.accounts.token_router_custody_token.to_account_info(),
- authority: ctx.accounts.custodian.to_account_info(),
+ authority: ctx.accounts.router_endpoint.to_account_info(),
},
- &[Custodian::SIGNER_SEEDS],
+ &[&[
+ RouterEndpoint::SEED_PREFIX,
+ &ctx.accounts.router_endpoint.chain.to_be_bytes(),
+ &[ctx.accounts.router_endpoint.bump],
+ ]],
),
fast_fill.amount(),
)
diff --git a/solana/programs/matching-engine/src/state/auction.rs b/solana/programs/matching-engine/src/state/auction.rs
index 07d97def2..b6e187e7e 100644
--- a/solana/programs/matching-engine/src/state/auction.rs
+++ b/solana/programs/matching-engine/src/state/auction.rs
@@ -1,12 +1,19 @@
use crate::state::AuctionParameters;
use anchor_lang::prelude::*;
-#[derive(Debug, AnchorSerialize, AnchorDeserialize, Clone, InitSpace, PartialEq, Eq)]
+#[derive(Debug, AnchorSerialize, AnchorDeserialize, Clone, InitSpace, PartialEq, Eq, Default)]
pub enum AuctionStatus {
+ #[default]
NotStarted,
Active,
- Completed { slot: u64 },
- Settled { base_fee: u64, penalty: Option },
+ Completed {
+ slot: u64,
+ execute_penalty: Option,
+ },
+ Settled {
+ base_fee: u64,
+ total_penalty: Option,
+ },
}
impl std::fmt::Display for AuctionStatus {
@@ -14,12 +21,22 @@ impl std::fmt::Display for AuctionStatus {
match self {
AuctionStatus::NotStarted => write!(f, "NotStarted"),
AuctionStatus::Active => write!(f, "Active"),
- AuctionStatus::Completed { slot } => write!(f, "Completed {{ slot: {} }}", slot),
- AuctionStatus::Settled { base_fee, penalty } => {
+ AuctionStatus::Completed {
+ slot,
+ execute_penalty,
+ } => write!(
+ f,
+ "Completed {{ slot: {}, execute_penalty: {:?} }}",
+ slot, execute_penalty
+ ),
+ AuctionStatus::Settled {
+ base_fee,
+ total_penalty,
+ } => {
write!(
f,
- "Settled {{ base_fee: {}, penalty: {:?} }}",
- base_fee, penalty
+ "Settled {{ base_fee: {}, total_penalty: {:?} }}",
+ base_fee, total_penalty
)
}
}
@@ -30,6 +47,8 @@ impl std::fmt::Display for AuctionStatus {
pub struct AuctionInfo {
pub config_id: u32,
+ pub custody_token_bump: u8,
+
/// Sequence of the fast market order VAA.
pub vaa_sequence: u64,
@@ -58,6 +77,8 @@ pub struct AuctionInfo {
/// The amount of tokens to be sent to the user. For CCTP fast transfers, this amount will equal
/// the [amount_in](Self::amount_in).
pub amount_out: u64,
+
+ pub end_early: bool,
}
impl AuctionInfo {
diff --git a/solana/programs/matching-engine/src/state/custodian.rs b/solana/programs/matching-engine/src/state/custodian.rs
index 323b8fb1a..56929dec6 100644
--- a/solana/programs/matching-engine/src/state/custodian.rs
+++ b/solana/programs/matching-engine/src/state/custodian.rs
@@ -1,6 +1,4 @@
use anchor_lang::prelude::*;
-
-use crate::error::MatchingEngineError;
use common::admin;
#[account]
@@ -57,50 +55,6 @@ impl admin::OwnerAssistant for Custodian {
}
}
-#[derive(Accounts)]
-pub struct OwnerCustodian<'info> {
- pub owner: Signer<'info>,
-
- #[account(has_one = owner @ MatchingEngineError::OwnerOnly)]
- pub custodian: Account<'info, Custodian>,
-}
-
-#[derive(Accounts)]
-pub struct OwnerMutCustodian<'info> {
- pub owner: Signer<'info>,
-
- #[account(
- mut,
- has_one = owner @ MatchingEngineError::OwnerOnly,
- )]
- pub custodian: Account<'info, Custodian>,
-}
-
-#[derive(Accounts)]
-pub struct AdminCustodian<'info> {
- #[account(
- constraint = {
- admin::utils::assistant::only_authorized(&custodian, &owner_or_assistant.key())
- } @ MatchingEngineError::OwnerOrAssistantOnly,
- )]
- pub owner_or_assistant: Signer<'info>,
-
- pub custodian: Account<'info, Custodian>,
-}
-
-#[derive(Accounts)]
-pub struct AdminMutCustodian<'info> {
- #[account(
- constraint = {
- admin::utils::assistant::only_authorized(&custodian, &owner_or_assistant.key())
- } @ MatchingEngineError::OwnerOrAssistantOnly,
- )]
- pub owner_or_assistant: Signer<'info>,
-
- #[account(mut)]
- pub custodian: Account<'info, Custodian>,
-}
-
#[cfg(test)]
mod test {
use solana_program::pubkey::Pubkey;
diff --git a/solana/programs/matching-engine/src/state/mod.rs b/solana/programs/matching-engine/src/state/mod.rs
index 4027e94dd..cb5247f46 100644
--- a/solana/programs/matching-engine/src/state/mod.rs
+++ b/solana/programs/matching-engine/src/state/mod.rs
@@ -1,7 +1,7 @@
mod auction_config;
pub use auction_config::*;
-mod auction;
+pub(crate) mod auction;
pub use auction::*;
pub(crate) mod custodian;
diff --git a/solana/programs/matching-engine/src/state/router_endpoint.rs b/solana/programs/matching-engine/src/state/router_endpoint.rs
index 96ef3e37b..45f3dde89 100644
--- a/solana/programs/matching-engine/src/state/router_endpoint.rs
+++ b/solana/programs/matching-engine/src/state/router_endpoint.rs
@@ -1,6 +1,3 @@
-use std::ops::Deref;
-
-use crate::error::MatchingEngineError;
use anchor_lang::prelude::*;
#[derive(Debug, AnchorSerialize, AnchorDeserialize, Clone, PartialEq, Eq, InitSpace)]
@@ -38,47 +35,3 @@ pub struct RouterEndpoint {
impl RouterEndpoint {
pub const SEED_PREFIX: &'static [u8] = b"endpoint";
}
-
-#[derive(Accounts)]
-pub(crate) struct ExistingMutRouterEndpoint<'info> {
- #[account(
- mut,
- seeds = [
- RouterEndpoint::SEED_PREFIX,
- &inner.chain.to_be_bytes()
- ],
- bump = inner.bump,
- )]
- pub inner: Account<'info, RouterEndpoint>,
-}
-
-#[derive(Accounts)]
-pub struct LiveRouterEndpoint<'info> {
- #[account(
- seeds = [
- RouterEndpoint::SEED_PREFIX,
- &inner.chain.to_be_bytes()
- ],
- bump = inner.bump,
- constraint = {
- inner.protocol != MessageProtocol::None
- } @ MatchingEngineError::EndpointDisabled,
- )]
- pub inner: Account<'info, RouterEndpoint>,
-}
-
-impl<'info> Deref for LiveRouterEndpoint<'info> {
- type Target = RouterEndpoint;
-
- fn deref(&self) -> &Self::Target {
- &self.inner
- }
-}
-
-#[derive(Accounts)]
-pub struct LiveRouterEndpointPair<'info> {
- pub from: LiveRouterEndpoint<'info>,
-
- #[account(constraint = from.chain != to.chain @ MatchingEngineError::SameEndpoint)]
- pub to: LiveRouterEndpoint<'info>,
-}
diff --git a/solana/programs/matching-engine/src/utils/auction.rs b/solana/programs/matching-engine/src/utils/auction.rs
index 247404d24..62892f8cc 100644
--- a/solana/programs/matching-engine/src/utils/auction.rs
+++ b/solana/programs/matching-engine/src/utils/auction.rs
@@ -12,6 +12,7 @@ pub struct DepositPenalty {
}
#[inline]
+#[allow(clippy::cast_possible_truncation)]
pub fn compute_deposit_penalty(
params: &AuctionParameters,
info: &AuctionInfo,
@@ -41,8 +42,9 @@ pub fn compute_deposit_penalty(
}
#[inline]
-pub fn compute_min_offer_delta(params: &AuctionParameters, info: &AuctionInfo) -> u64 {
- mul_bps_unsafe(info.offer_price, params.min_offer_delta_bps)
+pub fn compute_min_allowed_offer(params: &AuctionParameters, info: &AuctionInfo) -> u64 {
+ info.offer_price
+ .saturating_sub(mul_bps_unsafe(info.offer_price, params.min_offer_delta_bps))
}
pub fn require_valid_parameters(params: &AuctionParameters) -> Result<()> {
@@ -81,6 +83,7 @@ fn split_user_penalty_reward(params: &AuctionParameters, amount: u64) -> Deposit
}
#[inline]
+#[allow(clippy::cast_possible_truncation)]
fn mul_bps_unsafe(amount: u64, bps: u32) -> u64 {
((amount as u128 * bps as u128) / FEE_PRECISION_MAX as u128) as u64
}
@@ -289,9 +292,8 @@ mod test {
let offer_price = 10000000;
let (info, _) = set_up(0, None, offer_price);
- let min_offer_delta = compute_min_offer_delta(¶ms, &info);
-
- assert_eq!(min_offer_delta, offer_price);
+ let allowed_offer = compute_min_allowed_offer(¶ms, &info);
+ assert_eq!(allowed_offer, 0);
}
#[test]
@@ -302,9 +304,8 @@ mod test {
let offer_price = 10000000;
let (info, _) = set_up(0, None, offer_price);
- let min_offer_delta = compute_min_offer_delta(¶ms, &info);
-
- assert_eq!(min_offer_delta, 0);
+ let allowed_offer = compute_min_allowed_offer(¶ms, &info);
+ assert_eq!(allowed_offer, offer_price);
}
#[test]
@@ -314,9 +315,8 @@ mod test {
let offer_price = 10000000;
let (info, _) = set_up(0, None, offer_price);
- let min_offer_delta = compute_min_offer_delta(¶ms, &info);
-
- assert_eq!(min_offer_delta, 500000);
+ let allowed_offer = compute_min_allowed_offer(¶ms, &info);
+ assert_eq!(allowed_offer, offer_price - 500000);
}
fn set_up(
@@ -328,6 +328,7 @@ mod test {
(
AuctionInfo {
security_deposit,
+ custody_token_bump: Default::default(),
vaa_sequence: Default::default(),
start_slot: START,
config_id: Default::default(),
@@ -337,6 +338,7 @@ mod test {
amount_in: Default::default(),
offer_price,
amount_out: Default::default(),
+ end_early: Default::default(),
},
START + slots_elapsed.unwrap_or_default(),
)
diff --git a/solana/programs/matching-engine/src/utils/mod.rs b/solana/programs/matching-engine/src/utils/mod.rs
index 7ea004091..b594a6c72 100644
--- a/solana/programs/matching-engine/src/utils/mod.rs
+++ b/solana/programs/matching-engine/src/utils/mod.rs
@@ -2,70 +2,43 @@ pub mod admin;
pub mod auction;
-use crate::{
- error::MatchingEngineError,
- state::{Auction, AuctionConfig, AuctionStatus, RouterEndpoint},
-};
-use anchor_lang::prelude::*;
-use common::wormhole_cctp_solana::wormhole::VaaAccount;
+pub(crate) mod wormhole;
-pub fn require_valid_router_path(
- vaa: &VaaAccount<'_>,
- source_endpoint: &RouterEndpoint,
- target_endpoint: &RouterEndpoint,
- expected_target_chain: u16,
-) -> Result<()> {
- let emitter = vaa.emitter_info();
- require_eq!(
- source_endpoint.chain,
- emitter.chain,
- MatchingEngineError::ErrInvalidSourceRouter
- );
- require!(
- source_endpoint.address == emitter.address,
- MatchingEngineError::ErrInvalidSourceRouter
- );
- require_eq!(
- target_endpoint.chain,
- expected_target_chain,
- MatchingEngineError::ErrInvalidTargetRouter
- );
+use crate::{error::MatchingEngineError, state::RouterEndpoint};
+use anchor_lang::prelude::*;
+use common::wormhole_cctp_solana::wormhole::{VaaAccount, SOLANA_CHAIN};
- Ok(())
+pub trait VaaDigest {
+ fn digest(&self) -> [u8; 32];
}
-pub fn is_valid_active_auction(
- config: &AuctionConfig,
- auction: &Auction,
- best_offer_token: Option,
- initial_offer_token: Option,
-) -> Result {
- match (&auction.status, &auction.info) {
- (AuctionStatus::Active, Some(info)) => {
- require_eq!(
- info.config_id,
- config.id,
- MatchingEngineError::AuctionConfigMismatch
- );
+#[derive(PartialEq, Eq)]
+struct WrappedHash([u8; 32]);
+
+impl std::fmt::Display for WrappedHash {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ write!(f, "0x{}", hex::encode(self.0))
+ }
+}
- if let Some(best_offer_token) = best_offer_token {
- require_keys_eq!(
- best_offer_token,
- info.best_offer_token,
- MatchingEngineError::BestOfferTokenMismatch,
- );
- }
+pub fn require_vaa_hash_equals(ctx: &A, vaa: &VaaAccount) -> Result
+where
+ A: VaaDigest,
+{
+ require_eq!(
+ WrappedHash(vaa.digest().0),
+ WrappedHash(ctx.digest()),
+ MatchingEngineError::InvalidVaa
+ );
+ Ok(true)
+}
- if let Some(initial_offer_token) = initial_offer_token {
- require_keys_eq!(
- initial_offer_token,
- info.initial_offer_token,
- MatchingEngineError::InitialOfferTokenMismatch,
- );
- }
+pub fn require_local_endpoint(endpoint: &RouterEndpoint) -> Result {
+ require_eq!(
+ endpoint.chain,
+ SOLANA_CHAIN,
+ MatchingEngineError::InvalidEndpoint
+ );
- Ok(true)
- }
- _ => err!(MatchingEngineError::AuctionNotActive),
- }
+ Ok(true)
}
diff --git a/solana/programs/matching-engine/src/utils/wormhole.rs b/solana/programs/matching-engine/src/utils/wormhole.rs
new file mode 100644
index 000000000..7ef214a66
--- /dev/null
+++ b/solana/programs/matching-engine/src/utils/wormhole.rs
@@ -0,0 +1,71 @@
+use crate::{composite::*, state::Custodian};
+use anchor_lang::prelude::*;
+use common::{
+ wormhole_cctp_solana::wormhole::core_bridge_program, wormhole_io::TypePrefixedPayload,
+};
+
+pub struct PostMatchingEngineMessage<'ctx, 'info> {
+ pub wormhole: &'ctx WormholePublishMessage<'info>,
+ pub core_message: &'ctx AccountInfo<'info>,
+ pub custodian: &'ctx CheckedCustodian<'info>,
+ pub payer: &'ctx Signer<'info>,
+ pub system_program: &'ctx Program<'info, System>,
+ pub sysvars: &'ctx RequiredSysvars<'info>,
+}
+
+pub fn post_matching_engine_message(
+ ctx: PostMatchingEngineMessage,
+ message: M,
+ sequence_seed: &[u8; 8],
+ core_message_bump_seed: u8,
+) -> Result<()>
+where
+ M: TypePrefixedPayload,
+{
+ let PostMatchingEngineMessage {
+ wormhole,
+ core_message,
+ custodian,
+ payer,
+ system_program,
+ sysvars,
+ } = ctx;
+
+ let WormholePublishMessage {
+ core_bridge_program,
+ config: core_bridge_config,
+ emitter_sequence,
+ fee_collector,
+ } = wormhole;
+
+ core_bridge_program::cpi::post_message(
+ CpiContext::new_with_signer(
+ core_bridge_program.to_account_info(),
+ core_bridge_program::cpi::PostMessage {
+ payer: payer.to_account_info(),
+ message: core_message.to_account_info(),
+ emitter: custodian.to_account_info(),
+ config: core_bridge_config.to_account_info(),
+ emitter_sequence: emitter_sequence.to_account_info(),
+ fee_collector: fee_collector.to_account_info(),
+ system_program: system_program.to_account_info(),
+ clock: sysvars.clock.to_account_info(),
+ rent: sysvars.rent.to_account_info(),
+ },
+ &[
+ Custodian::SIGNER_SEEDS,
+ &[
+ common::constants::CORE_MESSAGE_SEED_PREFIX,
+ payer.key().as_ref(),
+ sequence_seed,
+ &[core_message_bump_seed],
+ ],
+ ],
+ ),
+ core_bridge_program::cpi::PostMessageArgs {
+ nonce: common::constants::WORMHOLE_MESSAGE_NONCE,
+ payload: message.to_vec_payload(),
+ commitment: core_bridge_program::Commitment::Finalized,
+ },
+ )
+}
diff --git a/solana/programs/token-router/Cargo.toml b/solana/programs/token-router/Cargo.toml
index c09cb1433..3dcc2ed68 100644
--- a/solana/programs/token-router/Cargo.toml
+++ b/solana/programs/token-router/Cargo.toml
@@ -35,4 +35,7 @@ ruint.workspace = true
cfg-if.workspace = true
[dev-dependencies]
-hex-literal.workspace = true
\ No newline at end of file
+hex-literal.workspace = true
+
+[lints]
+workspace = true
\ No newline at end of file
diff --git a/solana/programs/token-router/src/processor/admin/set_pause.rs b/solana/programs/token-router/src/processor/admin/set_pause.rs
index 9ea275972..f72ff0eba 100644
--- a/solana/programs/token-router/src/processor/admin/set_pause.rs
+++ b/solana/programs/token-router/src/processor/admin/set_pause.rs
@@ -10,9 +10,11 @@ pub struct SetPause<'info> {
mut,
seeds = [Custodian::SEED_PREFIX],
bump = Custodian::BUMP,
- constraint = {
- only_authorized(&custodian, &owner_or_assistant.key())
- } @ TokenRouterError::OwnerOrAssistantOnly,
+ constraint = only_authorized(
+ &custodian,
+ &owner_or_assistant,
+ error!(TokenRouterError::OwnerOrAssistantOnly)
+ )?
)]
/// Sender Config account. This program requires that the `owner` specified
/// in the context equals the pubkey specified in this account. Mutable.
diff --git a/solana/programs/token-router/src/processor/admin/update/owner_assistant.rs b/solana/programs/token-router/src/processor/admin/update/owner_assistant.rs
index d099b3655..52266e3fa 100644
--- a/solana/programs/token-router/src/processor/admin/update/owner_assistant.rs
+++ b/solana/programs/token-router/src/processor/admin/update/owner_assistant.rs
@@ -28,7 +28,7 @@ pub struct UpdateOwnerAssistant<'info> {
pub fn update_owner_assistant(ctx: Context) -> Result<()> {
common::admin::utils::assistant::transfer_owner_assistant(
&mut ctx.accounts.custodian,
- &ctx.accounts.new_owner_assistant.key(),
+ &ctx.accounts.new_owner_assistant,
);
// Done.
diff --git a/solana/programs/token-router/src/processor/redeem_fill/fast.rs b/solana/programs/token-router/src/processor/redeem_fill/fast.rs
index 3a3401538..21f9e982b 100644
--- a/solana/programs/token-router/src/processor/redeem_fill/fast.rs
+++ b/solana/programs/token-router/src/processor/redeem_fill/fast.rs
@@ -75,9 +75,10 @@ pub struct RedeemFastFill<'info> {
/// CHECK: Seeds must be \["endpoint", SOLANA_CHAIN.to_be_bytes()\] (Matching Engine program).
matching_engine_router_endpoint: UncheckedAccount<'info>,
- /// CHECK: Mutable. Seeds must be \["custody"] (Matching Engine program).
+ /// CHECK: Mutable. Seeds must be \["local-custody", source_chain.to_be_bytes()\]
+ /// (Matching Engine program).
#[account(mut)]
- matching_engine_cctp_mint_recipient: UncheckedAccount<'info>,
+ matching_engine_local_custody_token: UncheckedAccount<'info>,
matching_engine_program: Program<'info, matching_engine::program::MatchingEngine>,
token_program: Program<'info, token::Token>,
@@ -100,8 +101,12 @@ fn handle_redeem_fast_fill(ctx: Context) -> Result<()> {
ctx.accounts.matching_engine_program.to_account_info(),
matching_engine::cpi::accounts::CompleteFastFill {
payer: ctx.accounts.payer.to_account_info(),
- custodian: ctx.accounts.matching_engine_custodian.to_account_info(),
- vaa: ctx.accounts.vaa.to_account_info(),
+ custodian: matching_engine::cpi::accounts::CheckedCustodian {
+ custodian: ctx.accounts.matching_engine_custodian.to_account_info(),
+ },
+ fast_fill_vaa: matching_engine::cpi::accounts::LiquidityLayerVaa {
+ vaa: ctx.accounts.vaa.to_account_info(),
+ },
redeemed_fast_fill: ctx
.accounts
.matching_engine_redeemed_fast_fill
@@ -109,14 +114,14 @@ fn handle_redeem_fast_fill(ctx: Context) -> Result<()> {
token_router_emitter: ctx.accounts.custodian.to_account_info(),
token_router_custody_token: ctx.accounts.prepared_custody_token.to_account_info(),
router_endpoint: LiveRouterEndpoint {
- inner: ctx
+ endpoint: ctx
.accounts
.matching_engine_router_endpoint
.to_account_info(),
},
- cctp_mint_recipient: ctx
+ local_custody_token: ctx
.accounts
- .matching_engine_cctp_mint_recipient
+ .matching_engine_local_custody_token
.to_account_info(),
token_program: ctx.accounts.token_program.to_account_info(),
system_program: ctx.accounts.system_program.to_account_info(),
diff --git a/solana/programs/upgrade-manager/Cargo.toml b/solana/programs/upgrade-manager/Cargo.toml
index 46242fcb0..53460303d 100644
--- a/solana/programs/upgrade-manager/Cargo.toml
+++ b/solana/programs/upgrade-manager/Cargo.toml
@@ -35,4 +35,7 @@ hex.workspace = true
cfg-if.workspace = true
[dev-dependencies]
-hex-literal.workspace = true
\ No newline at end of file
+hex-literal.workspace = true
+
+[lints]
+workspace = true
\ No newline at end of file
diff --git a/solana/programs/upgrade-manager/src/processor/token_router_upgrade/execute.rs b/solana/programs/upgrade-manager/src/processor/token_router_upgrade/execute.rs
index 32ecd2c5b..0c29e2395 100644
--- a/solana/programs/upgrade-manager/src/processor/token_router_upgrade/execute.rs
+++ b/solana/programs/upgrade-manager/src/processor/token_router_upgrade/execute.rs
@@ -79,7 +79,7 @@ pub fn execute_token_router_upgrade(ctx: Context) ->
bump: ctx.bumps.upgrade_receipt,
owner: *ctx.accounts.owner.key,
buffer: *ctx.accounts.token_router_buffer.key,
- slot: Clock::get().map(|clock| clock.slot)?,
+ slot: Clock::get().unwrap().slot,
});
// First set the buffer's authority to the upgrade authority.
diff --git a/solana/target/idl/matching_engine.json b/solana/target/idl/matching_engine.json
index 02275e37f..908028795 100644
--- a/solana/target/idl/matching_engine.json
+++ b/solana/target/idl/matching_engine.json
@@ -12,15 +12,22 @@
},
{
"name": "custodian",
- "isMut": false,
- "isSigner": false
+ "accounts": [
+ {
+ "name": "custodian",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
},
{
- "name": "vaa",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "zero-copy using the [VaaAccount](core_bridge_program::sdk::VaaAccount) reader."
+ "name": "fastFillVaa",
+ "accounts": [
+ {
+ "name": "vaa",
+ "isMut": false,
+ "isSigner": false
+ }
]
},
{
@@ -42,23 +49,16 @@
"name": "routerEndpoint",
"accounts": [
{
- "name": "inner",
+ "name": "endpoint",
"isMut": false,
"isSigner": false
}
]
},
{
- "name": "cctpMintRecipient",
+ "name": "localCustodyToken",
"isMut": true,
- "isSigner": false,
- "docs": [
- "Mint recipient token account, which is encoded as the mint recipient in the CCTP message.",
- "The CCTP Token Messenger Minter program will transfer the amount encoded in the CCTP message",
- "from its custody account to this account.",
- "",
- "Mutable. Seeds must be \\[\"custody\"\\]."
- ]
+ "isSigner": false
},
{
"name": "tokenProgram",
@@ -83,27 +83,32 @@
},
{
"name": "custodian",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "This program's Wormhole (Core Bridge) emitter authority.",
- ""
+ "accounts": [
+ {
+ "name": "custodian",
+ "isMut": false,
+ "isSigner": false
+ }
]
},
{
"name": "fastVaa",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "zero-copy using the [VaaAccount](core_bridge_program::sdk::VaaAccount) reader."
+ "accounts": [
+ {
+ "name": "vaa",
+ "isMut": false,
+ "isSigner": false
+ }
]
},
{
"name": "finalizedVaa",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "[verify_vaa_and_mint](wormhole_cctp_solana::cpi::verify_vaa_and_mint)."
+ "accounts": [
+ {
+ "name": "vaa",
+ "isMut": false,
+ "isSigner": false
+ }
]
},
{
@@ -112,97 +117,120 @@
"isSigner": false
},
{
- "name": "cctpMintRecipient",
+ "name": "preparedCustodyToken",
"isMut": true,
- "isSigner": false,
- "docs": [
- "Mint recipient token account, which is encoded as the mint recipient in the CCTP message.",
- "The CCTP Token Messenger Minter program will transfer the amount encoded in the CCTP message",
- "from its custody account to this account.",
- "",
- "",
- "NOTE: This account must be encoded as the mint recipient in the CCTP message."
- ]
- },
- {
- "name": "messageTransmitterAuthority",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "messageTransmitterConfig",
- "isMut": false,
"isSigner": false
},
{
- "name": "usedNonces",
+ "name": "auction",
"isMut": true,
"isSigner": false,
- "docs": [
- "first_nonce.to_string()\\] (CCTP Message Transmitter program)."
- ]
- },
- {
- "name": "messageTransmitterEventAuthority",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "tokenMessenger",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "remoteTokenMessenger",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "Messenger Minter program)."
- ]
- },
- {
- "name": "tokenMinter",
- "isMut": false,
- "isSigner": false
+ "isOptional": true
},
{
- "name": "localToken",
- "isMut": true,
- "isSigner": false,
- "docs": [
- "Token Messenger Minter's Local Token account. This program uses the mint of this account to",
- "validate the `mint_recipient` token account's mint.",
- ""
+ "name": "usdc",
+ "accounts": [
+ {
+ "name": "mint",
+ "isMut": false,
+ "isSigner": false
+ }
]
},
{
- "name": "tokenPair",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "Token Messenger Minter program)."
+ "name": "cctp",
+ "accounts": [
+ {
+ "name": "mintRecipient",
+ "accounts": [
+ {
+ "name": "mintRecipient",
+ "isMut": true,
+ "isSigner": false
+ }
+ ]
+ },
+ {
+ "name": "messageTransmitterAuthority",
+ "isMut": false,
+ "isSigner": false
+ },
+ {
+ "name": "messageTransmitterConfig",
+ "isMut": false,
+ "isSigner": false
+ },
+ {
+ "name": "usedNonces",
+ "isMut": true,
+ "isSigner": false,
+ "docs": [
+ "first_nonce.to_string()\\] (CCTP Message Transmitter program)."
+ ]
+ },
+ {
+ "name": "messageTransmitterEventAuthority",
+ "isMut": false,
+ "isSigner": false
+ },
+ {
+ "name": "tokenMessenger",
+ "isMut": false,
+ "isSigner": false
+ },
+ {
+ "name": "remoteTokenMessenger",
+ "isMut": false,
+ "isSigner": false,
+ "docs": [
+ "Messenger Minter program)."
+ ]
+ },
+ {
+ "name": "tokenMinter",
+ "isMut": false,
+ "isSigner": false
+ },
+ {
+ "name": "localToken",
+ "isMut": true,
+ "isSigner": false,
+ "docs": [
+ "Token Messenger Minter's Local Token account. This program uses the mint of this account to",
+ "validate the `mint_recipient` token account's mint.",
+ ""
+ ]
+ },
+ {
+ "name": "tokenPair",
+ "isMut": false,
+ "isSigner": false,
+ "docs": [
+ "Token Messenger Minter program)."
+ ]
+ },
+ {
+ "name": "tokenMessengerMinterCustodyToken",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "tokenMessengerMinterEventAuthority",
+ "isMut": false,
+ "isSigner": false
+ },
+ {
+ "name": "tokenMessengerMinterProgram",
+ "isMut": false,
+ "isSigner": false
+ },
+ {
+ "name": "messageTransmitterProgram",
+ "isMut": false,
+ "isSigner": false
+ }
]
},
- {
- "name": "tokenMessengerMinterCustodyToken",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "tokenMessengerMinterEventAuthority",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "tokenMessengerMinterProgram",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "messageTransmitterProgram",
- "isMut": false,
- "isSigner": false
- },
{
"name": "tokenProgram",
"isMut": false,
@@ -227,29 +255,20 @@
"name": "settleAuctionComplete",
"accounts": [
{
- "name": "custodian",
- "isMut": false,
+ "name": "executor",
+ "isMut": true,
"isSigner": false,
"docs": [
- "This program's Wormhole (Core Bridge) emitter authority.",
- ""
+ "we will always reward the owner of the executor token account with the lamports from the",
+ "prepared order response and its custody token account when we close these accounts. This",
+ "means we disregard the `prepared_by` field in the prepared order response."
]
},
{
- "name": "preparedBy",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "preparedOrderResponse",
+ "name": "executorToken",
"isMut": true,
"isSigner": false
},
- {
- "name": "auction",
- "isMut": false,
- "isSigner": false
- },
{
"name": "bestOfferToken",
"isMut": true,
@@ -262,18 +281,19 @@
]
},
{
- "name": "cctpMintRecipient",
+ "name": "preparedOrderResponse",
"isMut": true,
- "isSigner": false,
- "docs": [
- "Mint recipient token account, which is encoded as the mint recipient in the CCTP message.",
- "The CCTP Token Messenger Minter program will transfer the amount encoded in the CCTP message",
- "from its custody account to this account.",
- "",
- "Mutable. Seeds must be \\[\"custody\"\\].",
- "",
- "NOTE: This account must be encoded as the mint recipient in the CCTP message."
- ]
+ "isSigner": false
+ },
+ {
+ "name": "preparedCustodyToken",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "auction",
+ "isMut": true,
+ "isSigner": false
},
{
"name": "tokenProgram",
@@ -297,46 +317,23 @@
"isSigner": false
},
{
- "name": "custodian",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "This program's Wormhole (Core Bridge) emitter authority.",
- ""
- ]
- },
- {
- "name": "fastVaa",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "zero-copy using the [VaaAccount](core_bridge_program::sdk::VaaAccount) reader."
- ]
- },
- {
- "name": "preparedOrderResponse",
+ "name": "coreMessage",
"isMut": true,
"isSigner": false
},
{
- "name": "auction",
+ "name": "cctpMessage",
"isMut": true,
- "isSigner": false,
- "docs": [
- "There should be no account data here because an auction was never created."
- ]
+ "isSigner": false
},
{
- "name": "cctpMintRecipient",
- "isMut": true,
- "isSigner": false,
- "docs": [
- "Mint recipient token account, which is encoded as the mint recipient in the CCTP message.",
- "The CCTP Token Messenger Minter program will transfer the amount encoded in the CCTP message",
- "from its custody account to this account.",
- "",
- "",
- "NOTE: This account must be encoded as the mint recipient in the CCTP message."
+ "name": "custodian",
+ "accounts": [
+ {
+ "name": "custodian",
+ "isMut": false,
+ "isSigner": false
+ }
]
},
{
@@ -351,310 +348,60 @@
]
},
{
- "name": "fromRouterEndpoint",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "Seeds must be \\[\"endpoint\", chain.to_be_bytes()\\]."
- ]
- },
- {
- "name": "toRouterEndpoint",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "Seeds must be \\[\"endpoint\", chain.to_be_bytes()\\]."
- ]
- },
- {
- "name": "mint",
- "isMut": true,
- "isSigner": false,
- "docs": [
- "Circle-supported mint.",
- "",
- "Token Messenger Minter program's local token account."
- ]
- },
- {
- "name": "coreBridgeConfig",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "coreMessage",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "cctpMessage",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "coreEmitterSequence",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "coreFeeCollector",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "tokenMessengerMinterSenderAuthority",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "messageTransmitterConfig",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "tokenMessenger",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "remoteTokenMessenger",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "Messenger Minter program)."
- ]
- },
- {
- "name": "tokenMinter",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "CHECK Seeds must be \\[\"token_minter\"\\] (CCTP Token Messenger Minter program)."
- ]
- },
- {
- "name": "localToken",
- "isMut": true,
- "isSigner": false,
- "docs": [
- "Local token account, which this program uses to validate the `mint` used to burn.",
- ""
- ]
- },
- {
- "name": "tokenMessengerMinterEventAuthority",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "coreBridgeProgram",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "tokenMessengerMinterProgram",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "messageTransmitterProgram",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "tokenProgram",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "systemProgram",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "clock",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "rent",
- "isMut": false,
- "isSigner": false
- }
- ],
- "args": []
- },
- {
- "name": "settleAuctionNoneLocal",
- "accounts": [
- {
- "name": "payer",
- "isMut": true,
- "isSigner": true
- },
- {
- "name": "payerSequence",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "custodian",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "This program's Wormhole (Core Bridge) emitter authority.",
- ""
- ]
- },
- {
- "name": "fastVaa",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "zero-copy using the [VaaAccount](core_bridge_program::sdk::VaaAccount) reader."
- ]
- },
- {
- "name": "preparedOrderResponse",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "auction",
- "isMut": true,
- "isSigner": false,
- "docs": [
- "There should be no account data here because an auction was never created."
- ]
- },
- {
- "name": "cctpMintRecipient",
- "isMut": true,
- "isSigner": false,
- "docs": [
- "Mint recipient token account, which is encoded as the mint recipient in the CCTP message.",
- "The CCTP Token Messenger Minter program will transfer the amount encoded in the CCTP message",
- "from its custody account to this account.",
- "",
- "",
- "NOTE: This account must be encoded as the mint recipient in the CCTP message."
- ]
- },
- {
- "name": "feeRecipientToken",
- "isMut": true,
- "isSigner": false,
- "docs": [
- "Destination token account, which the redeemer may not own. But because the redeemer is a",
- "signer and is the one encoded in the Deposit Fill message, he may have the tokens be sent",
- "to any account he chooses (this one).",
- ""
- ]
- },
- {
- "name": "fromRouterEndpoint",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "Seeds must be \\[\"endpoint\", chain.to_be_bytes()\\]."
- ]
- },
- {
- "name": "toRouterEndpoint",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "Seeds must be \\[\"endpoint\", chain.to_be_bytes()\\]."
- ]
- },
- {
- "name": "coreBridgeConfig",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "coreMessage",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "coreEmitterSequence",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "coreFeeCollector",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "coreBridgeProgram",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "tokenProgram",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "systemProgram",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "clock",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "rent",
- "isMut": false,
- "isSigner": false
- }
- ],
- "args": []
- },
- {
- "name": "settleAuctionActiveCctp",
- "accounts": [
- {
- "name": "payer",
- "isMut": true,
- "isSigner": true
- },
- {
- "name": "payerSequence",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "custodian",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "This program's Wormhole (Core Bridge) emitter authority.",
- ""
+ "name": "prepared",
+ "accounts": [
+ {
+ "name": "by",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "orderResponse",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "custodyToken",
+ "isMut": true,
+ "isSigner": false
+ }
]
},
{
- "name": "auctionConfig",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "fastVaa",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "zero-copy using the [VaaAccount](core_bridge_program::sdk::VaaAccount) reader."
+ "name": "fastOrderPath",
+ "accounts": [
+ {
+ "name": "fastVaa",
+ "accounts": [
+ {
+ "name": "vaa",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
+ },
+ {
+ "name": "from",
+ "accounts": [
+ {
+ "name": "endpoint",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
+ },
+ {
+ "name": "to",
+ "accounts": [
+ {
+ "name": "endpoint",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
+ }
]
},
- {
- "name": "preparedOrderResponse",
- "isMut": true,
- "isSigner": false
- },
{
"name": "auction",
"isMut": true,
@@ -664,131 +411,110 @@
]
},
{
- "name": "bestOfferToken",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "executorToken",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "cctpMintRecipient",
- "isMut": true,
- "isSigner": false,
- "docs": [
- "Mint recipient token account, which is encoded as the mint recipient in the CCTP message.",
- "The CCTP Token Messenger Minter program will transfer the amount encoded in the CCTP message",
- "from its custody account to this account.",
- "",
- "",
- "NOTE: This account must be encoded as the mint recipient in the CCTP message."
- ]
- },
- {
- "name": "toRouterEndpoint",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "Seeds must be \\[\"endpoint\", chain.to_be_bytes()\\]."
- ]
- },
- {
- "name": "mint",
- "isMut": true,
- "isSigner": false,
- "docs": [
- "Circle-supported mint.",
- "",
- "Token Messenger Minter program's local token account."
- ]
- },
- {
- "name": "coreBridgeConfig",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "coreMessage",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "cctpMessage",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "coreEmitterSequence",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "coreFeeCollector",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "tokenMessengerMinterSenderAuthority",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "messageTransmitterConfig",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "tokenMessenger",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "remoteTokenMessenger",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "Messenger Minter program)."
- ]
- },
- {
- "name": "tokenMinter",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "CHECK Seeds must be \\[\"token_minter\"\\] (CCTP Token Messenger Minter program)."
+ "name": "wormhole",
+ "accounts": [
+ {
+ "name": "config",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "emitterSequence",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "feeCollector",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "coreBridgeProgram",
+ "isMut": false,
+ "isSigner": false
+ }
]
},
{
- "name": "localToken",
- "isMut": true,
- "isSigner": false,
- "docs": [
- "Local token account, which this program uses to validate the `mint` used to burn.",
- ""
+ "name": "cctp",
+ "accounts": [
+ {
+ "name": "burnSource",
+ "accounts": [
+ {
+ "name": "mintRecipient",
+ "isMut": true,
+ "isSigner": false
+ }
+ ]
+ },
+ {
+ "name": "mint",
+ "isMut": true,
+ "isSigner": false,
+ "docs": [
+ "Circle-supported mint.",
+ "",
+ "Token Messenger Minter program's local token account."
+ ]
+ },
+ {
+ "name": "tokenMessengerMinterSenderAuthority",
+ "isMut": false,
+ "isSigner": false
+ },
+ {
+ "name": "messageTransmitterConfig",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "tokenMessenger",
+ "isMut": false,
+ "isSigner": false
+ },
+ {
+ "name": "remoteTokenMessenger",
+ "isMut": false,
+ "isSigner": false,
+ "docs": [
+ "Messenger Minter program)."
+ ]
+ },
+ {
+ "name": "tokenMinter",
+ "isMut": false,
+ "isSigner": false,
+ "docs": [
+ "CHECK Seeds must be \\[\"token_minter\"\\] (CCTP Token Messenger Minter program)."
+ ]
+ },
+ {
+ "name": "localToken",
+ "isMut": true,
+ "isSigner": false,
+ "docs": [
+ "Local token account, which this program uses to validate the `mint` used to burn.",
+ ""
+ ]
+ },
+ {
+ "name": "tokenMessengerMinterEventAuthority",
+ "isMut": false,
+ "isSigner": false
+ },
+ {
+ "name": "tokenMessengerMinterProgram",
+ "isMut": false,
+ "isSigner": false
+ },
+ {
+ "name": "messageTransmitterProgram",
+ "isMut": false,
+ "isSigner": false
+ }
]
},
- {
- "name": "tokenMessengerMinterEventAuthority",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "coreBridgeProgram",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "tokenMessengerMinterProgram",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "messageTransmitterProgram",
- "isMut": false,
- "isSigner": false
- },
{
"name": "tokenProgram",
"isMut": false,
@@ -800,20 +526,33 @@
"isSigner": false
},
{
- "name": "clock",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "rent",
- "isMut": false,
- "isSigner": false
+ "name": "sysvars",
+ "accounts": [
+ {
+ "name": "clock",
+ "isMut": false,
+ "isSigner": false,
+ "docs": [
+ "Wormhole Core Bridge needs the clock sysvar based on its legacy implementation.",
+ ""
+ ]
+ },
+ {
+ "name": "rent",
+ "isMut": false,
+ "isSigner": false,
+ "docs": [
+ "Wormhole Core Bridge needs the rent sysvar based on its legacy implementation.",
+ ""
+ ]
+ }
+ ]
}
],
"args": []
},
{
- "name": "settleAuctionActiveLocal",
+ "name": "settleAuctionNoneLocal",
"accounts": [
{
"name": "payer",
@@ -826,96 +565,124 @@
"isSigner": false
},
{
- "name": "custodian",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "This program's Wormhole (Core Bridge) emitter authority.",
- ""
- ]
- },
- {
- "name": "auctionConfig",
- "isMut": false,
+ "name": "coreMessage",
+ "isMut": true,
"isSigner": false
},
{
- "name": "fastVaa",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "zero-copy using the [VaaAccount](core_bridge_program::sdk::VaaAccount) reader."
+ "name": "custodian",
+ "accounts": [
+ {
+ "name": "custodian",
+ "isMut": false,
+ "isSigner": false
+ }
]
},
{
- "name": "preparedOrderResponse",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "auction",
+ "name": "feeRecipientToken",
"isMut": true,
"isSigner": false,
"docs": [
- "There should be no account data here because an auction was never created."
+ "Destination token account, which the redeemer may not own. But because the redeemer is a",
+ "signer and is the one encoded in the Deposit Fill message, he may have the tokens be sent",
+ "to any account he chooses (this one).",
+ ""
]
},
{
- "name": "toRouterEndpoint",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "Seeds must be \\[\"endpoint\", chain.to_be_bytes()\\]."
+ "name": "prepared",
+ "accounts": [
+ {
+ "name": "by",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "orderResponse",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "custodyToken",
+ "isMut": true,
+ "isSigner": false
+ }
]
},
{
- "name": "bestOfferToken",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "executorToken",
- "isMut": true,
- "isSigner": false
+ "name": "fastOrderPath",
+ "accounts": [
+ {
+ "name": "fastVaa",
+ "accounts": [
+ {
+ "name": "vaa",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
+ },
+ {
+ "name": "from",
+ "accounts": [
+ {
+ "name": "endpoint",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
+ },
+ {
+ "name": "to",
+ "accounts": [
+ {
+ "name": "endpoint",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
+ }
+ ]
},
{
- "name": "cctpMintRecipient",
+ "name": "auction",
"isMut": true,
"isSigner": false,
"docs": [
- "Mint recipient token account, which is encoded as the mint recipient in the CCTP message.",
- "The CCTP Token Messenger Minter program will transfer the amount encoded in the CCTP message",
- "from its custody account to this account.",
- "",
- "",
- "NOTE: This account must be encoded as the mint recipient in the CCTP message."
+ "There should be no account data here because an auction was never created."
]
},
{
- "name": "coreBridgeConfig",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "coreMessage",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "coreEmitterSequence",
- "isMut": true,
- "isSigner": false
+ "name": "wormhole",
+ "accounts": [
+ {
+ "name": "config",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "emitterSequence",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "feeCollector",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "coreBridgeProgram",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
},
{
- "name": "coreFeeCollector",
+ "name": "localCustodyToken",
"isMut": true,
"isSigner": false
},
- {
- "name": "coreBridgeProgram",
- "isMut": false,
- "isSigner": false
- },
{
"name": "tokenProgram",
"isMut": false,
@@ -927,14 +694,27 @@
"isSigner": false
},
{
- "name": "clock",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "rent",
- "isMut": false,
- "isSigner": false
+ "name": "sysvars",
+ "accounts": [
+ {
+ "name": "clock",
+ "isMut": false,
+ "isSigner": false,
+ "docs": [
+ "Wormhole Core Bridge needs the clock sysvar based on its legacy implementation.",
+ ""
+ ]
+ },
+ {
+ "name": "rent",
+ "isMut": false,
+ "isSigner": false,
+ "docs": [
+ "Wormhole Core Bridge needs the rent sysvar based on its legacy implementation.",
+ ""
+ ]
+ }
+ ]
}
],
"args": []
@@ -993,9 +773,14 @@
"isSigner": false
},
{
- "name": "mint",
- "isMut": false,
- "isSigner": false
+ "name": "usdc",
+ "accounts": [
+ {
+ "name": "mint",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
},
{
"name": "programData",
@@ -1064,8 +849,13 @@
},
{
"name": "custodian",
- "isMut": false,
- "isSigner": false
+ "accounts": [
+ {
+ "name": "custodian",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
}
]
},
@@ -1074,6 +864,33 @@
"isMut": true,
"isSigner": false
},
+ {
+ "name": "localRouterEndpoint",
+ "isMut": false,
+ "isSigner": false,
+ "docs": [
+ "Local router endpoint PDA.",
+ "",
+ "NOTE: This account may not exist yet. But we need to pass it since it will be the owner of",
+ "the local custody token account.",
+ ""
+ ]
+ },
+ {
+ "name": "localCustodyToken",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "usdc",
+ "accounts": [
+ {
+ "name": "mint",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
+ },
{
"name": "remoteTokenMessenger",
"isMut": false,
@@ -1082,6 +899,11 @@
"Messenger Minter program)."
]
},
+ {
+ "name": "tokenProgram",
+ "isMut": false,
+ "isSigner": false
+ },
{
"name": "systemProgram",
"isMut": false,
@@ -1115,8 +937,13 @@
},
{
"name": "custodian",
- "isMut": false,
- "isSigner": false
+ "accounts": [
+ {
+ "name": "custodian",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
}
]
},
@@ -1169,8 +996,13 @@
},
{
"name": "custodian",
- "isMut": false,
- "isSigner": false
+ "accounts": [
+ {
+ "name": "custodian",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
}
]
},
@@ -1178,7 +1010,7 @@
"name": "routerEndpoint",
"accounts": [
{
- "name": "inner",
+ "name": "endpoint",
"isMut": true,
"isSigner": false
}
@@ -1200,8 +1032,13 @@
},
{
"name": "custodian",
- "isMut": false,
- "isSigner": false
+ "accounts": [
+ {
+ "name": "custodian",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
}
]
},
@@ -1209,7 +1046,7 @@
"name": "routerEndpoint",
"accounts": [
{
- "name": "inner",
+ "name": "endpoint",
"isMut": true,
"isSigner": false
}
@@ -1246,8 +1083,13 @@
},
{
"name": "custodian",
- "isMut": false,
- "isSigner": false
+ "accounts": [
+ {
+ "name": "custodian",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
}
]
},
@@ -1255,7 +1097,7 @@
"name": "routerEndpoint",
"accounts": [
{
- "name": "inner",
+ "name": "endpoint",
"isMut": true,
"isSigner": false
}
@@ -1300,8 +1142,13 @@
},
{
"name": "custodian",
- "isMut": true,
- "isSigner": false
+ "accounts": [
+ {
+ "name": "custodian",
+ "isMut": true,
+ "isSigner": false
+ }
+ ]
}
]
},
@@ -1350,8 +1197,13 @@
},
{
"name": "custodian",
- "isMut": true,
- "isSigner": false
+ "accounts": [
+ {
+ "name": "custodian",
+ "isMut": true,
+ "isSigner": false
+ }
+ ]
}
]
}
@@ -1376,8 +1228,13 @@
},
{
"name": "custodian",
- "isMut": true,
- "isSigner": false
+ "accounts": [
+ {
+ "name": "custodian",
+ "isMut": true,
+ "isSigner": false
+ }
+ ]
}
]
},
@@ -1424,8 +1281,13 @@
},
{
"name": "custodian",
- "isMut": true,
- "isSigner": false
+ "accounts": [
+ {
+ "name": "custodian",
+ "isMut": true,
+ "isSigner": false
+ }
+ ]
}
]
},
@@ -1451,18 +1313,25 @@
"name": "updateOwnerAssistant",
"accounts": [
{
- "name": "owner",
- "isMut": false,
- "isSigner": true,
- "docs": [
- "Owner of the program set in the [`OwnerConfig`] account."
+ "name": "admin",
+ "accounts": [
+ {
+ "name": "owner",
+ "isMut": false,
+ "isSigner": true
+ },
+ {
+ "name": "custodian",
+ "accounts": [
+ {
+ "name": "custodian",
+ "isMut": true,
+ "isSigner": false
+ }
+ ]
+ }
]
},
- {
- "name": "custodian",
- "isMut": true,
- "isSigner": false
- },
{
"name": "newOwnerAssistant",
"isMut": false,
@@ -1479,14 +1348,24 @@
"name": "updateFeeRecipient",
"accounts": [
{
- "name": "ownerOrAssistant",
- "isMut": true,
- "isSigner": true
- },
- {
- "name": "custodian",
- "isMut": true,
- "isSigner": false
+ "name": "admin",
+ "accounts": [
+ {
+ "name": "ownerOrAssistant",
+ "isMut": false,
+ "isSigner": true
+ },
+ {
+ "name": "custodian",
+ "accounts": [
+ {
+ "name": "custodian",
+ "isMut": true,
+ "isSigner": false
+ }
+ ]
+ }
+ ]
},
{
"name": "newFeeRecipientToken",
@@ -1515,11 +1394,12 @@
},
{
"name": "custodian",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "This program's Wormhole (Core Bridge) emitter authority.",
- ""
+ "accounts": [
+ {
+ "name": "custodian",
+ "isMut": false,
+ "isSigner": false
+ }
]
},
{
@@ -1528,9 +1408,39 @@
"isSigner": false
},
{
- "name": "fastVaa",
- "isMut": false,
- "isSigner": false
+ "name": "fastOrderPath",
+ "accounts": [
+ {
+ "name": "fastVaa",
+ "accounts": [
+ {
+ "name": "vaa",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
+ },
+ {
+ "name": "from",
+ "accounts": [
+ {
+ "name": "endpoint",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
+ },
+ {
+ "name": "to",
+ "accounts": [
+ {
+ "name": "endpoint",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
+ }
+ ]
},
{
"name": "auction",
@@ -1542,24 +1452,27 @@
]
},
{
- "name": "fromRouterEndpoint",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "toRouterEndpoint",
+ "name": "offerToken",
"isMut": false,
- "isSigner": false
+ "isSigner": false,
+ "docs": [
+ "the auction PDA."
+ ]
},
{
- "name": "offerToken",
+ "name": "auctionCustodyToken",
"isMut": true,
"isSigner": false
},
{
- "name": "cctpMintRecipient",
- "isMut": true,
- "isSigner": false
+ "name": "usdc",
+ "accounts": [
+ {
+ "name": "mint",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
},
{
"name": "systemProgram",
@@ -1583,39 +1496,37 @@
"name": "improveOffer",
"accounts": [
{
- "name": "custodian",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "This program's Wormhole (Core Bridge) emitter authority. This is also the burn-source",
- "authority for CCTP transfers.",
- ""
+ "name": "activeAuction",
+ "accounts": [
+ {
+ "name": "auction",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "custodyToken",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "config",
+ "isMut": false,
+ "isSigner": false
+ },
+ {
+ "name": "bestOfferToken",
+ "isMut": true,
+ "isSigner": false
+ }
]
},
- {
- "name": "auctionConfig",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "offerAuthority",
- "isMut": false,
- "isSigner": true
- },
- {
- "name": "auction",
- "isMut": true,
- "isSigner": false
- },
{
"name": "offerToken",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "bestOfferToken",
- "isMut": true,
- "isSigner": false
+ "isMut": false,
+ "isSigner": false,
+ "docs": [
+ "the auction PDA."
+ ]
},
{
"name": "tokenProgram",
@@ -1643,75 +1554,6 @@
"isMut": true,
"isSigner": false
},
- {
- "name": "custodian",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "This program's Wormhole (Core Bridge) emitter authority. This is also the burn-source",
- "authority for CCTP transfers.",
- ""
- ]
- },
- {
- "name": "auctionConfig",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "fastVaa",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "auction",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "toRouterEndpoint",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "executorToken",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "bestOfferToken",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "initialOfferToken",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "cctpMintRecipient",
- "isMut": true,
- "isSigner": false,
- "docs": [
- "Also the burn_source token account.",
- ""
- ]
- },
- {
- "name": "mint",
- "isMut": true,
- "isSigner": false,
- "docs": [
- "Circle-supported mint.",
- "",
- "Token Messenger Minter program's local token account."
- ]
- },
- {
- "name": "coreBridgeConfig",
- "isMut": true,
- "isSigner": false
- },
{
"name": "coreMessage",
"isMut": true,
@@ -1723,75 +1565,180 @@
"isSigner": false
},
{
- "name": "coreEmitterSequence",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "coreFeeCollector",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "tokenMessengerMinterSenderAuthority",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "messageTransmitterConfig",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "tokenMessenger",
- "isMut": false,
- "isSigner": false
+ "name": "custodian",
+ "accounts": [
+ {
+ "name": "custodian",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
},
{
- "name": "remoteTokenMessenger",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "Messenger Minter program)."
+ "name": "executeOrder",
+ "accounts": [
+ {
+ "name": "fastVaa",
+ "accounts": [
+ {
+ "name": "vaa",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
+ },
+ {
+ "name": "activeAuction",
+ "accounts": [
+ {
+ "name": "auction",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "custodyToken",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "config",
+ "isMut": false,
+ "isSigner": false
+ },
+ {
+ "name": "bestOfferToken",
+ "isMut": true,
+ "isSigner": false
+ }
+ ]
+ },
+ {
+ "name": "toRouterEndpoint",
+ "accounts": [
+ {
+ "name": "endpoint",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
+ },
+ {
+ "name": "executorToken",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "initialOfferToken",
+ "isMut": true,
+ "isSigner": false
+ }
]
},
{
- "name": "tokenMinter",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "CHECK Seeds must be \\[\"token_minter\"\\] (CCTP Token Messenger Minter program)."
+ "name": "wormhole",
+ "accounts": [
+ {
+ "name": "config",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "emitterSequence",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "feeCollector",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "coreBridgeProgram",
+ "isMut": false,
+ "isSigner": false
+ }
]
},
{
- "name": "localToken",
- "isMut": true,
- "isSigner": false,
- "docs": [
- "Local token account, which this program uses to validate the `mint` used to burn.",
- ""
+ "name": "cctp",
+ "accounts": [
+ {
+ "name": "burnSource",
+ "accounts": [
+ {
+ "name": "mintRecipient",
+ "isMut": true,
+ "isSigner": false
+ }
+ ]
+ },
+ {
+ "name": "mint",
+ "isMut": true,
+ "isSigner": false,
+ "docs": [
+ "Circle-supported mint.",
+ "",
+ "Token Messenger Minter program's local token account."
+ ]
+ },
+ {
+ "name": "tokenMessengerMinterSenderAuthority",
+ "isMut": false,
+ "isSigner": false
+ },
+ {
+ "name": "messageTransmitterConfig",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "tokenMessenger",
+ "isMut": false,
+ "isSigner": false
+ },
+ {
+ "name": "remoteTokenMessenger",
+ "isMut": false,
+ "isSigner": false,
+ "docs": [
+ "Messenger Minter program)."
+ ]
+ },
+ {
+ "name": "tokenMinter",
+ "isMut": false,
+ "isSigner": false,
+ "docs": [
+ "CHECK Seeds must be \\[\"token_minter\"\\] (CCTP Token Messenger Minter program)."
+ ]
+ },
+ {
+ "name": "localToken",
+ "isMut": true,
+ "isSigner": false,
+ "docs": [
+ "Local token account, which this program uses to validate the `mint` used to burn.",
+ ""
+ ]
+ },
+ {
+ "name": "tokenMessengerMinterEventAuthority",
+ "isMut": false,
+ "isSigner": false
+ },
+ {
+ "name": "tokenMessengerMinterProgram",
+ "isMut": false,
+ "isSigner": false
+ },
+ {
+ "name": "messageTransmitterProgram",
+ "isMut": false,
+ "isSigner": false
+ }
]
},
- {
- "name": "tokenMessengerMinterEventAuthority",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "coreBridgeProgram",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "tokenMessengerMinterProgram",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "messageTransmitterProgram",
- "isMut": false,
- "isSigner": false
- },
{
"name": "systemProgram",
"isMut": false,
@@ -1803,14 +1750,27 @@
"isSigner": false
},
{
- "name": "clock",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "rent",
- "isMut": false,
- "isSigner": false
+ "name": "sysvars",
+ "accounts": [
+ {
+ "name": "clock",
+ "isMut": false,
+ "isSigner": false,
+ "docs": [
+ "Wormhole Core Bridge needs the clock sysvar based on its legacy implementation.",
+ ""
+ ]
+ },
+ {
+ "name": "rent",
+ "isMut": false,
+ "isSigner": false,
+ "docs": [
+ "Wormhole Core Bridge needs the rent sysvar based on its legacy implementation.",
+ ""
+ ]
+ }
+ ]
}
],
"args": []
@@ -1829,84 +1789,110 @@
"isSigner": false
},
{
- "name": "custodian",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "This program's Wormhole (Core Bridge) emitter authority. This is also the burn-source",
- "authority for CCTP transfers.",
- ""
- ]
- },
- {
- "name": "auctionConfig",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "fastVaa",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "auction",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "toRouterEndpoint",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "executorToken",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "bestOfferToken",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "initialOfferToken",
+ "name": "coreMessage",
"isMut": true,
"isSigner": false
},
{
- "name": "cctpMintRecipient",
- "isMut": true,
- "isSigner": false,
- "docs": [
- "Also the burn_source token account.",
- ""
+ "name": "custodian",
+ "accounts": [
+ {
+ "name": "custodian",
+ "isMut": false,
+ "isSigner": false
+ }
]
},
{
- "name": "coreBridgeConfig",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "coreMessage",
- "isMut": true,
- "isSigner": false
+ "name": "executeOrder",
+ "accounts": [
+ {
+ "name": "fastVaa",
+ "accounts": [
+ {
+ "name": "vaa",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
+ },
+ {
+ "name": "activeAuction",
+ "accounts": [
+ {
+ "name": "auction",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "custodyToken",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "config",
+ "isMut": false,
+ "isSigner": false
+ },
+ {
+ "name": "bestOfferToken",
+ "isMut": true,
+ "isSigner": false
+ }
+ ]
+ },
+ {
+ "name": "toRouterEndpoint",
+ "accounts": [
+ {
+ "name": "endpoint",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
+ },
+ {
+ "name": "executorToken",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "initialOfferToken",
+ "isMut": true,
+ "isSigner": false
+ }
+ ]
},
{
- "name": "coreEmitterSequence",
- "isMut": true,
- "isSigner": false
+ "name": "wormhole",
+ "accounts": [
+ {
+ "name": "config",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "emitterSequence",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "feeCollector",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "coreBridgeProgram",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
},
{
- "name": "coreFeeCollector",
+ "name": "localCustodyToken",
"isMut": true,
"isSigner": false
},
- {
- "name": "coreBridgeProgram",
- "isMut": false,
- "isSigner": false
- },
{
"name": "systemProgram",
"isMut": false,
@@ -1918,14 +1904,27 @@
"isSigner": false
},
{
- "name": "clock",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "rent",
- "isMut": false,
- "isSigner": false
+ "name": "sysvars",
+ "accounts": [
+ {
+ "name": "clock",
+ "isMut": false,
+ "isSigner": false,
+ "docs": [
+ "Wormhole Core Bridge needs the clock sysvar based on its legacy implementation.",
+ ""
+ ]
+ },
+ {
+ "name": "rent",
+ "isMut": false,
+ "isSigner": false,
+ "docs": [
+ "Wormhole Core Bridge needs the rent sysvar based on its legacy implementation.",
+ ""
+ ]
+ }
+ ]
}
],
"args": []
@@ -1943,8 +1942,13 @@
},
{
"name": "custodian",
- "isMut": false,
- "isSigner": false
+ "accounts": [
+ {
+ "name": "custodian",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
}
]
},
@@ -2294,6 +2298,10 @@
"name": "configId",
"type": "u32"
},
+ {
+ "name": "custodyTokenBump",
+ "type": "u8"
+ },
{
"name": "vaaSequence",
"docs": [
@@ -2358,6 +2366,10 @@
"the [amount_in](Self::amount_in)."
],
"type": "u64"
+ },
+ {
+ "name": "endEarly",
+ "type": "bool"
}
]
}
@@ -2415,6 +2427,12 @@
{
"name": "slot",
"type": "u64"
+ },
+ {
+ "name": "executePenalty",
+ "type": {
+ "option": "u64"
+ }
}
]
},
@@ -2426,7 +2444,7 @@
"type": "u64"
},
{
- "name": "penalty",
+ "name": "totalPenalty",
"type": {
"option": "u64"
}
@@ -2506,11 +2524,6 @@
"name": "OwnerOrAssistantOnly",
"msg": "OwnerOrAssistantOnly"
},
- {
- "code": 6006,
- "name": "InvalidCustodyToken",
- "msg": "InvalidCustodyToken"
- },
{
"code": 6008,
"name": "CpiDisallowed",
@@ -2546,11 +2559,6 @@
"name": "ImmutableProgram",
"msg": "ImmutableProgram"
},
- {
- "code": 6259,
- "name": "NotUsdc",
- "msg": "NotUsdc"
- },
{
"code": 6514,
"name": "InvalidNewOwner",
@@ -2733,8 +2741,8 @@
},
{
"code": 6556,
- "name": "BestOfferTokenMismatch",
- "msg": "BestOfferTokenMismatch"
+ "name": "ExecutorTokenMismatch",
+ "msg": "ExecutorTokenMismatch"
},
{
"code": 6557,
diff --git a/solana/target/idl/token_router.json b/solana/target/idl/token_router.json
index 1e35b5455..227335459 100644
--- a/solana/target/idl/token_router.json
+++ b/solana/target/idl/token_router.json
@@ -547,9 +547,12 @@
"isSigner": false
},
{
- "name": "matchingEngineCctpMintRecipient",
+ "name": "matchingEngineLocalCustodyToken",
"isMut": true,
- "isSigner": false
+ "isSigner": false,
+ "docs": [
+ "(Matching Engine program)."
+ ]
},
{
"name": "matchingEngineProgram",
diff --git a/solana/target/types/matching_engine.ts b/solana/target/types/matching_engine.ts
index 7f26e7c5a..21819270f 100644
--- a/solana/target/types/matching_engine.ts
+++ b/solana/target/types/matching_engine.ts
@@ -12,15 +12,22 @@ export type MatchingEngine = {
},
{
"name": "custodian",
- "isMut": false,
- "isSigner": false
+ "accounts": [
+ {
+ "name": "custodian",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
},
{
- "name": "vaa",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "zero-copy using the [VaaAccount](core_bridge_program::sdk::VaaAccount) reader."
+ "name": "fastFillVaa",
+ "accounts": [
+ {
+ "name": "vaa",
+ "isMut": false,
+ "isSigner": false
+ }
]
},
{
@@ -42,23 +49,16 @@ export type MatchingEngine = {
"name": "routerEndpoint",
"accounts": [
{
- "name": "inner",
+ "name": "endpoint",
"isMut": false,
"isSigner": false
}
]
},
{
- "name": "cctpMintRecipient",
+ "name": "localCustodyToken",
"isMut": true,
- "isSigner": false,
- "docs": [
- "Mint recipient token account, which is encoded as the mint recipient in the CCTP message.",
- "The CCTP Token Messenger Minter program will transfer the amount encoded in the CCTP message",
- "from its custody account to this account.",
- "",
- "Mutable. Seeds must be \\[\"custody\"\\]."
- ]
+ "isSigner": false
},
{
"name": "tokenProgram",
@@ -83,27 +83,32 @@ export type MatchingEngine = {
},
{
"name": "custodian",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "This program's Wormhole (Core Bridge) emitter authority.",
- ""
+ "accounts": [
+ {
+ "name": "custodian",
+ "isMut": false,
+ "isSigner": false
+ }
]
},
{
"name": "fastVaa",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "zero-copy using the [VaaAccount](core_bridge_program::sdk::VaaAccount) reader."
+ "accounts": [
+ {
+ "name": "vaa",
+ "isMut": false,
+ "isSigner": false
+ }
]
},
{
"name": "finalizedVaa",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "[verify_vaa_and_mint](wormhole_cctp_solana::cpi::verify_vaa_and_mint)."
+ "accounts": [
+ {
+ "name": "vaa",
+ "isMut": false,
+ "isSigner": false
+ }
]
},
{
@@ -112,97 +117,120 @@ export type MatchingEngine = {
"isSigner": false
},
{
- "name": "cctpMintRecipient",
+ "name": "preparedCustodyToken",
"isMut": true,
- "isSigner": false,
- "docs": [
- "Mint recipient token account, which is encoded as the mint recipient in the CCTP message.",
- "The CCTP Token Messenger Minter program will transfer the amount encoded in the CCTP message",
- "from its custody account to this account.",
- "",
- "",
- "NOTE: This account must be encoded as the mint recipient in the CCTP message."
- ]
- },
- {
- "name": "messageTransmitterAuthority",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "messageTransmitterConfig",
- "isMut": false,
"isSigner": false
},
{
- "name": "usedNonces",
+ "name": "auction",
"isMut": true,
"isSigner": false,
- "docs": [
- "first_nonce.to_string()\\] (CCTP Message Transmitter program)."
- ]
- },
- {
- "name": "messageTransmitterEventAuthority",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "tokenMessenger",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "remoteTokenMessenger",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "Messenger Minter program)."
- ]
- },
- {
- "name": "tokenMinter",
- "isMut": false,
- "isSigner": false
+ "isOptional": true
},
{
- "name": "localToken",
- "isMut": true,
- "isSigner": false,
- "docs": [
- "Token Messenger Minter's Local Token account. This program uses the mint of this account to",
- "validate the `mint_recipient` token account's mint.",
- ""
+ "name": "usdc",
+ "accounts": [
+ {
+ "name": "mint",
+ "isMut": false,
+ "isSigner": false
+ }
]
},
{
- "name": "tokenPair",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "Token Messenger Minter program)."
+ "name": "cctp",
+ "accounts": [
+ {
+ "name": "mintRecipient",
+ "accounts": [
+ {
+ "name": "mintRecipient",
+ "isMut": true,
+ "isSigner": false
+ }
+ ]
+ },
+ {
+ "name": "messageTransmitterAuthority",
+ "isMut": false,
+ "isSigner": false
+ },
+ {
+ "name": "messageTransmitterConfig",
+ "isMut": false,
+ "isSigner": false
+ },
+ {
+ "name": "usedNonces",
+ "isMut": true,
+ "isSigner": false,
+ "docs": [
+ "first_nonce.to_string()\\] (CCTP Message Transmitter program)."
+ ]
+ },
+ {
+ "name": "messageTransmitterEventAuthority",
+ "isMut": false,
+ "isSigner": false
+ },
+ {
+ "name": "tokenMessenger",
+ "isMut": false,
+ "isSigner": false
+ },
+ {
+ "name": "remoteTokenMessenger",
+ "isMut": false,
+ "isSigner": false,
+ "docs": [
+ "Messenger Minter program)."
+ ]
+ },
+ {
+ "name": "tokenMinter",
+ "isMut": false,
+ "isSigner": false
+ },
+ {
+ "name": "localToken",
+ "isMut": true,
+ "isSigner": false,
+ "docs": [
+ "Token Messenger Minter's Local Token account. This program uses the mint of this account to",
+ "validate the `mint_recipient` token account's mint.",
+ ""
+ ]
+ },
+ {
+ "name": "tokenPair",
+ "isMut": false,
+ "isSigner": false,
+ "docs": [
+ "Token Messenger Minter program)."
+ ]
+ },
+ {
+ "name": "tokenMessengerMinterCustodyToken",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "tokenMessengerMinterEventAuthority",
+ "isMut": false,
+ "isSigner": false
+ },
+ {
+ "name": "tokenMessengerMinterProgram",
+ "isMut": false,
+ "isSigner": false
+ },
+ {
+ "name": "messageTransmitterProgram",
+ "isMut": false,
+ "isSigner": false
+ }
]
},
- {
- "name": "tokenMessengerMinterCustodyToken",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "tokenMessengerMinterEventAuthority",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "tokenMessengerMinterProgram",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "messageTransmitterProgram",
- "isMut": false,
- "isSigner": false
- },
{
"name": "tokenProgram",
"isMut": false,
@@ -227,29 +255,20 @@ export type MatchingEngine = {
"name": "settleAuctionComplete",
"accounts": [
{
- "name": "custodian",
- "isMut": false,
+ "name": "executor",
+ "isMut": true,
"isSigner": false,
"docs": [
- "This program's Wormhole (Core Bridge) emitter authority.",
- ""
+ "we will always reward the owner of the executor token account with the lamports from the",
+ "prepared order response and its custody token account when we close these accounts. This",
+ "means we disregard the `prepared_by` field in the prepared order response."
]
},
{
- "name": "preparedBy",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "preparedOrderResponse",
+ "name": "executorToken",
"isMut": true,
"isSigner": false
},
- {
- "name": "auction",
- "isMut": false,
- "isSigner": false
- },
{
"name": "bestOfferToken",
"isMut": true,
@@ -262,18 +281,19 @@ export type MatchingEngine = {
]
},
{
- "name": "cctpMintRecipient",
+ "name": "preparedOrderResponse",
"isMut": true,
- "isSigner": false,
- "docs": [
- "Mint recipient token account, which is encoded as the mint recipient in the CCTP message.",
- "The CCTP Token Messenger Minter program will transfer the amount encoded in the CCTP message",
- "from its custody account to this account.",
- "",
- "Mutable. Seeds must be \\[\"custody\"\\].",
- "",
- "NOTE: This account must be encoded as the mint recipient in the CCTP message."
- ]
+ "isSigner": false
+ },
+ {
+ "name": "preparedCustodyToken",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "auction",
+ "isMut": true,
+ "isSigner": false
},
{
"name": "tokenProgram",
@@ -297,46 +317,23 @@ export type MatchingEngine = {
"isSigner": false
},
{
- "name": "custodian",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "This program's Wormhole (Core Bridge) emitter authority.",
- ""
- ]
- },
- {
- "name": "fastVaa",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "zero-copy using the [VaaAccount](core_bridge_program::sdk::VaaAccount) reader."
- ]
- },
- {
- "name": "preparedOrderResponse",
+ "name": "coreMessage",
"isMut": true,
"isSigner": false
},
{
- "name": "auction",
+ "name": "cctpMessage",
"isMut": true,
- "isSigner": false,
- "docs": [
- "There should be no account data here because an auction was never created."
- ]
+ "isSigner": false
},
{
- "name": "cctpMintRecipient",
- "isMut": true,
- "isSigner": false,
- "docs": [
- "Mint recipient token account, which is encoded as the mint recipient in the CCTP message.",
- "The CCTP Token Messenger Minter program will transfer the amount encoded in the CCTP message",
- "from its custody account to this account.",
- "",
- "",
- "NOTE: This account must be encoded as the mint recipient in the CCTP message."
+ "name": "custodian",
+ "accounts": [
+ {
+ "name": "custodian",
+ "isMut": false,
+ "isSigner": false
+ }
]
},
{
@@ -351,116 +348,173 @@ export type MatchingEngine = {
]
},
{
- "name": "fromRouterEndpoint",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "Seeds must be \\[\"endpoint\", chain.to_be_bytes()\\]."
- ]
- },
- {
- "name": "toRouterEndpoint",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "Seeds must be \\[\"endpoint\", chain.to_be_bytes()\\]."
+ "name": "prepared",
+ "accounts": [
+ {
+ "name": "by",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "orderResponse",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "custodyToken",
+ "isMut": true,
+ "isSigner": false
+ }
]
},
{
- "name": "mint",
- "isMut": true,
- "isSigner": false,
- "docs": [
- "Circle-supported mint.",
- "",
- "Token Messenger Minter program's local token account."
+ "name": "fastOrderPath",
+ "accounts": [
+ {
+ "name": "fastVaa",
+ "accounts": [
+ {
+ "name": "vaa",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
+ },
+ {
+ "name": "from",
+ "accounts": [
+ {
+ "name": "endpoint",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
+ },
+ {
+ "name": "to",
+ "accounts": [
+ {
+ "name": "endpoint",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
+ }
]
},
{
- "name": "coreBridgeConfig",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "coreMessage",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "cctpMessage",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "coreEmitterSequence",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "coreFeeCollector",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "tokenMessengerMinterSenderAuthority",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "messageTransmitterConfig",
+ "name": "auction",
"isMut": true,
- "isSigner": false
- },
- {
- "name": "tokenMessenger",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "remoteTokenMessenger",
- "isMut": false,
"isSigner": false,
"docs": [
- "Messenger Minter program)."
+ "There should be no account data here because an auction was never created."
]
},
{
- "name": "tokenMinter",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "CHECK Seeds must be \\[\"token_minter\"\\] (CCTP Token Messenger Minter program)."
+ "name": "wormhole",
+ "accounts": [
+ {
+ "name": "config",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "emitterSequence",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "feeCollector",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "coreBridgeProgram",
+ "isMut": false,
+ "isSigner": false
+ }
]
},
{
- "name": "localToken",
- "isMut": true,
- "isSigner": false,
- "docs": [
- "Local token account, which this program uses to validate the `mint` used to burn.",
- ""
+ "name": "cctp",
+ "accounts": [
+ {
+ "name": "burnSource",
+ "accounts": [
+ {
+ "name": "mintRecipient",
+ "isMut": true,
+ "isSigner": false
+ }
+ ]
+ },
+ {
+ "name": "mint",
+ "isMut": true,
+ "isSigner": false,
+ "docs": [
+ "Circle-supported mint.",
+ "",
+ "Token Messenger Minter program's local token account."
+ ]
+ },
+ {
+ "name": "tokenMessengerMinterSenderAuthority",
+ "isMut": false,
+ "isSigner": false
+ },
+ {
+ "name": "messageTransmitterConfig",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "tokenMessenger",
+ "isMut": false,
+ "isSigner": false
+ },
+ {
+ "name": "remoteTokenMessenger",
+ "isMut": false,
+ "isSigner": false,
+ "docs": [
+ "Messenger Minter program)."
+ ]
+ },
+ {
+ "name": "tokenMinter",
+ "isMut": false,
+ "isSigner": false,
+ "docs": [
+ "CHECK Seeds must be \\[\"token_minter\"\\] (CCTP Token Messenger Minter program)."
+ ]
+ },
+ {
+ "name": "localToken",
+ "isMut": true,
+ "isSigner": false,
+ "docs": [
+ "Local token account, which this program uses to validate the `mint` used to burn.",
+ ""
+ ]
+ },
+ {
+ "name": "tokenMessengerMinterEventAuthority",
+ "isMut": false,
+ "isSigner": false
+ },
+ {
+ "name": "tokenMessengerMinterProgram",
+ "isMut": false,
+ "isSigner": false
+ },
+ {
+ "name": "messageTransmitterProgram",
+ "isMut": false,
+ "isSigner": false
+ }
]
},
- {
- "name": "tokenMessengerMinterEventAuthority",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "coreBridgeProgram",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "tokenMessengerMinterProgram",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "messageTransmitterProgram",
- "isMut": false,
- "isSigner": false
- },
{
"name": "tokenProgram",
"isMut": false,
@@ -472,14 +526,27 @@ export type MatchingEngine = {
"isSigner": false
},
{
- "name": "clock",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "rent",
- "isMut": false,
- "isSigner": false
+ "name": "sysvars",
+ "accounts": [
+ {
+ "name": "clock",
+ "isMut": false,
+ "isSigner": false,
+ "docs": [
+ "Wormhole Core Bridge needs the clock sysvar based on its legacy implementation.",
+ ""
+ ]
+ },
+ {
+ "name": "rent",
+ "isMut": false,
+ "isSigner": false,
+ "docs": [
+ "Wormhole Core Bridge needs the rent sysvar based on its legacy implementation.",
+ ""
+ ]
+ }
+ ]
}
],
"args": []
@@ -498,46 +565,18 @@ export type MatchingEngine = {
"isSigner": false
},
{
- "name": "custodian",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "This program's Wormhole (Core Bridge) emitter authority.",
- ""
- ]
- },
- {
- "name": "fastVaa",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "zero-copy using the [VaaAccount](core_bridge_program::sdk::VaaAccount) reader."
- ]
- },
- {
- "name": "preparedOrderResponse",
+ "name": "coreMessage",
"isMut": true,
"isSigner": false
},
{
- "name": "auction",
- "isMut": true,
- "isSigner": false,
- "docs": [
- "There should be no account data here because an auction was never created."
- ]
- },
- {
- "name": "cctpMintRecipient",
- "isMut": true,
- "isSigner": false,
- "docs": [
- "Mint recipient token account, which is encoded as the mint recipient in the CCTP message.",
- "The CCTP Token Messenger Minter program will transfer the amount encoded in the CCTP message",
- "from its custody account to this account.",
- "",
- "",
- "NOTE: This account must be encoded as the mint recipient in the CCTP message."
+ "name": "custodian",
+ "accounts": [
+ {
+ "name": "custodian",
+ "isMut": false,
+ "isSigner": false
+ }
]
},
{
@@ -552,46 +591,98 @@ export type MatchingEngine = {
]
},
{
- "name": "fromRouterEndpoint",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "Seeds must be \\[\"endpoint\", chain.to_be_bytes()\\]."
+ "name": "prepared",
+ "accounts": [
+ {
+ "name": "by",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "orderResponse",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "custodyToken",
+ "isMut": true,
+ "isSigner": false
+ }
]
},
{
- "name": "toRouterEndpoint",
- "isMut": false,
+ "name": "fastOrderPath",
+ "accounts": [
+ {
+ "name": "fastVaa",
+ "accounts": [
+ {
+ "name": "vaa",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
+ },
+ {
+ "name": "from",
+ "accounts": [
+ {
+ "name": "endpoint",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
+ },
+ {
+ "name": "to",
+ "accounts": [
+ {
+ "name": "endpoint",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "name": "auction",
+ "isMut": true,
"isSigner": false,
"docs": [
- "Seeds must be \\[\"endpoint\", chain.to_be_bytes()\\]."
+ "There should be no account data here because an auction was never created."
]
},
{
- "name": "coreBridgeConfig",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "coreMessage",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "coreEmitterSequence",
- "isMut": true,
- "isSigner": false
+ "name": "wormhole",
+ "accounts": [
+ {
+ "name": "config",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "emitterSequence",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "feeCollector",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "coreBridgeProgram",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
},
{
- "name": "coreFeeCollector",
+ "name": "localCustodyToken",
"isMut": true,
"isSigner": false
},
- {
- "name": "coreBridgeProgram",
- "isMut": false,
- "isSigner": false
- },
{
"name": "tokenProgram",
"isMut": false,
@@ -603,194 +694,115 @@ export type MatchingEngine = {
"isSigner": false
},
{
- "name": "clock",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "rent",
- "isMut": false,
- "isSigner": false
+ "name": "sysvars",
+ "accounts": [
+ {
+ "name": "clock",
+ "isMut": false,
+ "isSigner": false,
+ "docs": [
+ "Wormhole Core Bridge needs the clock sysvar based on its legacy implementation.",
+ ""
+ ]
+ },
+ {
+ "name": "rent",
+ "isMut": false,
+ "isSigner": false,
+ "docs": [
+ "Wormhole Core Bridge needs the rent sysvar based on its legacy implementation.",
+ ""
+ ]
+ }
+ ]
}
],
"args": []
},
{
- "name": "settleAuctionActiveCctp",
+ "name": "initialize",
+ "docs": [
+ "This instruction is be used to generate your program's config.",
+ "And for convenience, we will store Wormhole-related PDAs in the",
+ "config so we can verify these accounts with a simple == constraint."
+ ],
"accounts": [
{
- "name": "payer",
- "isMut": true,
- "isSigner": true
- },
- {
- "name": "payerSequence",
+ "name": "owner",
"isMut": true,
- "isSigner": false
- },
- {
- "name": "custodian",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "This program's Wormhole (Core Bridge) emitter authority.",
- ""
- ]
- },
- {
- "name": "auctionConfig",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "fastVaa",
- "isMut": false,
- "isSigner": false,
+ "isSigner": true,
"docs": [
- "zero-copy using the [VaaAccount](core_bridge_program::sdk::VaaAccount) reader."
+ "Owner of the program, who presumably deployed this program."
]
},
{
- "name": "preparedOrderResponse",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "auction",
+ "name": "custodian",
"isMut": true,
"isSigner": false,
"docs": [
- "There should be no account data here because an auction was never created."
+ "Custodian account, which saves program data useful for other",
+ "instructions."
]
},
{
- "name": "bestOfferToken",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "executorToken",
+ "name": "auctionConfig",
"isMut": true,
"isSigner": false
},
{
- "name": "cctpMintRecipient",
- "isMut": true,
- "isSigner": false,
- "docs": [
- "Mint recipient token account, which is encoded as the mint recipient in the CCTP message.",
- "The CCTP Token Messenger Minter program will transfer the amount encoded in the CCTP message",
- "from its custody account to this account.",
- "",
- "",
- "NOTE: This account must be encoded as the mint recipient in the CCTP message."
- ]
- },
- {
- "name": "toRouterEndpoint",
+ "name": "ownerAssistant",
"isMut": false,
"isSigner": false,
"docs": [
- "Seeds must be \\[\"endpoint\", chain.to_be_bytes()\\]."
- ]
- },
- {
- "name": "mint",
- "isMut": true,
- "isSigner": false,
- "docs": [
- "Circle-supported mint.",
- "",
- "Token Messenger Minter program's local token account."
+ "TODO: do we prevent the owner from being the owner assistant?"
]
},
{
- "name": "coreBridgeConfig",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "coreMessage",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "cctpMessage",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "coreEmitterSequence",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "coreFeeCollector",
- "isMut": true,
+ "name": "feeRecipient",
+ "isMut": false,
"isSigner": false
},
{
- "name": "tokenMessengerMinterSenderAuthority",
+ "name": "feeRecipientToken",
"isMut": false,
"isSigner": false
},
{
- "name": "messageTransmitterConfig",
+ "name": "cctpMintRecipient",
"isMut": true,
"isSigner": false
},
{
- "name": "tokenMessenger",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "remoteTokenMessenger",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "Messenger Minter program)."
- ]
- },
- {
- "name": "tokenMinter",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "CHECK Seeds must be \\[\"token_minter\"\\] (CCTP Token Messenger Minter program)."
+ "name": "usdc",
+ "accounts": [
+ {
+ "name": "mint",
+ "isMut": false,
+ "isSigner": false
+ }
]
},
{
- "name": "localToken",
+ "name": "programData",
"isMut": true,
"isSigner": false,
"docs": [
- "Local token account, which this program uses to validate the `mint` used to burn.",
- ""
+ "We use the program data to make sure this owner is the upgrade authority (the true owner,",
+ "who deployed this program)."
]
},
{
- "name": "tokenMessengerMinterEventAuthority",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "coreBridgeProgram",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "tokenMessengerMinterProgram",
+ "name": "upgradeManagerAuthority",
"isMut": false,
"isSigner": false
},
{
- "name": "messageTransmitterProgram",
+ "name": "upgradeManagerProgram",
"isMut": false,
"isSigner": false
},
{
- "name": "tokenProgram",
+ "name": "bpfLoaderUpgradeableProgram",
"isMut": false,
"isSigner": false
},
@@ -800,20 +812,27 @@ export type MatchingEngine = {
"isSigner": false
},
{
- "name": "clock",
+ "name": "tokenProgram",
"isMut": false,
"isSigner": false
},
{
- "name": "rent",
+ "name": "associatedTokenProgram",
"isMut": false,
"isSigner": false
}
],
- "args": []
+ "args": [
+ {
+ "name": "auctionParams",
+ "type": {
+ "defined": "AuctionParameters"
+ }
+ }
+ ]
},
{
- "name": "settleAuctionActiveLocal",
+ "name": "addCctpRouterEndpoint",
"accounts": [
{
"name": "payer",
@@ -821,274 +840,77 @@ export type MatchingEngine = {
"isSigner": true
},
{
- "name": "payerSequence",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "custodian",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "This program's Wormhole (Core Bridge) emitter authority.",
- ""
+ "name": "admin",
+ "accounts": [
+ {
+ "name": "ownerOrAssistant",
+ "isMut": false,
+ "isSigner": true
+ },
+ {
+ "name": "custodian",
+ "accounts": [
+ {
+ "name": "custodian",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
+ }
]
},
{
- "name": "auctionConfig",
- "isMut": false,
+ "name": "routerEndpoint",
+ "isMut": true,
"isSigner": false
},
{
- "name": "fastVaa",
+ "name": "localRouterEndpoint",
"isMut": false,
"isSigner": false,
"docs": [
- "zero-copy using the [VaaAccount](core_bridge_program::sdk::VaaAccount) reader."
+ "Local router endpoint PDA.",
+ "",
+ "NOTE: This account may not exist yet. But we need to pass it since it will be the owner of",
+ "the local custody token account.",
+ ""
]
},
{
- "name": "preparedOrderResponse",
+ "name": "localCustodyToken",
"isMut": true,
"isSigner": false
},
{
- "name": "auction",
- "isMut": true,
- "isSigner": false,
- "docs": [
- "There should be no account data here because an auction was never created."
+ "name": "usdc",
+ "accounts": [
+ {
+ "name": "mint",
+ "isMut": false,
+ "isSigner": false
+ }
]
},
{
- "name": "toRouterEndpoint",
+ "name": "remoteTokenMessenger",
"isMut": false,
"isSigner": false,
"docs": [
- "Seeds must be \\[\"endpoint\", chain.to_be_bytes()\\]."
+ "Messenger Minter program)."
]
},
{
- "name": "bestOfferToken",
- "isMut": true,
+ "name": "tokenProgram",
+ "isMut": false,
"isSigner": false
},
{
- "name": "executorToken",
- "isMut": true,
+ "name": "systemProgram",
+ "isMut": false,
"isSigner": false
- },
- {
- "name": "cctpMintRecipient",
- "isMut": true,
- "isSigner": false,
- "docs": [
- "Mint recipient token account, which is encoded as the mint recipient in the CCTP message.",
- "The CCTP Token Messenger Minter program will transfer the amount encoded in the CCTP message",
- "from its custody account to this account.",
- "",
- "",
- "NOTE: This account must be encoded as the mint recipient in the CCTP message."
- ]
- },
- {
- "name": "coreBridgeConfig",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "coreMessage",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "coreEmitterSequence",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "coreFeeCollector",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "coreBridgeProgram",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "tokenProgram",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "systemProgram",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "clock",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "rent",
- "isMut": false,
- "isSigner": false
- }
- ],
- "args": []
- },
- {
- "name": "initialize",
- "docs": [
- "This instruction is be used to generate your program's config.",
- "And for convenience, we will store Wormhole-related PDAs in the",
- "config so we can verify these accounts with a simple == constraint."
- ],
- "accounts": [
- {
- "name": "owner",
- "isMut": true,
- "isSigner": true,
- "docs": [
- "Owner of the program, who presumably deployed this program."
- ]
- },
- {
- "name": "custodian",
- "isMut": true,
- "isSigner": false,
- "docs": [
- "Custodian account, which saves program data useful for other",
- "instructions."
- ]
- },
- {
- "name": "auctionConfig",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "ownerAssistant",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "TODO: do we prevent the owner from being the owner assistant?"
- ]
- },
- {
- "name": "feeRecipient",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "feeRecipientToken",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "cctpMintRecipient",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "mint",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "programData",
- "isMut": true,
- "isSigner": false,
- "docs": [
- "We use the program data to make sure this owner is the upgrade authority (the true owner,",
- "who deployed this program)."
- ]
- },
- {
- "name": "upgradeManagerAuthority",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "upgradeManagerProgram",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "bpfLoaderUpgradeableProgram",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "systemProgram",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "tokenProgram",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "associatedTokenProgram",
- "isMut": false,
- "isSigner": false
- }
- ],
- "args": [
- {
- "name": "auctionParams",
- "type": {
- "defined": "AuctionParameters"
- }
- }
- ]
- },
- {
- "name": "addCctpRouterEndpoint",
- "accounts": [
- {
- "name": "payer",
- "isMut": true,
- "isSigner": true
- },
- {
- "name": "admin",
- "accounts": [
- {
- "name": "ownerOrAssistant",
- "isMut": false,
- "isSigner": true
- },
- {
- "name": "custodian",
- "isMut": false,
- "isSigner": false
- }
- ]
- },
- {
- "name": "routerEndpoint",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "remoteTokenMessenger",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "Messenger Minter program)."
- ]
- },
- {
- "name": "systemProgram",
- "isMut": false,
- "isSigner": false
- }
- ],
- "args": [
+ }
+ ],
+ "args": [
{
"name": "args",
"type": {
@@ -1115,8 +937,13 @@ export type MatchingEngine = {
},
{
"name": "custodian",
- "isMut": false,
- "isSigner": false
+ "accounts": [
+ {
+ "name": "custodian",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
}
]
},
@@ -1169,8 +996,13 @@ export type MatchingEngine = {
},
{
"name": "custodian",
- "isMut": false,
- "isSigner": false
+ "accounts": [
+ {
+ "name": "custodian",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
}
]
},
@@ -1178,7 +1010,7 @@ export type MatchingEngine = {
"name": "routerEndpoint",
"accounts": [
{
- "name": "inner",
+ "name": "endpoint",
"isMut": true,
"isSigner": false
}
@@ -1200,8 +1032,13 @@ export type MatchingEngine = {
},
{
"name": "custodian",
- "isMut": false,
- "isSigner": false
+ "accounts": [
+ {
+ "name": "custodian",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
}
]
},
@@ -1209,7 +1046,7 @@ export type MatchingEngine = {
"name": "routerEndpoint",
"accounts": [
{
- "name": "inner",
+ "name": "endpoint",
"isMut": true,
"isSigner": false
}
@@ -1246,8 +1083,13 @@ export type MatchingEngine = {
},
{
"name": "custodian",
- "isMut": false,
- "isSigner": false
+ "accounts": [
+ {
+ "name": "custodian",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
}
]
},
@@ -1255,7 +1097,7 @@ export type MatchingEngine = {
"name": "routerEndpoint",
"accounts": [
{
- "name": "inner",
+ "name": "endpoint",
"isMut": true,
"isSigner": false
}
@@ -1300,8 +1142,13 @@ export type MatchingEngine = {
},
{
"name": "custodian",
- "isMut": true,
- "isSigner": false
+ "accounts": [
+ {
+ "name": "custodian",
+ "isMut": true,
+ "isSigner": false
+ }
+ ]
}
]
},
@@ -1350,8 +1197,13 @@ export type MatchingEngine = {
},
{
"name": "custodian",
- "isMut": true,
- "isSigner": false
+ "accounts": [
+ {
+ "name": "custodian",
+ "isMut": true,
+ "isSigner": false
+ }
+ ]
}
]
}
@@ -1376,8 +1228,13 @@ export type MatchingEngine = {
},
{
"name": "custodian",
- "isMut": true,
- "isSigner": false
+ "accounts": [
+ {
+ "name": "custodian",
+ "isMut": true,
+ "isSigner": false
+ }
+ ]
}
]
},
@@ -1424,8 +1281,13 @@ export type MatchingEngine = {
},
{
"name": "custodian",
- "isMut": true,
- "isSigner": false
+ "accounts": [
+ {
+ "name": "custodian",
+ "isMut": true,
+ "isSigner": false
+ }
+ ]
}
]
},
@@ -1451,18 +1313,25 @@ export type MatchingEngine = {
"name": "updateOwnerAssistant",
"accounts": [
{
- "name": "owner",
- "isMut": false,
- "isSigner": true,
- "docs": [
- "Owner of the program set in the [`OwnerConfig`] account."
+ "name": "admin",
+ "accounts": [
+ {
+ "name": "owner",
+ "isMut": false,
+ "isSigner": true
+ },
+ {
+ "name": "custodian",
+ "accounts": [
+ {
+ "name": "custodian",
+ "isMut": true,
+ "isSigner": false
+ }
+ ]
+ }
]
},
- {
- "name": "custodian",
- "isMut": true,
- "isSigner": false
- },
{
"name": "newOwnerAssistant",
"isMut": false,
@@ -1479,20 +1348,30 @@ export type MatchingEngine = {
"name": "updateFeeRecipient",
"accounts": [
{
- "name": "ownerOrAssistant",
- "isMut": true,
- "isSigner": true
- },
- {
- "name": "custodian",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "newFeeRecipientToken",
- "isMut": false,
- "isSigner": false
- },
+ "name": "admin",
+ "accounts": [
+ {
+ "name": "ownerOrAssistant",
+ "isMut": false,
+ "isSigner": true
+ },
+ {
+ "name": "custodian",
+ "accounts": [
+ {
+ "name": "custodian",
+ "isMut": true,
+ "isSigner": false
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "name": "newFeeRecipientToken",
+ "isMut": false,
+ "isSigner": false
+ },
{
"name": "newFeeRecipient",
"isMut": false,
@@ -1515,11 +1394,12 @@ export type MatchingEngine = {
},
{
"name": "custodian",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "This program's Wormhole (Core Bridge) emitter authority.",
- ""
+ "accounts": [
+ {
+ "name": "custodian",
+ "isMut": false,
+ "isSigner": false
+ }
]
},
{
@@ -1528,9 +1408,39 @@ export type MatchingEngine = {
"isSigner": false
},
{
- "name": "fastVaa",
- "isMut": false,
- "isSigner": false
+ "name": "fastOrderPath",
+ "accounts": [
+ {
+ "name": "fastVaa",
+ "accounts": [
+ {
+ "name": "vaa",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
+ },
+ {
+ "name": "from",
+ "accounts": [
+ {
+ "name": "endpoint",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
+ },
+ {
+ "name": "to",
+ "accounts": [
+ {
+ "name": "endpoint",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
+ }
+ ]
},
{
"name": "auction",
@@ -1542,24 +1452,27 @@ export type MatchingEngine = {
]
},
{
- "name": "fromRouterEndpoint",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "toRouterEndpoint",
+ "name": "offerToken",
"isMut": false,
- "isSigner": false
+ "isSigner": false,
+ "docs": [
+ "the auction PDA."
+ ]
},
{
- "name": "offerToken",
+ "name": "auctionCustodyToken",
"isMut": true,
"isSigner": false
},
{
- "name": "cctpMintRecipient",
- "isMut": true,
- "isSigner": false
+ "name": "usdc",
+ "accounts": [
+ {
+ "name": "mint",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
},
{
"name": "systemProgram",
@@ -1583,39 +1496,37 @@ export type MatchingEngine = {
"name": "improveOffer",
"accounts": [
{
- "name": "custodian",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "This program's Wormhole (Core Bridge) emitter authority. This is also the burn-source",
- "authority for CCTP transfers.",
- ""
+ "name": "activeAuction",
+ "accounts": [
+ {
+ "name": "auction",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "custodyToken",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "config",
+ "isMut": false,
+ "isSigner": false
+ },
+ {
+ "name": "bestOfferToken",
+ "isMut": true,
+ "isSigner": false
+ }
]
},
- {
- "name": "auctionConfig",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "offerAuthority",
- "isMut": false,
- "isSigner": true
- },
- {
- "name": "auction",
- "isMut": true,
- "isSigner": false
- },
{
"name": "offerToken",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "bestOfferToken",
- "isMut": true,
- "isSigner": false
+ "isMut": false,
+ "isSigner": false,
+ "docs": [
+ "the auction PDA."
+ ]
},
{
"name": "tokenProgram",
@@ -1644,157 +1555,193 @@ export type MatchingEngine = {
"isSigner": false
},
{
- "name": "custodian",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "This program's Wormhole (Core Bridge) emitter authority. This is also the burn-source",
- "authority for CCTP transfers.",
- ""
- ]
- },
- {
- "name": "auctionConfig",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "fastVaa",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "auction",
+ "name": "coreMessage",
"isMut": true,
"isSigner": false
},
{
- "name": "toRouterEndpoint",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "executorToken",
+ "name": "cctpMessage",
"isMut": true,
"isSigner": false
},
{
- "name": "bestOfferToken",
- "isMut": true,
- "isSigner": false
+ "name": "custodian",
+ "accounts": [
+ {
+ "name": "custodian",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
},
{
- "name": "initialOfferToken",
- "isMut": true,
- "isSigner": false
+ "name": "executeOrder",
+ "accounts": [
+ {
+ "name": "fastVaa",
+ "accounts": [
+ {
+ "name": "vaa",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
+ },
+ {
+ "name": "activeAuction",
+ "accounts": [
+ {
+ "name": "auction",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "custodyToken",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "config",
+ "isMut": false,
+ "isSigner": false
+ },
+ {
+ "name": "bestOfferToken",
+ "isMut": true,
+ "isSigner": false
+ }
+ ]
+ },
+ {
+ "name": "toRouterEndpoint",
+ "accounts": [
+ {
+ "name": "endpoint",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
+ },
+ {
+ "name": "executorToken",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "initialOfferToken",
+ "isMut": true,
+ "isSigner": false
+ }
+ ]
},
{
- "name": "cctpMintRecipient",
- "isMut": true,
- "isSigner": false,
- "docs": [
- "Also the burn_source token account.",
- ""
+ "name": "wormhole",
+ "accounts": [
+ {
+ "name": "config",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "emitterSequence",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "feeCollector",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "coreBridgeProgram",
+ "isMut": false,
+ "isSigner": false
+ }
]
},
{
- "name": "mint",
- "isMut": true,
- "isSigner": false,
- "docs": [
- "Circle-supported mint.",
- "",
- "Token Messenger Minter program's local token account."
+ "name": "cctp",
+ "accounts": [
+ {
+ "name": "burnSource",
+ "accounts": [
+ {
+ "name": "mintRecipient",
+ "isMut": true,
+ "isSigner": false
+ }
+ ]
+ },
+ {
+ "name": "mint",
+ "isMut": true,
+ "isSigner": false,
+ "docs": [
+ "Circle-supported mint.",
+ "",
+ "Token Messenger Minter program's local token account."
+ ]
+ },
+ {
+ "name": "tokenMessengerMinterSenderAuthority",
+ "isMut": false,
+ "isSigner": false
+ },
+ {
+ "name": "messageTransmitterConfig",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "tokenMessenger",
+ "isMut": false,
+ "isSigner": false
+ },
+ {
+ "name": "remoteTokenMessenger",
+ "isMut": false,
+ "isSigner": false,
+ "docs": [
+ "Messenger Minter program)."
+ ]
+ },
+ {
+ "name": "tokenMinter",
+ "isMut": false,
+ "isSigner": false,
+ "docs": [
+ "CHECK Seeds must be \\[\"token_minter\"\\] (CCTP Token Messenger Minter program)."
+ ]
+ },
+ {
+ "name": "localToken",
+ "isMut": true,
+ "isSigner": false,
+ "docs": [
+ "Local token account, which this program uses to validate the `mint` used to burn.",
+ ""
+ ]
+ },
+ {
+ "name": "tokenMessengerMinterEventAuthority",
+ "isMut": false,
+ "isSigner": false
+ },
+ {
+ "name": "tokenMessengerMinterProgram",
+ "isMut": false,
+ "isSigner": false
+ },
+ {
+ "name": "messageTransmitterProgram",
+ "isMut": false,
+ "isSigner": false
+ }
]
},
{
- "name": "coreBridgeConfig",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "coreMessage",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "cctpMessage",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "coreEmitterSequence",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "coreFeeCollector",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "tokenMessengerMinterSenderAuthority",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "messageTransmitterConfig",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "tokenMessenger",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "remoteTokenMessenger",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "Messenger Minter program)."
- ]
- },
- {
- "name": "tokenMinter",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "CHECK Seeds must be \\[\"token_minter\"\\] (CCTP Token Messenger Minter program)."
- ]
- },
- {
- "name": "localToken",
- "isMut": true,
- "isSigner": false,
- "docs": [
- "Local token account, which this program uses to validate the `mint` used to burn.",
- ""
- ]
- },
- {
- "name": "tokenMessengerMinterEventAuthority",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "coreBridgeProgram",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "tokenMessengerMinterProgram",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "messageTransmitterProgram",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "systemProgram",
- "isMut": false,
+ "name": "systemProgram",
+ "isMut": false,
"isSigner": false
},
{
@@ -1803,14 +1750,27 @@ export type MatchingEngine = {
"isSigner": false
},
{
- "name": "clock",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "rent",
- "isMut": false,
- "isSigner": false
+ "name": "sysvars",
+ "accounts": [
+ {
+ "name": "clock",
+ "isMut": false,
+ "isSigner": false,
+ "docs": [
+ "Wormhole Core Bridge needs the clock sysvar based on its legacy implementation.",
+ ""
+ ]
+ },
+ {
+ "name": "rent",
+ "isMut": false,
+ "isSigner": false,
+ "docs": [
+ "Wormhole Core Bridge needs the rent sysvar based on its legacy implementation.",
+ ""
+ ]
+ }
+ ]
}
],
"args": []
@@ -1829,84 +1789,110 @@ export type MatchingEngine = {
"isSigner": false
},
{
- "name": "custodian",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "This program's Wormhole (Core Bridge) emitter authority. This is also the burn-source",
- "authority for CCTP transfers.",
- ""
- ]
- },
- {
- "name": "auctionConfig",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "fastVaa",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "auction",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "toRouterEndpoint",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "executorToken",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "bestOfferToken",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "initialOfferToken",
+ "name": "coreMessage",
"isMut": true,
"isSigner": false
},
{
- "name": "cctpMintRecipient",
- "isMut": true,
- "isSigner": false,
- "docs": [
- "Also the burn_source token account.",
- ""
+ "name": "custodian",
+ "accounts": [
+ {
+ "name": "custodian",
+ "isMut": false,
+ "isSigner": false
+ }
]
},
{
- "name": "coreBridgeConfig",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "coreMessage",
- "isMut": true,
- "isSigner": false
+ "name": "executeOrder",
+ "accounts": [
+ {
+ "name": "fastVaa",
+ "accounts": [
+ {
+ "name": "vaa",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
+ },
+ {
+ "name": "activeAuction",
+ "accounts": [
+ {
+ "name": "auction",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "custodyToken",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "config",
+ "isMut": false,
+ "isSigner": false
+ },
+ {
+ "name": "bestOfferToken",
+ "isMut": true,
+ "isSigner": false
+ }
+ ]
+ },
+ {
+ "name": "toRouterEndpoint",
+ "accounts": [
+ {
+ "name": "endpoint",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
+ },
+ {
+ "name": "executorToken",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "initialOfferToken",
+ "isMut": true,
+ "isSigner": false
+ }
+ ]
},
{
- "name": "coreEmitterSequence",
- "isMut": true,
- "isSigner": false
+ "name": "wormhole",
+ "accounts": [
+ {
+ "name": "config",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "emitterSequence",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "feeCollector",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "coreBridgeProgram",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
},
{
- "name": "coreFeeCollector",
+ "name": "localCustodyToken",
"isMut": true,
"isSigner": false
},
- {
- "name": "coreBridgeProgram",
- "isMut": false,
- "isSigner": false
- },
{
"name": "systemProgram",
"isMut": false,
@@ -1918,14 +1904,27 @@ export type MatchingEngine = {
"isSigner": false
},
{
- "name": "clock",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "rent",
- "isMut": false,
- "isSigner": false
+ "name": "sysvars",
+ "accounts": [
+ {
+ "name": "clock",
+ "isMut": false,
+ "isSigner": false,
+ "docs": [
+ "Wormhole Core Bridge needs the clock sysvar based on its legacy implementation.",
+ ""
+ ]
+ },
+ {
+ "name": "rent",
+ "isMut": false,
+ "isSigner": false,
+ "docs": [
+ "Wormhole Core Bridge needs the rent sysvar based on its legacy implementation.",
+ ""
+ ]
+ }
+ ]
}
],
"args": []
@@ -1943,8 +1942,13 @@ export type MatchingEngine = {
},
{
"name": "custodian",
- "isMut": false,
- "isSigner": false
+ "accounts": [
+ {
+ "name": "custodian",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
}
]
},
@@ -2294,6 +2298,10 @@ export type MatchingEngine = {
"name": "configId",
"type": "u32"
},
+ {
+ "name": "custodyTokenBump",
+ "type": "u8"
+ },
{
"name": "vaaSequence",
"docs": [
@@ -2358,6 +2366,10 @@ export type MatchingEngine = {
"the [amount_in](Self::amount_in)."
],
"type": "u64"
+ },
+ {
+ "name": "endEarly",
+ "type": "bool"
}
]
}
@@ -2415,6 +2427,12 @@ export type MatchingEngine = {
{
"name": "slot",
"type": "u64"
+ },
+ {
+ "name": "executePenalty",
+ "type": {
+ "option": "u64"
+ }
}
]
},
@@ -2426,7 +2444,7 @@ export type MatchingEngine = {
"type": "u64"
},
{
- "name": "penalty",
+ "name": "totalPenalty",
"type": {
"option": "u64"
}
@@ -2506,11 +2524,6 @@ export type MatchingEngine = {
"name": "OwnerOrAssistantOnly",
"msg": "OwnerOrAssistantOnly"
},
- {
- "code": 6006,
- "name": "InvalidCustodyToken",
- "msg": "InvalidCustodyToken"
- },
{
"code": 6008,
"name": "CpiDisallowed",
@@ -2546,11 +2559,6 @@ export type MatchingEngine = {
"name": "ImmutableProgram",
"msg": "ImmutableProgram"
},
- {
- "code": 6259,
- "name": "NotUsdc",
- "msg": "NotUsdc"
- },
{
"code": 6514,
"name": "InvalidNewOwner",
@@ -2733,8 +2741,8 @@ export type MatchingEngine = {
},
{
"code": 6556,
- "name": "BestOfferTokenMismatch",
- "msg": "BestOfferTokenMismatch"
+ "name": "ExecutorTokenMismatch",
+ "msg": "ExecutorTokenMismatch"
},
{
"code": 6557,
@@ -2803,15 +2811,22 @@ export const IDL: MatchingEngine = {
},
{
"name": "custodian",
- "isMut": false,
- "isSigner": false
+ "accounts": [
+ {
+ "name": "custodian",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
},
{
- "name": "vaa",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "zero-copy using the [VaaAccount](core_bridge_program::sdk::VaaAccount) reader."
+ "name": "fastFillVaa",
+ "accounts": [
+ {
+ "name": "vaa",
+ "isMut": false,
+ "isSigner": false
+ }
]
},
{
@@ -2833,23 +2848,16 @@ export const IDL: MatchingEngine = {
"name": "routerEndpoint",
"accounts": [
{
- "name": "inner",
+ "name": "endpoint",
"isMut": false,
"isSigner": false
}
]
},
{
- "name": "cctpMintRecipient",
+ "name": "localCustodyToken",
"isMut": true,
- "isSigner": false,
- "docs": [
- "Mint recipient token account, which is encoded as the mint recipient in the CCTP message.",
- "The CCTP Token Messenger Minter program will transfer the amount encoded in the CCTP message",
- "from its custody account to this account.",
- "",
- "Mutable. Seeds must be \\[\"custody\"\\]."
- ]
+ "isSigner": false
},
{
"name": "tokenProgram",
@@ -2874,238 +2882,41 @@ export const IDL: MatchingEngine = {
},
{
"name": "custodian",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "This program's Wormhole (Core Bridge) emitter authority.",
- ""
+ "accounts": [
+ {
+ "name": "custodian",
+ "isMut": false,
+ "isSigner": false
+ }
]
},
{
"name": "fastVaa",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "zero-copy using the [VaaAccount](core_bridge_program::sdk::VaaAccount) reader."
+ "accounts": [
+ {
+ "name": "vaa",
+ "isMut": false,
+ "isSigner": false
+ }
]
},
{
"name": "finalizedVaa",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "[verify_vaa_and_mint](wormhole_cctp_solana::cpi::verify_vaa_and_mint)."
- ]
- },
- {
- "name": "preparedOrderResponse",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "cctpMintRecipient",
- "isMut": true,
- "isSigner": false,
- "docs": [
- "Mint recipient token account, which is encoded as the mint recipient in the CCTP message.",
- "The CCTP Token Messenger Minter program will transfer the amount encoded in the CCTP message",
- "from its custody account to this account.",
- "",
- "",
- "NOTE: This account must be encoded as the mint recipient in the CCTP message."
- ]
- },
- {
- "name": "messageTransmitterAuthority",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "messageTransmitterConfig",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "usedNonces",
- "isMut": true,
- "isSigner": false,
- "docs": [
- "first_nonce.to_string()\\] (CCTP Message Transmitter program)."
- ]
- },
- {
- "name": "messageTransmitterEventAuthority",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "tokenMessenger",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "remoteTokenMessenger",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "Messenger Minter program)."
- ]
- },
- {
- "name": "tokenMinter",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "localToken",
- "isMut": true,
- "isSigner": false,
- "docs": [
- "Token Messenger Minter's Local Token account. This program uses the mint of this account to",
- "validate the `mint_recipient` token account's mint.",
- ""
- ]
- },
- {
- "name": "tokenPair",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "Token Messenger Minter program)."
- ]
- },
- {
- "name": "tokenMessengerMinterCustodyToken",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "tokenMessengerMinterEventAuthority",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "tokenMessengerMinterProgram",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "messageTransmitterProgram",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "tokenProgram",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "systemProgram",
- "isMut": false,
- "isSigner": false
- }
- ],
- "args": [
- {
- "name": "args",
- "type": {
- "defined": "CctpMessageArgs"
- }
- }
- ]
- },
- {
- "name": "settleAuctionComplete",
- "accounts": [
- {
- "name": "custodian",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "This program's Wormhole (Core Bridge) emitter authority.",
- ""
+ "accounts": [
+ {
+ "name": "vaa",
+ "isMut": false,
+ "isSigner": false
+ }
]
},
- {
- "name": "preparedBy",
- "isMut": true,
- "isSigner": false
- },
{
"name": "preparedOrderResponse",
"isMut": true,
"isSigner": false
},
{
- "name": "auction",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "bestOfferToken",
- "isMut": true,
- "isSigner": false,
- "docs": [
- "Destination token account, which the redeemer may not own. But because the redeemer is a",
- "signer and is the one encoded in the Deposit Fill message, he may have the tokens be sent",
- "to any account he chooses (this one).",
- ""
- ]
- },
- {
- "name": "cctpMintRecipient",
- "isMut": true,
- "isSigner": false,
- "docs": [
- "Mint recipient token account, which is encoded as the mint recipient in the CCTP message.",
- "The CCTP Token Messenger Minter program will transfer the amount encoded in the CCTP message",
- "from its custody account to this account.",
- "",
- "Mutable. Seeds must be \\[\"custody\"\\].",
- "",
- "NOTE: This account must be encoded as the mint recipient in the CCTP message."
- ]
- },
- {
- "name": "tokenProgram",
- "isMut": false,
- "isSigner": false
- }
- ],
- "args": []
- },
- {
- "name": "settleAuctionNoneCctp",
- "accounts": [
- {
- "name": "payer",
- "isMut": true,
- "isSigner": true
- },
- {
- "name": "payerSequence",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "custodian",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "This program's Wormhole (Core Bridge) emitter authority.",
- ""
- ]
- },
- {
- "name": "fastVaa",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "zero-copy using the [VaaAccount](core_bridge_program::sdk::VaaAccount) reader."
- ]
- },
- {
- "name": "preparedOrderResponse",
+ "name": "preparedCustodyToken",
"isMut": true,
"isSigner": false
},
@@ -3113,145 +2924,112 @@ export const IDL: MatchingEngine = {
"name": "auction",
"isMut": true,
"isSigner": false,
- "docs": [
- "There should be no account data here because an auction was never created."
- ]
- },
- {
- "name": "cctpMintRecipient",
- "isMut": true,
- "isSigner": false,
- "docs": [
- "Mint recipient token account, which is encoded as the mint recipient in the CCTP message.",
- "The CCTP Token Messenger Minter program will transfer the amount encoded in the CCTP message",
- "from its custody account to this account.",
- "",
- "",
- "NOTE: This account must be encoded as the mint recipient in the CCTP message."
- ]
- },
- {
- "name": "feeRecipientToken",
- "isMut": true,
- "isSigner": false,
- "docs": [
- "Destination token account, which the redeemer may not own. But because the redeemer is a",
- "signer and is the one encoded in the Deposit Fill message, he may have the tokens be sent",
- "to any account he chooses (this one).",
- ""
- ]
- },
- {
- "name": "fromRouterEndpoint",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "Seeds must be \\[\"endpoint\", chain.to_be_bytes()\\]."
- ]
- },
- {
- "name": "toRouterEndpoint",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "Seeds must be \\[\"endpoint\", chain.to_be_bytes()\\]."
- ]
- },
- {
- "name": "mint",
- "isMut": true,
- "isSigner": false,
- "docs": [
- "Circle-supported mint.",
- "",
- "Token Messenger Minter program's local token account."
- ]
- },
- {
- "name": "coreBridgeConfig",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "coreMessage",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "cctpMessage",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "coreEmitterSequence",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "coreFeeCollector",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "tokenMessengerMinterSenderAuthority",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "messageTransmitterConfig",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "tokenMessenger",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "remoteTokenMessenger",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "Messenger Minter program)."
- ]
+ "isOptional": true
},
{
- "name": "tokenMinter",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "CHECK Seeds must be \\[\"token_minter\"\\] (CCTP Token Messenger Minter program)."
+ "name": "usdc",
+ "accounts": [
+ {
+ "name": "mint",
+ "isMut": false,
+ "isSigner": false
+ }
]
},
{
- "name": "localToken",
- "isMut": true,
- "isSigner": false,
- "docs": [
- "Local token account, which this program uses to validate the `mint` used to burn.",
- ""
+ "name": "cctp",
+ "accounts": [
+ {
+ "name": "mintRecipient",
+ "accounts": [
+ {
+ "name": "mintRecipient",
+ "isMut": true,
+ "isSigner": false
+ }
+ ]
+ },
+ {
+ "name": "messageTransmitterAuthority",
+ "isMut": false,
+ "isSigner": false
+ },
+ {
+ "name": "messageTransmitterConfig",
+ "isMut": false,
+ "isSigner": false
+ },
+ {
+ "name": "usedNonces",
+ "isMut": true,
+ "isSigner": false,
+ "docs": [
+ "first_nonce.to_string()\\] (CCTP Message Transmitter program)."
+ ]
+ },
+ {
+ "name": "messageTransmitterEventAuthority",
+ "isMut": false,
+ "isSigner": false
+ },
+ {
+ "name": "tokenMessenger",
+ "isMut": false,
+ "isSigner": false
+ },
+ {
+ "name": "remoteTokenMessenger",
+ "isMut": false,
+ "isSigner": false,
+ "docs": [
+ "Messenger Minter program)."
+ ]
+ },
+ {
+ "name": "tokenMinter",
+ "isMut": false,
+ "isSigner": false
+ },
+ {
+ "name": "localToken",
+ "isMut": true,
+ "isSigner": false,
+ "docs": [
+ "Token Messenger Minter's Local Token account. This program uses the mint of this account to",
+ "validate the `mint_recipient` token account's mint.",
+ ""
+ ]
+ },
+ {
+ "name": "tokenPair",
+ "isMut": false,
+ "isSigner": false,
+ "docs": [
+ "Token Messenger Minter program)."
+ ]
+ },
+ {
+ "name": "tokenMessengerMinterCustodyToken",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "tokenMessengerMinterEventAuthority",
+ "isMut": false,
+ "isSigner": false
+ },
+ {
+ "name": "tokenMessengerMinterProgram",
+ "isMut": false,
+ "isSigner": false
+ },
+ {
+ "name": "messageTransmitterProgram",
+ "isMut": false,
+ "isSigner": false
+ }
]
},
- {
- "name": "tokenMessengerMinterEventAuthority",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "coreBridgeProgram",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "tokenMessengerMinterProgram",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "messageTransmitterProgram",
- "isMut": false,
- "isSigner": false
- },
{
"name": "tokenProgram",
"isMut": false,
@@ -3261,78 +3039,37 @@ export const IDL: MatchingEngine = {
"name": "systemProgram",
"isMut": false,
"isSigner": false
- },
- {
- "name": "clock",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "rent",
- "isMut": false,
- "isSigner": false
}
],
- "args": []
+ "args": [
+ {
+ "name": "args",
+ "type": {
+ "defined": "CctpMessageArgs"
+ }
+ }
+ ]
},
{
- "name": "settleAuctionNoneLocal",
+ "name": "settleAuctionComplete",
"accounts": [
{
- "name": "payer",
- "isMut": true,
- "isSigner": true
- },
- {
- "name": "payerSequence",
+ "name": "executor",
"isMut": true,
- "isSigner": false
- },
- {
- "name": "custodian",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "This program's Wormhole (Core Bridge) emitter authority.",
- ""
- ]
- },
- {
- "name": "fastVaa",
- "isMut": false,
"isSigner": false,
"docs": [
- "zero-copy using the [VaaAccount](core_bridge_program::sdk::VaaAccount) reader."
+ "we will always reward the owner of the executor token account with the lamports from the",
+ "prepared order response and its custody token account when we close these accounts. This",
+ "means we disregard the `prepared_by` field in the prepared order response."
]
},
{
- "name": "preparedOrderResponse",
+ "name": "executorToken",
"isMut": true,
"isSigner": false
},
{
- "name": "auction",
- "isMut": true,
- "isSigner": false,
- "docs": [
- "There should be no account data here because an auction was never created."
- ]
- },
- {
- "name": "cctpMintRecipient",
- "isMut": true,
- "isSigner": false,
- "docs": [
- "Mint recipient token account, which is encoded as the mint recipient in the CCTP message.",
- "The CCTP Token Messenger Minter program will transfer the amount encoded in the CCTP message",
- "from its custody account to this account.",
- "",
- "",
- "NOTE: This account must be encoded as the mint recipient in the CCTP message."
- ]
- },
- {
- "name": "feeRecipientToken",
+ "name": "bestOfferToken",
"isMut": true,
"isSigner": false,
"docs": [
@@ -3343,71 +3080,30 @@ export const IDL: MatchingEngine = {
]
},
{
- "name": "fromRouterEndpoint",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "Seeds must be \\[\"endpoint\", chain.to_be_bytes()\\]."
- ]
- },
- {
- "name": "toRouterEndpoint",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "Seeds must be \\[\"endpoint\", chain.to_be_bytes()\\]."
- ]
- },
- {
- "name": "coreBridgeConfig",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "coreMessage",
+ "name": "preparedOrderResponse",
"isMut": true,
"isSigner": false
},
{
- "name": "coreEmitterSequence",
+ "name": "preparedCustodyToken",
"isMut": true,
"isSigner": false
},
{
- "name": "coreFeeCollector",
+ "name": "auction",
"isMut": true,
"isSigner": false
},
- {
- "name": "coreBridgeProgram",
- "isMut": false,
- "isSigner": false
- },
{
"name": "tokenProgram",
"isMut": false,
"isSigner": false
- },
- {
- "name": "systemProgram",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "clock",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "rent",
- "isMut": false,
- "isSigner": false
}
],
"args": []
},
{
- "name": "settleAuctionActiveCctp",
+ "name": "settleAuctionNoneCctp",
"accounts": [
{
"name": "payer",
@@ -3420,166 +3116,204 @@ export const IDL: MatchingEngine = {
"isSigner": false
},
{
- "name": "custodian",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "This program's Wormhole (Core Bridge) emitter authority.",
- ""
- ]
- },
- {
- "name": "auctionConfig",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "fastVaa",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "zero-copy using the [VaaAccount](core_bridge_program::sdk::VaaAccount) reader."
- ]
- },
- {
- "name": "preparedOrderResponse",
+ "name": "coreMessage",
"isMut": true,
"isSigner": false
},
{
- "name": "auction",
- "isMut": true,
- "isSigner": false,
- "docs": [
- "There should be no account data here because an auction was never created."
- ]
- },
- {
- "name": "bestOfferToken",
+ "name": "cctpMessage",
"isMut": true,
"isSigner": false
},
{
- "name": "executorToken",
- "isMut": true,
- "isSigner": false
+ "name": "custodian",
+ "accounts": [
+ {
+ "name": "custodian",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
},
{
- "name": "cctpMintRecipient",
+ "name": "feeRecipientToken",
"isMut": true,
"isSigner": false,
"docs": [
- "Mint recipient token account, which is encoded as the mint recipient in the CCTP message.",
- "The CCTP Token Messenger Minter program will transfer the amount encoded in the CCTP message",
- "from its custody account to this account.",
- "",
- "",
- "NOTE: This account must be encoded as the mint recipient in the CCTP message."
+ "Destination token account, which the redeemer may not own. But because the redeemer is a",
+ "signer and is the one encoded in the Deposit Fill message, he may have the tokens be sent",
+ "to any account he chooses (this one).",
+ ""
]
},
{
- "name": "toRouterEndpoint",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "Seeds must be \\[\"endpoint\", chain.to_be_bytes()\\]."
+ "name": "prepared",
+ "accounts": [
+ {
+ "name": "by",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "orderResponse",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "custodyToken",
+ "isMut": true,
+ "isSigner": false
+ }
]
},
{
- "name": "mint",
- "isMut": true,
- "isSigner": false,
- "docs": [
- "Circle-supported mint.",
- "",
- "Token Messenger Minter program's local token account."
+ "name": "fastOrderPath",
+ "accounts": [
+ {
+ "name": "fastVaa",
+ "accounts": [
+ {
+ "name": "vaa",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
+ },
+ {
+ "name": "from",
+ "accounts": [
+ {
+ "name": "endpoint",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
+ },
+ {
+ "name": "to",
+ "accounts": [
+ {
+ "name": "endpoint",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
+ }
]
},
{
- "name": "coreBridgeConfig",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "coreMessage",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "cctpMessage",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "coreEmitterSequence",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "coreFeeCollector",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "tokenMessengerMinterSenderAuthority",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "messageTransmitterConfig",
+ "name": "auction",
"isMut": true,
- "isSigner": false
- },
- {
- "name": "tokenMessenger",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "remoteTokenMessenger",
- "isMut": false,
"isSigner": false,
"docs": [
- "Messenger Minter program)."
+ "There should be no account data here because an auction was never created."
]
},
{
- "name": "tokenMinter",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "CHECK Seeds must be \\[\"token_minter\"\\] (CCTP Token Messenger Minter program)."
+ "name": "wormhole",
+ "accounts": [
+ {
+ "name": "config",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "emitterSequence",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "feeCollector",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "coreBridgeProgram",
+ "isMut": false,
+ "isSigner": false
+ }
]
},
{
- "name": "localToken",
- "isMut": true,
- "isSigner": false,
- "docs": [
- "Local token account, which this program uses to validate the `mint` used to burn.",
- ""
+ "name": "cctp",
+ "accounts": [
+ {
+ "name": "burnSource",
+ "accounts": [
+ {
+ "name": "mintRecipient",
+ "isMut": true,
+ "isSigner": false
+ }
+ ]
+ },
+ {
+ "name": "mint",
+ "isMut": true,
+ "isSigner": false,
+ "docs": [
+ "Circle-supported mint.",
+ "",
+ "Token Messenger Minter program's local token account."
+ ]
+ },
+ {
+ "name": "tokenMessengerMinterSenderAuthority",
+ "isMut": false,
+ "isSigner": false
+ },
+ {
+ "name": "messageTransmitterConfig",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "tokenMessenger",
+ "isMut": false,
+ "isSigner": false
+ },
+ {
+ "name": "remoteTokenMessenger",
+ "isMut": false,
+ "isSigner": false,
+ "docs": [
+ "Messenger Minter program)."
+ ]
+ },
+ {
+ "name": "tokenMinter",
+ "isMut": false,
+ "isSigner": false,
+ "docs": [
+ "CHECK Seeds must be \\[\"token_minter\"\\] (CCTP Token Messenger Minter program)."
+ ]
+ },
+ {
+ "name": "localToken",
+ "isMut": true,
+ "isSigner": false,
+ "docs": [
+ "Local token account, which this program uses to validate the `mint` used to burn.",
+ ""
+ ]
+ },
+ {
+ "name": "tokenMessengerMinterEventAuthority",
+ "isMut": false,
+ "isSigner": false
+ },
+ {
+ "name": "tokenMessengerMinterProgram",
+ "isMut": false,
+ "isSigner": false
+ },
+ {
+ "name": "messageTransmitterProgram",
+ "isMut": false,
+ "isSigner": false
+ }
]
},
- {
- "name": "tokenMessengerMinterEventAuthority",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "coreBridgeProgram",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "tokenMessengerMinterProgram",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "messageTransmitterProgram",
- "isMut": false,
- "isSigner": false
- },
{
"name": "tokenProgram",
"isMut": false,
@@ -3591,20 +3325,33 @@ export const IDL: MatchingEngine = {
"isSigner": false
},
{
- "name": "clock",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "rent",
- "isMut": false,
- "isSigner": false
+ "name": "sysvars",
+ "accounts": [
+ {
+ "name": "clock",
+ "isMut": false,
+ "isSigner": false,
+ "docs": [
+ "Wormhole Core Bridge needs the clock sysvar based on its legacy implementation.",
+ ""
+ ]
+ },
+ {
+ "name": "rent",
+ "isMut": false,
+ "isSigner": false,
+ "docs": [
+ "Wormhole Core Bridge needs the rent sysvar based on its legacy implementation.",
+ ""
+ ]
+ }
+ ]
}
],
"args": []
},
{
- "name": "settleAuctionActiveLocal",
+ "name": "settleAuctionNoneLocal",
"accounts": [
{
"name": "payer",
@@ -3617,96 +3364,124 @@ export const IDL: MatchingEngine = {
"isSigner": false
},
{
- "name": "custodian",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "This program's Wormhole (Core Bridge) emitter authority.",
- ""
- ]
- },
- {
- "name": "auctionConfig",
- "isMut": false,
+ "name": "coreMessage",
+ "isMut": true,
"isSigner": false
},
{
- "name": "fastVaa",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "zero-copy using the [VaaAccount](core_bridge_program::sdk::VaaAccount) reader."
+ "name": "custodian",
+ "accounts": [
+ {
+ "name": "custodian",
+ "isMut": false,
+ "isSigner": false
+ }
]
},
{
- "name": "preparedOrderResponse",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "auction",
+ "name": "feeRecipientToken",
"isMut": true,
"isSigner": false,
"docs": [
- "There should be no account data here because an auction was never created."
+ "Destination token account, which the redeemer may not own. But because the redeemer is a",
+ "signer and is the one encoded in the Deposit Fill message, he may have the tokens be sent",
+ "to any account he chooses (this one).",
+ ""
]
},
{
- "name": "toRouterEndpoint",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "Seeds must be \\[\"endpoint\", chain.to_be_bytes()\\]."
+ "name": "prepared",
+ "accounts": [
+ {
+ "name": "by",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "orderResponse",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "custodyToken",
+ "isMut": true,
+ "isSigner": false
+ }
]
},
{
- "name": "bestOfferToken",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "executorToken",
- "isMut": true,
- "isSigner": false
+ "name": "fastOrderPath",
+ "accounts": [
+ {
+ "name": "fastVaa",
+ "accounts": [
+ {
+ "name": "vaa",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
+ },
+ {
+ "name": "from",
+ "accounts": [
+ {
+ "name": "endpoint",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
+ },
+ {
+ "name": "to",
+ "accounts": [
+ {
+ "name": "endpoint",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
+ }
+ ]
},
{
- "name": "cctpMintRecipient",
+ "name": "auction",
"isMut": true,
"isSigner": false,
"docs": [
- "Mint recipient token account, which is encoded as the mint recipient in the CCTP message.",
- "The CCTP Token Messenger Minter program will transfer the amount encoded in the CCTP message",
- "from its custody account to this account.",
- "",
- "",
- "NOTE: This account must be encoded as the mint recipient in the CCTP message."
+ "There should be no account data here because an auction was never created."
]
},
{
- "name": "coreBridgeConfig",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "coreMessage",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "coreEmitterSequence",
- "isMut": true,
- "isSigner": false
+ "name": "wormhole",
+ "accounts": [
+ {
+ "name": "config",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "emitterSequence",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "feeCollector",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "coreBridgeProgram",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
},
{
- "name": "coreFeeCollector",
+ "name": "localCustodyToken",
"isMut": true,
"isSigner": false
},
- {
- "name": "coreBridgeProgram",
- "isMut": false,
- "isSigner": false
- },
{
"name": "tokenProgram",
"isMut": false,
@@ -3718,14 +3493,27 @@ export const IDL: MatchingEngine = {
"isSigner": false
},
{
- "name": "clock",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "rent",
- "isMut": false,
- "isSigner": false
+ "name": "sysvars",
+ "accounts": [
+ {
+ "name": "clock",
+ "isMut": false,
+ "isSigner": false,
+ "docs": [
+ "Wormhole Core Bridge needs the clock sysvar based on its legacy implementation.",
+ ""
+ ]
+ },
+ {
+ "name": "rent",
+ "isMut": false,
+ "isSigner": false,
+ "docs": [
+ "Wormhole Core Bridge needs the rent sysvar based on its legacy implementation.",
+ ""
+ ]
+ }
+ ]
}
],
"args": []
@@ -3784,9 +3572,14 @@ export const IDL: MatchingEngine = {
"isSigner": false
},
{
- "name": "mint",
- "isMut": false,
- "isSigner": false
+ "name": "usdc",
+ "accounts": [
+ {
+ "name": "mint",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
},
{
"name": "programData",
@@ -3855,8 +3648,13 @@ export const IDL: MatchingEngine = {
},
{
"name": "custodian",
- "isMut": false,
- "isSigner": false
+ "accounts": [
+ {
+ "name": "custodian",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
}
]
},
@@ -3865,6 +3663,33 @@ export const IDL: MatchingEngine = {
"isMut": true,
"isSigner": false
},
+ {
+ "name": "localRouterEndpoint",
+ "isMut": false,
+ "isSigner": false,
+ "docs": [
+ "Local router endpoint PDA.",
+ "",
+ "NOTE: This account may not exist yet. But we need to pass it since it will be the owner of",
+ "the local custody token account.",
+ ""
+ ]
+ },
+ {
+ "name": "localCustodyToken",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "usdc",
+ "accounts": [
+ {
+ "name": "mint",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
+ },
{
"name": "remoteTokenMessenger",
"isMut": false,
@@ -3873,6 +3698,11 @@ export const IDL: MatchingEngine = {
"Messenger Minter program)."
]
},
+ {
+ "name": "tokenProgram",
+ "isMut": false,
+ "isSigner": false
+ },
{
"name": "systemProgram",
"isMut": false,
@@ -3906,8 +3736,13 @@ export const IDL: MatchingEngine = {
},
{
"name": "custodian",
- "isMut": false,
- "isSigner": false
+ "accounts": [
+ {
+ "name": "custodian",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
}
]
},
@@ -3960,8 +3795,13 @@ export const IDL: MatchingEngine = {
},
{
"name": "custodian",
- "isMut": false,
- "isSigner": false
+ "accounts": [
+ {
+ "name": "custodian",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
}
]
},
@@ -3969,7 +3809,7 @@ export const IDL: MatchingEngine = {
"name": "routerEndpoint",
"accounts": [
{
- "name": "inner",
+ "name": "endpoint",
"isMut": true,
"isSigner": false
}
@@ -3991,8 +3831,13 @@ export const IDL: MatchingEngine = {
},
{
"name": "custodian",
- "isMut": false,
- "isSigner": false
+ "accounts": [
+ {
+ "name": "custodian",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
}
]
},
@@ -4000,7 +3845,7 @@ export const IDL: MatchingEngine = {
"name": "routerEndpoint",
"accounts": [
{
- "name": "inner",
+ "name": "endpoint",
"isMut": true,
"isSigner": false
}
@@ -4037,8 +3882,13 @@ export const IDL: MatchingEngine = {
},
{
"name": "custodian",
- "isMut": false,
- "isSigner": false
+ "accounts": [
+ {
+ "name": "custodian",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
}
]
},
@@ -4046,7 +3896,7 @@ export const IDL: MatchingEngine = {
"name": "routerEndpoint",
"accounts": [
{
- "name": "inner",
+ "name": "endpoint",
"isMut": true,
"isSigner": false
}
@@ -4091,8 +3941,13 @@ export const IDL: MatchingEngine = {
},
{
"name": "custodian",
- "isMut": true,
- "isSigner": false
+ "accounts": [
+ {
+ "name": "custodian",
+ "isMut": true,
+ "isSigner": false
+ }
+ ]
}
]
},
@@ -4141,8 +3996,13 @@ export const IDL: MatchingEngine = {
},
{
"name": "custodian",
- "isMut": true,
- "isSigner": false
+ "accounts": [
+ {
+ "name": "custodian",
+ "isMut": true,
+ "isSigner": false
+ }
+ ]
}
]
}
@@ -4167,8 +4027,13 @@ export const IDL: MatchingEngine = {
},
{
"name": "custodian",
- "isMut": true,
- "isSigner": false
+ "accounts": [
+ {
+ "name": "custodian",
+ "isMut": true,
+ "isSigner": false
+ }
+ ]
}
]
},
@@ -4215,8 +4080,13 @@ export const IDL: MatchingEngine = {
},
{
"name": "custodian",
- "isMut": true,
- "isSigner": false
+ "accounts": [
+ {
+ "name": "custodian",
+ "isMut": true,
+ "isSigner": false
+ }
+ ]
}
]
},
@@ -4242,18 +4112,25 @@ export const IDL: MatchingEngine = {
"name": "updateOwnerAssistant",
"accounts": [
{
- "name": "owner",
- "isMut": false,
- "isSigner": true,
- "docs": [
- "Owner of the program set in the [`OwnerConfig`] account."
+ "name": "admin",
+ "accounts": [
+ {
+ "name": "owner",
+ "isMut": false,
+ "isSigner": true
+ },
+ {
+ "name": "custodian",
+ "accounts": [
+ {
+ "name": "custodian",
+ "isMut": true,
+ "isSigner": false
+ }
+ ]
+ }
]
},
- {
- "name": "custodian",
- "isMut": true,
- "isSigner": false
- },
{
"name": "newOwnerAssistant",
"isMut": false,
@@ -4270,14 +4147,24 @@ export const IDL: MatchingEngine = {
"name": "updateFeeRecipient",
"accounts": [
{
- "name": "ownerOrAssistant",
- "isMut": true,
- "isSigner": true
- },
- {
- "name": "custodian",
- "isMut": true,
- "isSigner": false
+ "name": "admin",
+ "accounts": [
+ {
+ "name": "ownerOrAssistant",
+ "isMut": false,
+ "isSigner": true
+ },
+ {
+ "name": "custodian",
+ "accounts": [
+ {
+ "name": "custodian",
+ "isMut": true,
+ "isSigner": false
+ }
+ ]
+ }
+ ]
},
{
"name": "newFeeRecipientToken",
@@ -4306,11 +4193,12 @@ export const IDL: MatchingEngine = {
},
{
"name": "custodian",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "This program's Wormhole (Core Bridge) emitter authority.",
- ""
+ "accounts": [
+ {
+ "name": "custodian",
+ "isMut": false,
+ "isSigner": false
+ }
]
},
{
@@ -4319,9 +4207,39 @@ export const IDL: MatchingEngine = {
"isSigner": false
},
{
- "name": "fastVaa",
- "isMut": false,
- "isSigner": false
+ "name": "fastOrderPath",
+ "accounts": [
+ {
+ "name": "fastVaa",
+ "accounts": [
+ {
+ "name": "vaa",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
+ },
+ {
+ "name": "from",
+ "accounts": [
+ {
+ "name": "endpoint",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
+ },
+ {
+ "name": "to",
+ "accounts": [
+ {
+ "name": "endpoint",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
+ }
+ ]
},
{
"name": "auction",
@@ -4333,24 +4251,27 @@ export const IDL: MatchingEngine = {
]
},
{
- "name": "fromRouterEndpoint",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "toRouterEndpoint",
+ "name": "offerToken",
"isMut": false,
- "isSigner": false
+ "isSigner": false,
+ "docs": [
+ "the auction PDA."
+ ]
},
{
- "name": "offerToken",
+ "name": "auctionCustodyToken",
"isMut": true,
"isSigner": false
},
{
- "name": "cctpMintRecipient",
- "isMut": true,
- "isSigner": false
+ "name": "usdc",
+ "accounts": [
+ {
+ "name": "mint",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
},
{
"name": "systemProgram",
@@ -4374,39 +4295,37 @@ export const IDL: MatchingEngine = {
"name": "improveOffer",
"accounts": [
{
- "name": "custodian",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "This program's Wormhole (Core Bridge) emitter authority. This is also the burn-source",
- "authority for CCTP transfers.",
- ""
+ "name": "activeAuction",
+ "accounts": [
+ {
+ "name": "auction",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "custodyToken",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "config",
+ "isMut": false,
+ "isSigner": false
+ },
+ {
+ "name": "bestOfferToken",
+ "isMut": true,
+ "isSigner": false
+ }
]
},
- {
- "name": "auctionConfig",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "offerAuthority",
- "isMut": false,
- "isSigner": true
- },
- {
- "name": "auction",
- "isMut": true,
- "isSigner": false
- },
{
"name": "offerToken",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "bestOfferToken",
- "isMut": true,
- "isSigner": false
+ "isMut": false,
+ "isSigner": false,
+ "docs": [
+ "the auction PDA."
+ ]
},
{
"name": "tokenProgram",
@@ -4434,75 +4353,6 @@ export const IDL: MatchingEngine = {
"isMut": true,
"isSigner": false
},
- {
- "name": "custodian",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "This program's Wormhole (Core Bridge) emitter authority. This is also the burn-source",
- "authority for CCTP transfers.",
- ""
- ]
- },
- {
- "name": "auctionConfig",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "fastVaa",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "auction",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "toRouterEndpoint",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "executorToken",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "bestOfferToken",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "initialOfferToken",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "cctpMintRecipient",
- "isMut": true,
- "isSigner": false,
- "docs": [
- "Also the burn_source token account.",
- ""
- ]
- },
- {
- "name": "mint",
- "isMut": true,
- "isSigner": false,
- "docs": [
- "Circle-supported mint.",
- "",
- "Token Messenger Minter program's local token account."
- ]
- },
- {
- "name": "coreBridgeConfig",
- "isMut": true,
- "isSigner": false
- },
{
"name": "coreMessage",
"isMut": true,
@@ -4514,75 +4364,180 @@ export const IDL: MatchingEngine = {
"isSigner": false
},
{
- "name": "coreEmitterSequence",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "coreFeeCollector",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "tokenMessengerMinterSenderAuthority",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "messageTransmitterConfig",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "tokenMessenger",
- "isMut": false,
- "isSigner": false
+ "name": "custodian",
+ "accounts": [
+ {
+ "name": "custodian",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
},
{
- "name": "remoteTokenMessenger",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "Messenger Minter program)."
+ "name": "executeOrder",
+ "accounts": [
+ {
+ "name": "fastVaa",
+ "accounts": [
+ {
+ "name": "vaa",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
+ },
+ {
+ "name": "activeAuction",
+ "accounts": [
+ {
+ "name": "auction",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "custodyToken",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "config",
+ "isMut": false,
+ "isSigner": false
+ },
+ {
+ "name": "bestOfferToken",
+ "isMut": true,
+ "isSigner": false
+ }
+ ]
+ },
+ {
+ "name": "toRouterEndpoint",
+ "accounts": [
+ {
+ "name": "endpoint",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
+ },
+ {
+ "name": "executorToken",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "initialOfferToken",
+ "isMut": true,
+ "isSigner": false
+ }
]
},
{
- "name": "tokenMinter",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "CHECK Seeds must be \\[\"token_minter\"\\] (CCTP Token Messenger Minter program)."
+ "name": "wormhole",
+ "accounts": [
+ {
+ "name": "config",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "emitterSequence",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "feeCollector",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "coreBridgeProgram",
+ "isMut": false,
+ "isSigner": false
+ }
]
},
{
- "name": "localToken",
- "isMut": true,
- "isSigner": false,
- "docs": [
- "Local token account, which this program uses to validate the `mint` used to burn.",
- ""
+ "name": "cctp",
+ "accounts": [
+ {
+ "name": "burnSource",
+ "accounts": [
+ {
+ "name": "mintRecipient",
+ "isMut": true,
+ "isSigner": false
+ }
+ ]
+ },
+ {
+ "name": "mint",
+ "isMut": true,
+ "isSigner": false,
+ "docs": [
+ "Circle-supported mint.",
+ "",
+ "Token Messenger Minter program's local token account."
+ ]
+ },
+ {
+ "name": "tokenMessengerMinterSenderAuthority",
+ "isMut": false,
+ "isSigner": false
+ },
+ {
+ "name": "messageTransmitterConfig",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "tokenMessenger",
+ "isMut": false,
+ "isSigner": false
+ },
+ {
+ "name": "remoteTokenMessenger",
+ "isMut": false,
+ "isSigner": false,
+ "docs": [
+ "Messenger Minter program)."
+ ]
+ },
+ {
+ "name": "tokenMinter",
+ "isMut": false,
+ "isSigner": false,
+ "docs": [
+ "CHECK Seeds must be \\[\"token_minter\"\\] (CCTP Token Messenger Minter program)."
+ ]
+ },
+ {
+ "name": "localToken",
+ "isMut": true,
+ "isSigner": false,
+ "docs": [
+ "Local token account, which this program uses to validate the `mint` used to burn.",
+ ""
+ ]
+ },
+ {
+ "name": "tokenMessengerMinterEventAuthority",
+ "isMut": false,
+ "isSigner": false
+ },
+ {
+ "name": "tokenMessengerMinterProgram",
+ "isMut": false,
+ "isSigner": false
+ },
+ {
+ "name": "messageTransmitterProgram",
+ "isMut": false,
+ "isSigner": false
+ }
]
},
- {
- "name": "tokenMessengerMinterEventAuthority",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "coreBridgeProgram",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "tokenMessengerMinterProgram",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "messageTransmitterProgram",
- "isMut": false,
- "isSigner": false
- },
{
"name": "systemProgram",
"isMut": false,
@@ -4594,14 +4549,27 @@ export const IDL: MatchingEngine = {
"isSigner": false
},
{
- "name": "clock",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "rent",
- "isMut": false,
- "isSigner": false
+ "name": "sysvars",
+ "accounts": [
+ {
+ "name": "clock",
+ "isMut": false,
+ "isSigner": false,
+ "docs": [
+ "Wormhole Core Bridge needs the clock sysvar based on its legacy implementation.",
+ ""
+ ]
+ },
+ {
+ "name": "rent",
+ "isMut": false,
+ "isSigner": false,
+ "docs": [
+ "Wormhole Core Bridge needs the rent sysvar based on its legacy implementation.",
+ ""
+ ]
+ }
+ ]
}
],
"args": []
@@ -4620,84 +4588,110 @@ export const IDL: MatchingEngine = {
"isSigner": false
},
{
- "name": "custodian",
- "isMut": false,
- "isSigner": false,
- "docs": [
- "This program's Wormhole (Core Bridge) emitter authority. This is also the burn-source",
- "authority for CCTP transfers.",
- ""
- ]
- },
- {
- "name": "auctionConfig",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "fastVaa",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "auction",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "toRouterEndpoint",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "executorToken",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "bestOfferToken",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "initialOfferToken",
+ "name": "coreMessage",
"isMut": true,
"isSigner": false
},
{
- "name": "cctpMintRecipient",
- "isMut": true,
- "isSigner": false,
- "docs": [
- "Also the burn_source token account.",
- ""
+ "name": "custodian",
+ "accounts": [
+ {
+ "name": "custodian",
+ "isMut": false,
+ "isSigner": false
+ }
]
},
{
- "name": "coreBridgeConfig",
- "isMut": true,
- "isSigner": false
- },
- {
- "name": "coreMessage",
- "isMut": true,
- "isSigner": false
+ "name": "executeOrder",
+ "accounts": [
+ {
+ "name": "fastVaa",
+ "accounts": [
+ {
+ "name": "vaa",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
+ },
+ {
+ "name": "activeAuction",
+ "accounts": [
+ {
+ "name": "auction",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "custodyToken",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "config",
+ "isMut": false,
+ "isSigner": false
+ },
+ {
+ "name": "bestOfferToken",
+ "isMut": true,
+ "isSigner": false
+ }
+ ]
+ },
+ {
+ "name": "toRouterEndpoint",
+ "accounts": [
+ {
+ "name": "endpoint",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
+ },
+ {
+ "name": "executorToken",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "initialOfferToken",
+ "isMut": true,
+ "isSigner": false
+ }
+ ]
},
{
- "name": "coreEmitterSequence",
- "isMut": true,
- "isSigner": false
+ "name": "wormhole",
+ "accounts": [
+ {
+ "name": "config",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "emitterSequence",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "feeCollector",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "coreBridgeProgram",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
},
{
- "name": "coreFeeCollector",
+ "name": "localCustodyToken",
"isMut": true,
"isSigner": false
},
- {
- "name": "coreBridgeProgram",
- "isMut": false,
- "isSigner": false
- },
{
"name": "systemProgram",
"isMut": false,
@@ -4709,14 +4703,27 @@ export const IDL: MatchingEngine = {
"isSigner": false
},
{
- "name": "clock",
- "isMut": false,
- "isSigner": false
- },
- {
- "name": "rent",
- "isMut": false,
- "isSigner": false
+ "name": "sysvars",
+ "accounts": [
+ {
+ "name": "clock",
+ "isMut": false,
+ "isSigner": false,
+ "docs": [
+ "Wormhole Core Bridge needs the clock sysvar based on its legacy implementation.",
+ ""
+ ]
+ },
+ {
+ "name": "rent",
+ "isMut": false,
+ "isSigner": false,
+ "docs": [
+ "Wormhole Core Bridge needs the rent sysvar based on its legacy implementation.",
+ ""
+ ]
+ }
+ ]
}
],
"args": []
@@ -4734,8 +4741,13 @@ export const IDL: MatchingEngine = {
},
{
"name": "custodian",
- "isMut": false,
- "isSigner": false
+ "accounts": [
+ {
+ "name": "custodian",
+ "isMut": false,
+ "isSigner": false
+ }
+ ]
}
]
},
@@ -5085,6 +5097,10 @@ export const IDL: MatchingEngine = {
"name": "configId",
"type": "u32"
},
+ {
+ "name": "custodyTokenBump",
+ "type": "u8"
+ },
{
"name": "vaaSequence",
"docs": [
@@ -5149,6 +5165,10 @@ export const IDL: MatchingEngine = {
"the [amount_in](Self::amount_in)."
],
"type": "u64"
+ },
+ {
+ "name": "endEarly",
+ "type": "bool"
}
]
}
@@ -5206,6 +5226,12 @@ export const IDL: MatchingEngine = {
{
"name": "slot",
"type": "u64"
+ },
+ {
+ "name": "executePenalty",
+ "type": {
+ "option": "u64"
+ }
}
]
},
@@ -5217,7 +5243,7 @@ export const IDL: MatchingEngine = {
"type": "u64"
},
{
- "name": "penalty",
+ "name": "totalPenalty",
"type": {
"option": "u64"
}
@@ -5297,11 +5323,6 @@ export const IDL: MatchingEngine = {
"name": "OwnerOrAssistantOnly",
"msg": "OwnerOrAssistantOnly"
},
- {
- "code": 6006,
- "name": "InvalidCustodyToken",
- "msg": "InvalidCustodyToken"
- },
{
"code": 6008,
"name": "CpiDisallowed",
@@ -5337,11 +5358,6 @@ export const IDL: MatchingEngine = {
"name": "ImmutableProgram",
"msg": "ImmutableProgram"
},
- {
- "code": 6259,
- "name": "NotUsdc",
- "msg": "NotUsdc"
- },
{
"code": 6514,
"name": "InvalidNewOwner",
@@ -5524,8 +5540,8 @@ export const IDL: MatchingEngine = {
},
{
"code": 6556,
- "name": "BestOfferTokenMismatch",
- "msg": "BestOfferTokenMismatch"
+ "name": "ExecutorTokenMismatch",
+ "msg": "ExecutorTokenMismatch"
},
{
"code": 6557,
diff --git a/solana/target/types/token_router.ts b/solana/target/types/token_router.ts
index bee01f9de..53d2bb331 100644
--- a/solana/target/types/token_router.ts
+++ b/solana/target/types/token_router.ts
@@ -547,9 +547,12 @@ export type TokenRouter = {
"isSigner": false
},
{
- "name": "matchingEngineCctpMintRecipient",
+ "name": "matchingEngineLocalCustodyToken",
"isMut": true,
- "isSigner": false
+ "isSigner": false,
+ "docs": [
+ "(Matching Engine program)."
+ ]
},
{
"name": "matchingEngineProgram",
@@ -1917,9 +1920,12 @@ export const IDL: TokenRouter = {
"isSigner": false
},
{
- "name": "matchingEngineCctpMintRecipient",
+ "name": "matchingEngineLocalCustodyToken",
"isMut": true,
- "isSigner": false
+ "isSigner": false,
+ "docs": [
+ "(Matching Engine program)."
+ ]
},
{
"name": "matchingEngineProgram",
diff --git a/solana/ts/src/matchingEngine/index.ts b/solana/ts/src/matchingEngine/index.ts
index 8f9703a2c..658cfff2b 100644
--- a/solana/ts/src/matchingEngine/index.ts
+++ b/solana/ts/src/matchingEngine/index.ts
@@ -107,7 +107,7 @@ export type RedeemFastFillAccounts = {
custodian: PublicKey;
redeemedFastFill: PublicKey;
routerEndpoint: PublicKey;
- cctpMintRecipient: PublicKey;
+ localCustodyToken: PublicKey;
matchingEngineProgram: PublicKey;
};
@@ -183,6 +183,7 @@ export class MatchingEngineProgram {
async fetchAuction(input: VaaHash | { address: PublicKey }): Promise {
const addr = "address" in input ? input.address : this.auctionAddress(input);
+ // @ts-ignore This is BS. This is correct.
return this.program.account.auction.fetch(addr);
}
@@ -243,18 +244,58 @@ export class MatchingEngineProgram {
return this.program.account.redeemedFastFill.fetch(addr);
}
- preparedOrderResponseAddress(preparedBy: PublicKey, fastVaaHash: VaaHash): PublicKey {
- return PreparedOrderResponse.address(this.ID, preparedBy, fastVaaHash);
+ preparedOrderResponseAddress(fastVaaHash: VaaHash): PublicKey {
+ return PreparedOrderResponse.address(this.ID, fastVaaHash);
}
- fetchPreparedOrderResponse(
- input: [PublicKey, VaaHash] | { address: PublicKey },
+ async fetchPreparedOrderResponse(
+ input: VaaHash | { address: PublicKey },
): Promise {
- const addr =
- "address" in input ? input.address : this.preparedOrderResponseAddress(...input);
+ const addr = "address" in input ? input.address : this.preparedOrderResponseAddress(input);
return this.program.account.preparedOrderResponse.fetch(addr);
}
+ preparedCustodyTokenAddress(preparedOrderResponse: PublicKey): PublicKey {
+ return PublicKey.findProgramAddressSync(
+ [Buffer.from("prepared-custody"), preparedOrderResponse.toBuffer()],
+ this.ID,
+ )[0];
+ }
+
+ auctionCustodyTokenAddress(auction: PublicKey): PublicKey {
+ return PublicKey.findProgramAddressSync(
+ [Buffer.from("auction-custody"), auction.toBuffer()],
+ this.ID,
+ )[0];
+ }
+
+ async fetchAuctionCustodyTokenBalance(auction: PublicKey): Promise {
+ return splToken
+ .getAccount(this.program.provider.connection, this.auctionCustodyTokenAddress(auction))
+ .then((token) => token.amount)
+ .catch((_) => 0n);
+ }
+
+ localCustodyTokenAddress(sourceChain: number): PublicKey {
+ const encodedSourceChain = Buffer.alloc(2);
+ encodedSourceChain.writeUInt16BE(sourceChain);
+
+ return PublicKey.findProgramAddressSync(
+ [Buffer.from("local-custody"), encodedSourceChain],
+ this.ID,
+ )[0];
+ }
+
+ async fetchLocalCustodyTokenBalance(sourceChain: number): Promise {
+ return splToken
+ .getAccount(
+ this.program.provider.connection,
+ this.localCustodyTokenAddress(sourceChain),
+ )
+ .then((token) => token.amount)
+ .catch((_) => 0n);
+ }
+
async approveCustodianIx(
owner: PublicKey,
amount: bigint | number,
@@ -267,6 +308,23 @@ export class MatchingEngineProgram {
);
}
+ async approveAuctionIx(
+ accounts: {
+ auction: PublicKey;
+ owner: PublicKey;
+ },
+ amount: bigint | number,
+ ): Promise {
+ const { auction, owner } = accounts;
+
+ return splToken.createApproveInstruction(
+ splToken.getAssociatedTokenAddressSync(USDC_MINT_ADDRESS, owner),
+ auction,
+ owner,
+ amount,
+ );
+ }
+
async commonAccounts(): Promise {
const custodian = this.custodianAddress();
const { coreBridgeConfig, coreEmitterSequence, coreFeeCollector, coreBridgeProgram } =
@@ -306,6 +364,143 @@ export class MatchingEngineProgram {
};
}
+ checkedCustodianComposite(addr?: PublicKey): { custodian: PublicKey } {
+ return { custodian: addr ?? this.custodianAddress() };
+ }
+
+ adminComposite(
+ ownerOrAssistant: PublicKey,
+ custodian?: PublicKey,
+ ): { ownerOrAssistant: PublicKey; custodian: { custodian: PublicKey } } {
+ return { ownerOrAssistant, custodian: this.checkedCustodianComposite(custodian) };
+ }
+
+ ownerOnlyComposite(
+ owner: PublicKey,
+ custodian?: PublicKey,
+ ): { owner: PublicKey; custodian: { custodian: PublicKey } } {
+ return { owner, custodian: this.checkedCustodianComposite(custodian) };
+ }
+
+ routerEndpointComposite(addr: PublicKey): { endpoint: PublicKey } {
+ return {
+ endpoint: addr,
+ };
+ }
+
+ liquidityLayerVaaComposite(vaa: PublicKey): { vaa: PublicKey } {
+ return {
+ vaa,
+ };
+ }
+
+ usdcComposite(mint?: PublicKey): { mint: PublicKey } {
+ return {
+ mint: mint ?? this.mint,
+ };
+ }
+
+ localTokenRouterComposite(tokenRouterProgram: PublicKey): {
+ tokenRouterProgram: PublicKey;
+ tokenRouterEmitter: PublicKey;
+ tokenRouterMintRecipient: PublicKey;
+ } {
+ const [tokenRouterEmitter] = PublicKey.findProgramAddressSync(
+ [Buffer.from("emitter")],
+ tokenRouterProgram,
+ );
+ return {
+ tokenRouterProgram,
+ tokenRouterEmitter,
+ tokenRouterMintRecipient: splToken.getAssociatedTokenAddressSync(
+ this.mint,
+ tokenRouterEmitter,
+ true,
+ ),
+ };
+ }
+
+ async activeAuctionComposite(
+ accounts: {
+ auction: PublicKey;
+ config?: PublicKey;
+ bestOfferToken?: PublicKey;
+ },
+ cached: {
+ info?: AuctionInfo | null;
+ } = {},
+ ) {
+ const { auction, config: inputConfig, bestOfferToken: inputBestOfferToken } = accounts;
+ let { info: inputInfo } = cached;
+ inputInfo ??= null;
+
+ const { config, bestOfferToken } = await (async () => {
+ if (inputConfig === undefined || inputBestOfferToken === undefined) {
+ const { info } =
+ inputInfo === null
+ ? await this.fetchAuction({ address: auction })
+ : { info: inputInfo };
+ if (info === null) {
+ throw new Error("Auction info not found");
+ }
+
+ const { configId, bestOfferToken } = info;
+ return {
+ config: inputConfig ?? this.auctionConfigAddress(configId),
+ bestOfferToken: inputBestOfferToken ?? bestOfferToken,
+ };
+ } else {
+ return {
+ config: inputConfig,
+ bestOfferToken: inputBestOfferToken,
+ };
+ }
+ })();
+
+ return {
+ custodyToken: this.auctionCustodyTokenAddress(auction),
+ auction,
+ config,
+ bestOfferToken,
+ };
+ }
+
+ closePreparedOrderResponseComposite(accounts: { by: PublicKey; orderResponse: PublicKey }): {
+ by: PublicKey;
+ orderResponse: PublicKey;
+ custodyToken: PublicKey;
+ } {
+ const { by, orderResponse } = accounts;
+ return {
+ by,
+ orderResponse,
+ custodyToken: this.preparedCustodyTokenAddress(orderResponse),
+ };
+ }
+
+ fastOrderPathComposite(accounts: { fastVaa: PublicKey; from: PublicKey; to: PublicKey }): {
+ fastVaa: {
+ vaa: PublicKey;
+ };
+ from: {
+ endpoint: PublicKey;
+ };
+ to: { endpoint: PublicKey };
+ } {
+ const { fastVaa, from, to } = accounts;
+ return {
+ fastVaa: { vaa: fastVaa },
+ from: { endpoint: from },
+ to: { endpoint: to },
+ };
+ }
+
+ cctpMintRecipientComposite(): { mintRecipient: PublicKey } {
+ return {
+ mintRecipient: this.cctpMintRecipientAddress(),
+ };
+ }
+
async initializeIx(
accounts: {
owner: PublicKey;
@@ -328,7 +523,7 @@ export class MatchingEngineProgram {
feeRecipient,
feeRecipientToken: splToken.getAssociatedTokenAddressSync(this.mint, feeRecipient),
cctpMintRecipient: this.cctpMintRecipientAddress(),
- mint: inputMint ?? this.mint,
+ usdc: this.usdcComposite(inputMint),
programData: programDataAddress(this.ID),
upgradeManagerAuthority: upgradeManager.upgradeAuthorityAddress(),
upgradeManagerProgram: upgradeManager.ID,
@@ -346,10 +541,7 @@ export class MatchingEngineProgram {
return this.program.methods
.submitOwnershipTransferRequest()
.accounts({
- admin: {
- owner,
- custodian: inputCustodian ?? this.custodianAddress(),
- },
+ admin: this.ownerOnlyComposite(owner, inputCustodian),
newOwner,
})
.instruction();
@@ -377,10 +569,7 @@ export class MatchingEngineProgram {
return this.program.methods
.cancelOwnershipTransferRequest()
.accounts({
- admin: {
- owner,
- custodian: inputCustodian ?? this.custodianAddress(),
- },
+ admin: this.ownerOnlyComposite(owner, inputCustodian),
})
.instruction();
}
@@ -394,8 +583,7 @@ export class MatchingEngineProgram {
return this.program.methods
.updateOwnerAssistant()
.accounts({
- owner,
- custodian: inputCustodian ?? this.custodianAddress(),
+ admin: this.ownerOnlyComposite(owner, inputCustodian),
newOwnerAssistant,
})
.instruction();
@@ -426,12 +614,12 @@ export class MatchingEngineProgram {
.addCctpRouterEndpoint(args)
.accounts({
payer: inputPayer ?? ownerOrAssistant,
- admin: {
- ownerOrAssistant,
- custodian: inputCustodian ?? this.custodianAddress(),
- },
+ admin: this.adminComposite(ownerOrAssistant, inputCustodian),
routerEndpoint: inputRouterEndpoint ?? this.routerEndpointAddress(chain),
+ localRouterEndpoint: this.routerEndpointAddress(wormholeSdk.CHAIN_ID_SOLANA),
+ localCustodyToken: this.localCustodyTokenAddress(chain),
remoteTokenMessenger: inputRemoteTokenMessenger ?? derivedRemoteTokenMessenger,
+ usdc: this.usdcComposite(),
})
.instruction();
}
@@ -458,13 +646,10 @@ export class MatchingEngineProgram {
return this.program.methods
.updateCctpRouterEndpoint(args)
.accounts({
- admin: {
- owner,
- custodian: inputCustodian ?? this.custodianAddress(),
- },
- routerEndpoint: {
- inner: inputRouterEndpoint ?? this.routerEndpointAddress(chain),
- },
+ admin: this.ownerOnlyComposite(owner, inputCustodian),
+ routerEndpoint: this.routerEndpointComposite(
+ inputRouterEndpoint ?? this.routerEndpointAddress(chain),
+ ),
remoteTokenMessenger: inputRemoteTokenMessenger ?? derivedRemoteTokenMessenger,
})
.instruction();
@@ -492,7 +677,7 @@ export class MatchingEngineProgram {
payer: inputPayer ?? ownerOrAssistant,
admin: {
ownerOrAssistant,
- custodian: inputCustodian ?? this.custodianAddress(),
+ custodian: this.checkedCustodianComposite(inputCustodian),
},
proposal: inputProposal ?? (await this.proposalAddress()),
epochSchedule: SYSVAR_EPOCH_SCHEDULE_PUBKEY,
@@ -509,10 +694,7 @@ export class MatchingEngineProgram {
return this.program.methods
.closeProposal()
.accounts({
- admin: {
- owner,
- custodian: this.custodianAddress(),
- },
+ admin: this.ownerOnlyComposite(owner),
proposedBy,
proposal,
})
@@ -548,10 +730,7 @@ export class MatchingEngineProgram {
.updateAuctionParameters()
.accounts({
payer: inputPayer ?? owner,
- admin: {
- owner,
- custodian: inputCustodian ?? this.custodianAddress(),
- },
+ admin: this.ownerOnlyComposite(owner, inputCustodian),
proposal: inputProposal ?? (await this.proposalAddress()),
auctionConfig,
})
@@ -572,29 +751,15 @@ export class MatchingEngineProgram {
custodian: inputCustodian,
routerEndpoint: inputRouterEndpoint,
} = accounts;
- const [tokenRouterEmitter] = PublicKey.findProgramAddressSync(
- [Buffer.from("emitter")],
- tokenRouterProgram,
- );
+
return this.program.methods
.addLocalRouterEndpoint()
.accounts({
payer: inputPayer ?? ownerOrAssistant,
- admin: {
- ownerOrAssistant,
- custodian: inputCustodian ?? this.custodianAddress(),
- },
+ admin: this.adminComposite(ownerOrAssistant, inputCustodian),
routerEndpoint:
inputRouterEndpoint ?? this.routerEndpointAddress(wormholeSdk.CHAIN_ID_SOLANA),
- local: {
- tokenRouterProgram,
- tokenRouterEmitter,
- tokenRouterMintRecipient: splToken.getAssociatedTokenAddressSync(
- this.mint,
- tokenRouterEmitter,
- true,
- ),
- },
+ local: this.localTokenRouterComposite(tokenRouterProgram),
})
.instruction();
}
@@ -611,31 +776,18 @@ export class MatchingEngineProgram {
custodian: inputCustodian,
routerEndpoint: inputRouterEndpoint,
} = accounts;
- const [tokenRouterEmitter] = PublicKey.findProgramAddressSync(
- [Buffer.from("emitter")],
- tokenRouterProgram,
- );
+
return this.program.methods
.updateLocalRouterEndpoint()
.accounts({
admin: {
owner,
- custodian: inputCustodian ?? this.custodianAddress(),
- },
- routerEndpoint: {
- inner:
- inputRouterEndpoint ??
- this.routerEndpointAddress(wormholeSdk.CHAIN_ID_SOLANA),
- },
- local: {
- tokenRouterProgram,
- tokenRouterEmitter,
- tokenRouterMintRecipient: splToken.getAssociatedTokenAddressSync(
- this.mint,
- tokenRouterEmitter,
- true,
- ),
+ custodian: this.checkedCustodianComposite(inputCustodian),
},
+ routerEndpoint: this.routerEndpointComposite(
+ inputRouterEndpoint ?? this.routerEndpointAddress(wormholeSdk.CHAIN_ID_SOLANA),
+ ),
+ local: this.localTokenRouterComposite(tokenRouterProgram),
})
.instruction();
}
@@ -652,13 +804,10 @@ export class MatchingEngineProgram {
return this.program.methods
.disableRouterEndpoint()
.accounts({
- admin: {
- owner,
- custodian: inputCustodian ?? this.custodianAddress(),
- },
- routerEndpoint: {
- inner: inputRouterEndpoint ?? this.routerEndpointAddress(chain),
- },
+ admin: this.ownerOnlyComposite(owner, inputCustodian),
+ routerEndpoint: this.routerEndpointComposite(
+ inputRouterEndpoint ?? this.routerEndpointAddress(chain),
+ ),
})
.instruction();
}
@@ -673,8 +822,10 @@ export class MatchingEngineProgram {
return this.program.methods
.updateFeeRecipient()
.accounts({
- ownerOrAssistant,
- custodian: inputCustodian ?? this.custodianAddress(),
+ admin: {
+ ownerOrAssistant,
+ custodian: this.checkedCustodianComposite(inputCustodian),
+ },
newFeeRecipient,
newFeeRecipientToken: splToken.getAssociatedTokenAddressSync(
this.mint,
@@ -728,8 +879,6 @@ export class MatchingEngineProgram {
totalDeposit: inputTotalDeposit,
} = accounts;
- const cctpMintRecipient = this.cctpMintRecipientAddress();
-
const offerToken = await (async () => {
if (inputOfferToken !== undefined) {
return inputOfferToken;
@@ -774,6 +923,8 @@ export class MatchingEngineProgram {
}
})();
+ const auctionCustodyToken = this.auctionCustodyTokenAddress(auction);
+
const auctionConfig = await (async () => {
if (inputAuctionConfig === undefined) {
const { auctionConfigId } = await this.fetchCustodian();
@@ -783,19 +934,22 @@ export class MatchingEngineProgram {
}
})();
- const approveIx = await this.approveCustodianIx(payer, totalDeposit);
+ const approveIx = await this.approveAuctionIx({ auction, owner: payer }, totalDeposit);
const placeInitialOfferIx = await this.program.methods
.placeInitialOffer(new BN(feeOffer.toString()))
.accounts({
payer,
- custodian: this.custodianAddress(),
+ custodian: this.checkedCustodianComposite(),
auctionConfig,
auction,
- fromRouterEndpoint,
- toRouterEndpoint,
+ fastOrderPath: this.fastOrderPathComposite({
+ fastVaa,
+ from: fromRouterEndpoint,
+ to: toRouterEndpoint,
+ }),
offerToken,
- cctpMintRecipient,
- fastVaa,
+ auctionCustodyToken,
+ usdc: this.usdcComposite(),
})
.instruction();
@@ -809,7 +963,7 @@ export class MatchingEngineProgram {
auctionConfig?: PublicKey;
bestOfferToken?: PublicKey;
},
- feeOffer: bigint,
+ offerPrice: bigint,
): Promise<[approveIx: TransactionInstruction, improveOfferIx: TransactionInstruction]> {
const {
offerAuthority,
@@ -818,37 +972,25 @@ export class MatchingEngineProgram {
bestOfferToken: inputBestOfferToken,
} = accounts;
+ // TODO: add cached args above
const { info } = await this.fetchAuction({ address: auction });
if (info === null) {
throw new Error("no auction info found");
}
- const { auctionConfig, bestOfferToken } = await (async () => {
- if (inputAuctionConfig === undefined || inputBestOfferToken === undefined) {
- return {
- auctionConfig: inputAuctionConfig ?? this.auctionConfigAddress(info.configId),
- bestOfferToken: inputBestOfferToken ?? info.bestOfferToken,
- };
- } else {
- return {
- auctionConfig: inputAuctionConfig,
- bestOfferToken: inputBestOfferToken,
- };
- }
- })();
- const approveIx = await this.approveCustodianIx(
- offerAuthority,
+ const approveIx = await this.approveAuctionIx(
+ { auction, owner: offerAuthority },
info.amountIn.add(info.securityDeposit).toNumber(),
);
+
const improveOfferIx = await this.program.methods
- .improveOffer(new BN(feeOffer.toString()))
+ .improveOffer(new BN(offerPrice.toString()))
.accounts({
- offerAuthority,
- custodian: this.custodianAddress(),
- auctionConfig,
- auction,
+ activeAuction: await this.activeAuctionComposite(
+ { auction, config: inputAuctionConfig, bestOfferToken: inputBestOfferToken },
+ { info },
+ ),
offerToken: splToken.getAssociatedTokenAddressSync(this.mint, offerAuthority),
- bestOfferToken,
})
.instruction();
@@ -860,11 +1002,11 @@ export class MatchingEngineProgram {
payer: PublicKey;
fastVaa: PublicKey;
finalizedVaa: PublicKey;
- mint?: PublicKey;
},
args: CctpMessageArgs,
+ hasAuction: boolean = false,
): Promise {
- const { payer, fastVaa, finalizedVaa, mint: inputMint } = accounts;
+ const { payer, fastVaa, finalizedVaa } = accounts;
const fastVaaAcct = await VaaAccount.fetch(this.program.provider.connection, fastVaa);
const { encodedCctpMessage } = args;
@@ -883,322 +1025,96 @@ export class MatchingEngineProgram {
messageTransmitterProgram,
tokenMessengerMinterEventAuthority,
} = this.messageTransmitterProgram().receiveTokenMessengerMinterMessageAccounts(
- inputMint ?? this.mint,
+ this.mint,
encodedCctpMessage,
);
+ const preparedOrderResponse = this.preparedOrderResponseAddress(fastVaaAcct.digest());
return this.program.methods
.prepareOrderResponseCctp(args)
.accounts({
payer,
- custodian: this.custodianAddress(),
- fastVaa,
- finalizedVaa,
- preparedOrderResponse: this.preparedOrderResponseAddress(
- payer,
- fastVaaAcct.digest(),
- ),
- cctpMintRecipient: this.cctpMintRecipientAddress(),
- messageTransmitterAuthority,
- messageTransmitterConfig,
- usedNonces,
- messageTransmitterEventAuthority,
- tokenMessenger,
- remoteTokenMessenger,
- tokenMinter,
- localToken,
- tokenPair,
- tokenMessengerMinterCustodyToken,
- tokenMessengerMinterEventAuthority,
- tokenMessengerMinterProgram,
- messageTransmitterProgram,
+ custodian: this.checkedCustodianComposite(),
+ fastVaa: this.liquidityLayerVaaComposite(fastVaa),
+ finalizedVaa: this.liquidityLayerVaaComposite(finalizedVaa),
+ preparedOrderResponse,
+ preparedCustodyToken: this.preparedCustodyTokenAddress(preparedOrderResponse),
+ usdc: this.usdcComposite(),
+ auction: hasAuction ? this.auctionAddress(fastVaaAcct.digest()) : null,
+ cctp: {
+ mintRecipient: this.cctpMintRecipientComposite(),
+ messageTransmitterAuthority,
+ messageTransmitterConfig,
+ usedNonces,
+ messageTransmitterEventAuthority,
+ tokenMessenger,
+ remoteTokenMessenger,
+ tokenMinter,
+ localToken,
+ tokenPair,
+ tokenMessengerMinterCustodyToken,
+ tokenMessengerMinterEventAuthority,
+ tokenMessengerMinterProgram,
+ messageTransmitterProgram,
+ },
})
.instruction();
}
async settleAuctionCompleteIx(accounts: {
+ executor: PublicKey;
preparedOrderResponse: PublicKey;
auction?: PublicKey;
- preparedBy?: PublicKey;
bestOfferToken?: PublicKey;
}) {
const {
+ executor,
preparedOrderResponse,
auction: inputAuction,
- preparedBy: inputPreparedBy,
bestOfferToken: inputBestOfferToken,
} = accounts;
- const { preparedBy, auction } = await (async () => {
- if (inputPreparedBy !== undefined && inputAuction !== undefined) {
+ const { auction } = await (async () => {
+ if (inputAuction !== undefined) {
return {
- preparedBy: inputPreparedBy,
auction: inputAuction,
};
} else {
- const { preparedBy, fastVaaHash } = await this.fetchPreparedOrderResponse({
+ const { fastVaaHash } = await this.fetchPreparedOrderResponse({
address: preparedOrderResponse,
});
return {
- preparedBy: inputPreparedBy ?? preparedBy,
auction: inputAuction ?? this.auctionAddress(fastVaaHash),
};
}
})();
- const bestOfferToken = await (async () => {
- if (inputBestOfferToken !== undefined) {
- return inputBestOfferToken;
- } else {
+ const { bestOfferToken } = await (async () => {
+ if (inputBestOfferToken === undefined) {
const { info } = await this.fetchAuction({ address: auction });
if (info === null) {
throw new Error("no auction info found");
}
- return info.bestOfferToken;
- }
- })();
-
- return this.program.methods
- .settleAuctionComplete()
- .accounts({
- custodian: this.custodianAddress(),
- preparedBy,
- preparedOrderResponse,
- auction,
- bestOfferToken,
- cctpMintRecipient: this.cctpMintRecipientAddress(),
- })
- .instruction();
- }
-
- async settleAuctionActiveLocalIx(accounts: {
- payer: PublicKey;
- fastVaa: PublicKey;
- executorToken: PublicKey;
- preparedOrderResponse?: PublicKey;
- auction?: PublicKey;
- bestOfferToken?: PublicKey;
- auctionConfig?: PublicKey;
- }) {
- const {
- payer,
- preparedOrderResponse: inputPreparedOrderResponse,
- auction,
- fastVaa,
- executorToken,
- bestOfferToken: inputBestOfferToken,
- auctionConfig: inputAuctionConfig,
- } = accounts;
- const fastVaaAccount = await VaaAccount.fetch(this.program.provider.connection, fastVaa);
-
- const mint = this.mint;
- const auctionAddress = auction ?? this.auctionAddress(fastVaaAccount.digest());
- const preparedOrderResponse =
- inputPreparedOrderResponse ??
- this.preparedOrderResponseAddress(payer, fastVaaAccount.digest());
-
- const { auctionConfig, bestOfferToken } = await (async () => {
- if (inputAuctionConfig === undefined || inputBestOfferToken === undefined) {
- const { info } = await this.fetchAuction({ address: auctionAddress });
- if (info === null) {
- throw new Error("no auction info found");
- }
- const { configId, bestOfferToken } = info;
return {
- auctionConfig: inputAuctionConfig ?? this.auctionConfigAddress(configId),
- bestOfferToken: inputBestOfferToken ?? bestOfferToken,
+ bestOfferToken: inputBestOfferToken ?? info.bestOfferToken,
};
} else {
return {
- auctionConfig: inputAuctionConfig,
bestOfferToken: inputBestOfferToken,
};
}
})();
- const { targetChain, toRouterEndpoint } = await (async () => {
- const message = LiquidityLayerMessage.decode(fastVaaAccount.payload());
- if (message.fastMarketOrder == undefined) {
- throw new Error("Message not FastMarketOrder");
- }
-
- const targetChain = message.fastMarketOrder.targetChain;
- const toRouterEndpoint = this.routerEndpointAddress(
- message.fastMarketOrder.targetChain,
- );
-
- return { targetChain, toRouterEndpoint };
- })();
-
- const payerSequence = this.payerSequenceAddress(payer);
- const payerSequenceValue = await this.fetchPayerSequenceValue({
- address: payerSequence,
- });
- const {
- custodian,
- coreMessage,
- coreBridgeConfig,
- coreEmitterSequence,
- coreFeeCollector,
- coreBridgeProgram,
- } = await this.publishMessageAccounts(payer, payerSequenceValue);
-
- const cctpMintRecipient = this.cctpMintRecipientAddress();
return this.program.methods
- .settleAuctionActiveLocal()
+ .settleAuctionComplete()
.accounts({
- payer,
- payerSequence,
- custodian,
- auctionConfig,
- fastVaa,
+ executor,
+ executorToken: splToken.getAssociatedTokenAddressSync(this.mint, executor),
preparedOrderResponse,
+ preparedCustodyToken: this.preparedCustodyTokenAddress(preparedOrderResponse),
auction,
- cctpMintRecipient,
- toRouterEndpoint,
- coreBridgeConfig,
- coreMessage,
- coreEmitterSequence,
- coreBridgeProgram,
- tokenProgram: splToken.TOKEN_PROGRAM_ID,
- systemProgram: SystemProgram.programId,
- clock: SYSVAR_CLOCK_PUBKEY,
- coreFeeCollector,
- rent: SYSVAR_RENT_PUBKEY,
- bestOfferToken,
- executorToken,
- })
- .instruction();
- }
-
- async settleAuctionActiveCctpIx(
- accounts: {
- payer: PublicKey;
- executorToken: PublicKey;
- preparedOrderResponse?: PublicKey;
- auction?: PublicKey;
- fastVaa: PublicKey;
- fastVaaAccount: VaaAccount;
- auctionConfig?: PublicKey;
- bestOfferToken?: PublicKey;
- encodedCctpMessage: Buffer;
- },
- args: { targetChain: wormholeSdk.ChainId; remoteDomain?: number },
- ) {
- const {
- payer,
- auction: inputAuction,
- executorToken,
- preparedOrderResponse: inputPreparedOrderResponse,
- fastVaa,
- fastVaaAccount,
- auctionConfig: inputAuctionConfig,
- bestOfferToken: inputBestOfferToken,
- encodedCctpMessage,
- } = accounts;
- const auctionAddress = inputAuction ?? this.auctionAddress(fastVaaAccount.digest());
-
- const mint = this.mint;
-
- const preparedOrderResponse =
- inputPreparedOrderResponse ??
- this.preparedOrderResponseAddress(payer, fastVaaAccount.digest());
-
- const { auctionConfig, bestOfferToken } = await (async () => {
- if (inputAuctionConfig === undefined || inputBestOfferToken === undefined) {
- const { info } = await this.fetchAuction({ address: auctionAddress });
- if (info === null) {
- throw new Error("no auction info found");
- }
- const { configId, bestOfferToken } = info;
- return {
- auctionConfig: inputAuctionConfig ?? this.auctionConfigAddress(configId),
- bestOfferToken: inputBestOfferToken ?? bestOfferToken,
- };
- } else {
- return {
- auctionConfig: inputAuctionConfig,
- bestOfferToken: inputBestOfferToken,
- };
- }
- })();
-
- const targetChain = await (async () => {
- const message = LiquidityLayerMessage.decode(fastVaaAccount.payload());
- if (message.fastMarketOrder == undefined) {
- throw new Error("Message not FastMarketOrder");
- }
-
- const targetChain = message.fastMarketOrder.targetChain;
-
- return targetChain;
- })();
-
- const {
- protocol: { cctp },
- } = await this.fetchRouterEndpoint(targetChain);
- if (cctp === undefined) {
- throw new Error("CCTP domain is not undefined");
- }
- const destinationCctpDomain = cctp.domain;
-
- const routerEndpoint = this.routerEndpointAddress(targetChain);
- const {
- custodian,
- payerSequence,
- tokenMessengerMinterSenderAuthority,
- coreBridgeConfig,
- coreMessage,
- cctpMessage,
- coreEmitterSequence,
- coreFeeCollector,
- coreBridgeProgram,
- messageTransmitterConfig,
- tokenMessengerMinterProgram,
- tokenMinter,
- localToken,
- tokenMessenger,
- tokenMessengerMinterEventAuthority,
- messageTransmitterProgram,
- } = await this.burnAndPublishAccounts(
- { payer, mint },
- { targetChain, destinationCctpDomain },
- );
-
- return this.program.methods
- .settleAuctionActiveCctp()
- .accounts({
- payer,
- payerSequence,
- custodian,
- fastVaa,
- preparedOrderResponse,
- auction: auctionAddress,
- executorToken,
- cctpMintRecipient: this.cctpMintRecipientAddress(),
- auctionConfig,
bestOfferToken,
- toRouterEndpoint: routerEndpoint,
- mint,
- messageTransmitterConfig,
- coreBridgeConfig,
- coreEmitterSequence,
- coreFeeCollector,
- coreMessage,
- cctpMessage,
- localToken,
- tokenMinter,
- tokenMessenger,
- tokenMessengerMinterProgram,
- tokenMessengerMinterSenderAuthority,
- remoteTokenMessenger:
- this.tokenMessengerMinterProgram().remoteTokenMessengerAddress(
- destinationCctpDomain,
- ),
- tokenMessengerMinterEventAuthority,
- messageTransmitterProgram,
- coreBridgeProgram,
})
.instruction();
}
@@ -1217,11 +1133,9 @@ export class MatchingEngineProgram {
} = accounts;
const fastVaaAccount = await VaaAccount.fetch(this.program.provider.connection, fastVaa);
- const mint = this.mint;
-
const preparedOrderResponse =
inputPreparedOrderResponse ??
- this.preparedOrderResponseAddress(payer, fastVaaAccount.digest());
+ this.preparedOrderResponseAddress(fastVaaAccount.digest());
const { targetChain, toRouterEndpoint } = await (async () => {
const message = LiquidityLayerMessage.decode(fastVaaAccount.payload());
@@ -1251,30 +1165,31 @@ export class MatchingEngineProgram {
} = await this.publishMessageAccounts(payer, payerSequenceValue);
const { feeRecipientToken } = await this.fetchCustodian();
- const cctpMintRecipient = this.cctpMintRecipientAddress();
return this.program.methods
.settleAuctionNoneLocal()
.accounts({
payer,
payerSequence,
- custodian,
- fastVaa,
- preparedOrderResponse,
- auction,
- cctpMintRecipient,
- feeRecipientToken,
- fromRouterEndpoint: this.routerEndpointAddress(fastVaaAccount.emitterInfo().chain),
- toRouterEndpoint,
- coreBridgeConfig,
coreMessage,
- coreEmitterSequence,
- coreBridgeProgram,
- tokenProgram: splToken.TOKEN_PROGRAM_ID,
- systemProgram: SystemProgram.programId,
- clock: SYSVAR_CLOCK_PUBKEY,
- coreFeeCollector,
- rent: SYSVAR_RENT_PUBKEY,
+ custodian: this.checkedCustodianComposite(custodian),
+ feeRecipientToken,
+ prepared: this.closePreparedOrderResponseComposite({
+ by: payer,
+ orderResponse: preparedOrderResponse,
+ }),
+ fastOrderPath: this.fastOrderPathComposite({
+ fastVaa,
+ from: this.routerEndpointAddress(fastVaaAccount.emitterInfo().chain),
+ to: toRouterEndpoint,
+ }),
+ auction,
+ wormhole: {
+ config: coreBridgeConfig,
+ emitterSequence: coreEmitterSequence,
+ feeCollector: coreFeeCollector,
+ coreBridgeProgram,
+ },
})
.instruction();
}
@@ -1323,30 +1238,39 @@ export class MatchingEngineProgram {
.accounts({
payer,
payerSequence,
- custodian,
- fastVaa,
- preparedOrderResponse,
- auction: this.auctionAddress(fastVaaAccount.digest()),
- cctpMintRecipient: this.cctpMintRecipientAddress(),
- feeRecipientToken,
- mint: this.mint,
- fromRouterEndpoint: this.routerEndpointAddress(fastVaaAccount.emitterInfo().chain),
- toRouterEndpoint,
- coreBridgeConfig,
coreMessage,
cctpMessage,
- coreEmitterSequence,
- coreFeeCollector,
- tokenMessengerMinterSenderAuthority,
- messageTransmitterConfig,
- tokenMessenger,
- remoteTokenMessenger,
- tokenMinter,
- localToken,
- tokenMessengerMinterEventAuthority,
- coreBridgeProgram,
- tokenMessengerMinterProgram,
- messageTransmitterProgram,
+ custodian: this.checkedCustodianComposite(custodian),
+ feeRecipientToken,
+ prepared: this.closePreparedOrderResponseComposite({
+ by: payer,
+ orderResponse: preparedOrderResponse,
+ }),
+ fastOrderPath: this.fastOrderPathComposite({
+ fastVaa,
+ from: this.routerEndpointAddress(fastVaaAccount.emitterInfo().chain),
+ to: toRouterEndpoint,
+ }),
+ auction: this.auctionAddress(fastVaaAccount.digest()),
+ wormhole: {
+ config: coreBridgeConfig,
+ emitterSequence: coreEmitterSequence,
+ feeCollector: coreFeeCollector,
+ coreBridgeProgram,
+ },
+ cctp: {
+ burnSource: this.cctpMintRecipientComposite(),
+ mint: this.mint,
+ tokenMessengerMinterSenderAuthority,
+ messageTransmitterConfig,
+ tokenMessenger,
+ remoteTokenMessenger,
+ tokenMinter,
+ localToken,
+ tokenMessengerMinterEventAuthority,
+ tokenMessengerMinterProgram,
+ messageTransmitterProgram,
+ },
})
.instruction();
}
@@ -1379,27 +1303,20 @@ export class MatchingEngineProgram {
const auction = inputAuction ?? this.auctionAddress(vaaAccount.digest());
- const { auctionConfig, initialOfferToken, bestOfferToken } = await (async () => {
- if (
- inputAuctionConfig === undefined ||
- inputInitialOfferToken === undefined ||
- inputBestOfferToken === undefined
- ) {
+ const { info, initialOfferToken } = await (async () => {
+ if (inputInitialOfferToken === undefined) {
const { info } = await this.fetchAuction({ address: auction });
if (info === null) {
throw new Error("no auction info found");
}
- const { configId, initialOfferToken, bestOfferToken } = info;
return {
- auctionConfig: inputAuctionConfig ?? this.auctionConfigAddress(configId),
- initialOfferToken: inputInitialOfferToken ?? initialOfferToken,
- bestOfferToken: inputBestOfferToken ?? bestOfferToken,
+ info,
+ initialOfferToken: inputInitialOfferToken ?? info.initialOfferToken,
};
} else {
return {
- auctionConfig: inputAuctionConfig,
+ info: null,
initialOfferToken: inputInitialOfferToken,
- bestOfferToken: inputBestOfferToken,
};
}
})();
@@ -1430,33 +1347,44 @@ export class MatchingEngineProgram {
.executeFastOrderCctp()
.accounts({
payer,
- custodian,
- auctionConfig,
- fastVaa,
- auction,
- toRouterEndpoint,
- executorToken:
- inputExecutorToken ?? splToken.getAssociatedTokenAddressSync(mint, payer),
- bestOfferToken,
- initialOfferToken,
- cctpMintRecipient: this.cctpMintRecipientAddress(),
- mint,
payerSequence,
- coreBridgeConfig,
coreMessage,
cctpMessage,
- coreEmitterSequence,
- coreFeeCollector,
- tokenMessengerMinterSenderAuthority,
- messageTransmitterConfig,
- tokenMessenger,
- remoteTokenMessenger,
- tokenMinter,
- localToken,
- tokenMessengerMinterEventAuthority,
- coreBridgeProgram,
- tokenMessengerMinterProgram,
- messageTransmitterProgram,
+ executeOrder: {
+ fastVaa: this.liquidityLayerVaaComposite(fastVaa),
+ activeAuction: await this.activeAuctionComposite(
+ {
+ auction,
+ config: inputAuctionConfig,
+ bestOfferToken: inputBestOfferToken,
+ },
+ { info },
+ ),
+ toRouterEndpoint: this.routerEndpointComposite(toRouterEndpoint),
+ executorToken:
+ inputExecutorToken ?? splToken.getAssociatedTokenAddressSync(mint, payer),
+ initialOfferToken,
+ },
+ custodian: this.checkedCustodianComposite(custodian),
+ wormhole: {
+ config: coreBridgeConfig,
+ emitterSequence: coreEmitterSequence,
+ feeCollector: coreFeeCollector,
+ coreBridgeProgram,
+ },
+ cctp: {
+ burnSource: this.cctpMintRecipientComposite(),
+ mint,
+ tokenMessengerMinterSenderAuthority,
+ messageTransmitterConfig,
+ tokenMessenger,
+ remoteTokenMessenger,
+ tokenMinter,
+ localToken,
+ tokenMessengerMinterEventAuthority,
+ tokenMessengerMinterProgram,
+ messageTransmitterProgram,
+ },
})
.instruction();
}
@@ -1485,30 +1413,13 @@ export class MatchingEngineProgram {
const vaaAccount = await VaaAccount.fetch(this.program.provider.connection, fastVaa);
const auction = inputAuction ?? this.auctionAddress(vaaAccount.digest());
- const { auctionConfig, initialOfferToken, bestOfferToken } = await (async () => {
- if (
- inputAuctionConfig === undefined ||
- inputInitialOfferToken === undefined ||
- inputBestOfferToken === undefined
- ) {
- const { info } = await this.fetchAuction({ address: auction });
- if (info === null) {
- throw new Error("no auction info found");
- }
- const { configId, initialOfferToken, bestOfferToken } = info;
- return {
- auctionConfig: inputAuctionConfig ?? this.auctionConfigAddress(configId),
- initialOfferToken: inputInitialOfferToken ?? initialOfferToken,
- bestOfferToken: inputBestOfferToken ?? bestOfferToken,
- };
- } else {
- return {
- auctionConfig: inputAuctionConfig,
- initialOfferToken: inputInitialOfferToken,
- bestOfferToken: inputBestOfferToken,
- };
- }
- })();
+ // TODO: Add caching so we do not have to do an rpc call here.
+ const { info } = await this.fetchAuction({ address: auction });
+ if (info === null) {
+ throw new Error("no auction info found");
+ }
+ const sourceChain = info.sourceChain;
+ const initialOfferToken = inputInitialOfferToken ?? info.initialOfferToken;
const payerSequence = this.payerSequenceAddress(payer);
const payerSequenceValue = await this.fetchPayerSequenceValue({
@@ -1527,40 +1438,64 @@ export class MatchingEngineProgram {
.executeFastOrderLocal()
.accounts({
payer,
- custodian,
- auctionConfig,
- fastVaa,
- auction,
- toRouterEndpoint:
- inputToRouterEndpoint ??
- this.routerEndpointAddress(wormholeSdk.CHAIN_ID_SOLANA),
- executorToken:
- inputExecutorToken ?? splToken.getAssociatedTokenAddressSync(this.mint, payer),
- bestOfferToken,
- initialOfferToken,
- cctpMintRecipient: this.cctpMintRecipientAddress(),
payerSequence,
- coreBridgeConfig,
+ custodian: this.checkedCustodianComposite(custodian),
coreMessage,
- coreEmitterSequence,
- coreFeeCollector,
- coreBridgeProgram,
+ executeOrder: {
+ fastVaa: this.liquidityLayerVaaComposite(fastVaa),
+ activeAuction: await this.activeAuctionComposite(
+ {
+ auction,
+ config: inputAuctionConfig,
+ bestOfferToken: inputBestOfferToken,
+ },
+ { info },
+ ),
+ toRouterEndpoint: this.routerEndpointComposite(
+ inputToRouterEndpoint ??
+ this.routerEndpointAddress(wormholeSdk.CHAIN_ID_SOLANA),
+ ),
+ executorToken:
+ inputExecutorToken ??
+ splToken.getAssociatedTokenAddressSync(this.mint, payer),
+ initialOfferToken,
+ },
+ wormhole: {
+ config: coreBridgeConfig,
+ emitterSequence: coreEmitterSequence,
+ feeCollector: coreFeeCollector,
+ coreBridgeProgram,
+ },
+ localCustodyToken: this.localCustodyTokenAddress(sourceChain),
})
.instruction();
}
async redeemFastFillAccounts(
vaa: PublicKey,
+ sourceChain?: number,
): Promise<{ vaaAccount: VaaAccount; accounts: RedeemFastFillAccounts }> {
const vaaAccount = await VaaAccount.fetch(this.program.provider.connection, vaa);
+ const localCustodyToken = this.localCustodyTokenAddress(
+ sourceChain ??
+ (() => {
+ const { fastFill } = LiquidityLayerMessage.decode(vaaAccount.payload());
+ if (fastFill === undefined) {
+ throw new Error("Message not FastFill");
+ }
+
+ return fastFill.fill.sourceChain;
+ })(),
+ );
+
return {
vaaAccount,
accounts: {
custodian: this.custodianAddress(),
redeemedFastFill: this.redeemedFastFillAddress(vaaAccount.digest()),
routerEndpoint: this.routerEndpointAddress(wormholeSdk.CHAIN_ID_SOLANA),
- cctpMintRecipient: this.cctpMintRecipientAddress(),
+ localCustodyToken,
matchingEngineProgram: this.ID,
},
};
diff --git a/solana/ts/src/matchingEngine/state/Auction.ts b/solana/ts/src/matchingEngine/state/Auction.ts
index 368189070..19ebfc00f 100644
--- a/solana/ts/src/matchingEngine/state/Auction.ts
+++ b/solana/ts/src/matchingEngine/state/Auction.ts
@@ -1,19 +1,19 @@
-import { PublicKey } from "@solana/web3.js";
import { BN } from "@coral-xyz/anchor";
-import { AuctionParameters } from "./AuctionConfig";
+import { PublicKey } from "@solana/web3.js";
export type AuctionStatus = {
notStarted?: {};
active?: {};
- completed?: { slot: BN };
+ completed?: { slot: BN; executePenalty: BN | null };
settled?: {
baseFee: BN;
- penalty: BN | null;
+ totalPenalty: BN | null;
};
};
export type AuctionInfo = {
configId: number;
+ custodyTokenBump: number;
vaaSequence: BN;
sourceChain: number;
bestOfferToken: PublicKey;
@@ -23,12 +23,13 @@ export type AuctionInfo = {
securityDeposit: BN;
offerPrice: BN;
amountOut: BN | null;
+ endEarly: boolean;
};
export class Auction {
bump: number;
vaaHash: number[];
- status: Object;
+ status: AuctionStatus;
info: AuctionInfo | null;
constructor(bump: number, vaaHash: number[], status: AuctionStatus, info: AuctionInfo | null) {
diff --git a/solana/ts/src/matchingEngine/state/PreparedOrderResponse.ts b/solana/ts/src/matchingEngine/state/PreparedOrderResponse.ts
index 59ee67623..03658c933 100644
--- a/solana/ts/src/matchingEngine/state/PreparedOrderResponse.ts
+++ b/solana/ts/src/matchingEngine/state/PreparedOrderResponse.ts
@@ -14,7 +14,7 @@ export class PreparedOrderResponse {
preparedBy: PublicKey,
fastVaaHash: Array,
sourceChain: number,
- baseFee: BN
+ baseFee: BN,
) {
this.bump = bump;
this.preparedBy = preparedBy;
@@ -23,10 +23,10 @@ export class PreparedOrderResponse {
this.baseFee = baseFee;
}
- static address(programId: PublicKey, preparedBy: PublicKey, fastVaaHash: VaaHash) {
+ static address(programId: PublicKey, fastVaaHash: VaaHash) {
return PublicKey.findProgramAddressSync(
- [Buffer.from("order-response"), preparedBy.toBuffer(), Buffer.from(fastVaaHash)],
- programId
+ [Buffer.from("order-response"), Buffer.from(fastVaaHash)],
+ programId,
)[0];
}
}
diff --git a/solana/ts/src/tokenRouter/index.ts b/solana/ts/src/tokenRouter/index.ts
index caf083ae3..54c2f291b 100644
--- a/solana/ts/src/tokenRouter/index.ts
+++ b/solana/ts/src/tokenRouter/index.ts
@@ -95,7 +95,7 @@ export type RedeemFastFillAccounts = {
matchingEngineCustodian: PublicKey;
matchingEngineRedeemedFastFill: PublicKey;
matchingEngineRouterEndpoint: PublicKey;
- matchingEngineCctpMintRecipient: PublicKey;
+ matchingEngineLocalCustodyToken: PublicKey;
matchingEngineProgram: PublicKey;
};
@@ -555,17 +555,20 @@ export class TokenRouterProgram {
.instruction();
}
- async redeemFastFillAccounts(vaa: PublicKey): Promise {
+ async redeemFastFillAccounts(
+ vaa: PublicKey,
+ sourceChain?: number,
+ ): Promise {
const {
vaaAccount,
accounts: {
custodian: matchingEngineCustodian,
redeemedFastFill: matchingEngineRedeemedFastFill,
routerEndpoint: matchingEngineRouterEndpoint,
- cctpMintRecipient: matchingEngineCctpMintRecipient,
+ localCustodyToken: matchingEngineLocalCustodyToken,
matchingEngineProgram,
},
- } = await this.matchingEngineProgram().redeemFastFillAccounts(vaa);
+ } = await this.matchingEngineProgram().redeemFastFillAccounts(vaa, sourceChain);
return {
custodian: this.custodianAddress(),
@@ -574,7 +577,7 @@ export class TokenRouterProgram {
matchingEngineCustodian,
matchingEngineRedeemedFastFill,
matchingEngineRouterEndpoint,
- matchingEngineCctpMintRecipient,
+ matchingEngineLocalCustodyToken,
matchingEngineProgram,
};
}
@@ -590,7 +593,7 @@ export class TokenRouterProgram {
matchingEngineCustodian,
matchingEngineRedeemedFastFill,
matchingEngineRouterEndpoint,
- matchingEngineCctpMintRecipient,
+ matchingEngineLocalCustodyToken,
matchingEngineProgram,
} = await this.redeemFastFillAccounts(vaa);
@@ -606,7 +609,7 @@ export class TokenRouterProgram {
matchingEngineCustodian,
matchingEngineRedeemedFastFill,
matchingEngineRouterEndpoint,
- matchingEngineCctpMintRecipient,
+ matchingEngineLocalCustodyToken,
matchingEngineProgram,
})
.instruction();
diff --git a/solana/ts/tests/01__matchingEngine.ts b/solana/ts/tests/01__matchingEngine.ts
index 70acd1d5d..97f39217e 100644
--- a/solana/ts/tests/01__matchingEngine.ts
+++ b/solana/ts/tests/01__matchingEngine.ts
@@ -8,7 +8,6 @@ import {
Connection,
Keypair,
PublicKey,
- SYSVAR_RENT_PUBKEY,
SystemProgram,
TransactionInstruction,
VersionedTransactionResponse,
@@ -21,6 +20,7 @@ import {
Fill,
LiquidityLayerDeposit,
LiquidityLayerMessage,
+ SlowOrderResponse,
} from "../src";
import {
Auction,
@@ -34,6 +34,7 @@ import {
} from "../src/matchingEngine";
import { VaaAccount } from "../src/wormhole";
import {
+ CHAIN_TO_DOMAIN,
CircleAttester,
ETHEREUM_USDC_ADDRESS,
LOCALHOST,
@@ -41,6 +42,7 @@ import {
OWNER_ASSISTANT_KEYPAIR,
OWNER_KEYPAIR,
PAYER_KEYPAIR,
+ REGISTERED_TOKEN_ROUTERS,
USDC_MINT_ADDRESS,
bigintToU64BN,
expectIxErr,
@@ -75,15 +77,15 @@ describe("Matching Engine", function () {
const liquidator = Keypair.generate();
// Foreign endpoints.
- const ethChain = wormholeSdk.CHAINS.ethereum;
- const ethRouter = Array.from(Buffer.alloc(32, "deadbeef", "hex"));
- const ethDomain = 0;
- const arbChain = wormholeSdk.CHAINS.arbitrum;
- const arbRouter = Array.from(Buffer.alloc(32, "bead", "hex"));
- const arbDomain = 3;
- const solanaChain = wormholeSdk.CHAINS.solana;
- const solanaRouter = Array.from(Buffer.alloc(32, "c0ffee", "hex"));
- const solanaDomain = 5;
+ const ethChain = wormholeSdk.coalesceChainId("ethereum");
+ const ethRouter = REGISTERED_TOKEN_ROUTERS["ethereum"]!;
+ const ethDomain = CHAIN_TO_DOMAIN["ethereum"]!;
+
+ const arbChain = wormholeSdk.coalesceChainId("arbitrum");
+ const arbRouter = REGISTERED_TOKEN_ROUTERS["arbitrum"]!;
+ const arbDomain = CHAIN_TO_DOMAIN["arbitrum"]!;
+
+ const solanaChain = wormholeSdk.coalesceChainId("solana");
// Matching Engine program.
const engine = new MatchingEngineProgram(connection, localnet(), USDC_MINT_ADDRESS);
@@ -99,6 +101,13 @@ describe("Matching Engine", function () {
minOfferDeltaBps: 20000, // 2%
};
+ let testCctpNonce = 2n ** 64n - 1n;
+
+ // Hack to prevent math overflow error when invoking CCTP programs.
+ testCctpNonce -= 10n * 6400n;
+
+ let wormholeSequence = 1000n;
+
describe("Admin", function () {
describe("Initialize", function () {
const localVariables = new Map();
@@ -115,17 +124,7 @@ describe("Matching Engine", function () {
},
auctionParams,
);
- const unknownAta = splToken.getAssociatedTokenAddressSync(
- mint,
- engine.custodianAddress(),
- true,
- );
- await expectIxErr(
- connection,
- [ix],
- [payer],
- `Instruction references an unknown account ${unknownAta.toString()}`,
- );
+ await expectIxErr(connection, [ix], [payer], "mint. Error Code: ConstraintAddress");
});
it("Cannot Initialize with Default Owner Assistant", async function () {
@@ -1115,13 +1114,6 @@ describe("Matching Engine", function () {
});
describe("Business Logic", function () {
- let testCctpNonce = 2n ** 64n - 1n;
-
- // Hack to prevent math overflow error when invoking CCTP programs.
- testCctpNonce -= 10n * 6400n;
-
- let wormholeSequence = 1000n;
-
const baseFastOrder: FastMarketOrder = {
amountIn: 50000000000n,
minAmountOut: 0n,
@@ -1138,33 +1130,12 @@ describe("Matching Engine", function () {
describe("Place Initial Offer", function () {
for (const offerPrice of [0n, baseFastOrder.maxFee / 2n, baseFastOrder.maxFee]) {
it(`Place Initial Offer (Price == ${offerPrice})`, async function () {
- // Fetch the balances before.
- const offerBalanceBefore = await getUsdcAtaBalance(
- connection,
- offerAuthorityOne.publicKey,
- );
- const { amount: custodyBalanceBefore } = await engine.fetchCctpMintRecipient();
-
- const { fastVaa, txDetails } = await placeInitialOfferForTest(
+ const { fastVaa, txDetails, auction } = await placeInitialOfferForTest(
offerAuthorityOne,
- wormholeSequence++,
baseFastOrder,
ethRouter,
offerPrice,
);
-
- // Validate balance changes.
- const offerBalanceAfter = await getUsdcAtaBalance(
- connection,
- offerAuthorityOne.publicKey,
- );
- const { amount: custodyBalanceAfter } = await engine.fetchCctpMintRecipient();
- const balanceChange = baseFastOrder.amountIn + baseFastOrder.maxFee;
-
- expect(offerBalanceAfter).equals(offerBalanceBefore - balanceChange);
- expect(custodyBalanceAfter).equals(custodyBalanceBefore + balanceChange);
-
- await checkAfterEffects({ txDetails, fastVaa, offerPrice });
});
}
@@ -1172,31 +1143,12 @@ describe("Matching Engine", function () {
const fastOrder = { ...baseFastOrder } as FastMarketOrder;
fastOrder.maxFee = fastOrder.amountIn - 1n;
- // Fetch the balances before.
- const offerBalanceBefore = await getUsdcAtaBalance(
- connection,
- offerAuthorityOne.publicKey,
- );
- const { amount: custodyBalanceBefore } = await engine.fetchCctpMintRecipient();
-
- const { fastVaa, txDetails } = await placeInitialOfferForTest(
+ await placeInitialOfferForTest(
offerAuthorityOne,
- wormholeSequence++,
fastOrder,
ethRouter,
+ fastOrder.maxFee,
);
-
- // Validate balance changes.
- const offerBalanceAfter = await getUsdcAtaBalance(
- connection,
- offerAuthorityOne.publicKey,
- );
- const { amount: custodyBalanceAfter } = await engine.fetchCctpMintRecipient();
- const balanceChange = fastOrder.amountIn + fastOrder.maxFee;
- expect(offerBalanceAfter).equals(offerBalanceBefore - balanceChange);
- expect(custodyBalanceAfter).equals(custodyBalanceBefore + balanceChange);
-
- await checkAfterEffects({ txDetails, fastVaa, offerPrice: fastOrder.maxFee });
});
it("Place Initial Offer (With Deadline)", async function () {
@@ -1210,32 +1162,7 @@ describe("Matching Engine", function () {
}
fastOrder.deadline = currTime + 10;
- // Fetch the balances before.
- const offerBalanceBefore = await getUsdcAtaBalance(
- connection,
- offerAuthorityOne.publicKey,
- );
- const { amount: custodyBalanceBefore } = await engine.fetchCctpMintRecipient();
-
- const { fastVaa, txDetails } = await placeInitialOfferForTest(
- offerAuthorityOne,
- wormholeSequence++,
- fastOrder,
- ethRouter,
- offerPrice,
- );
-
- // Validate balance changes.
- const offerBalanceAfter = await getUsdcAtaBalance(
- connection,
- offerAuthorityOne.publicKey,
- );
- const { amount: custodyBalanceAfter } = await engine.fetchCctpMintRecipient();
- const balanceChange = fastOrder.amountIn + fastOrder.maxFee;
- expect(offerBalanceAfter).equals(offerBalanceBefore - balanceChange);
- expect(custodyBalanceAfter).equals(custodyBalanceBefore + balanceChange);
-
- await checkAfterEffects({ txDetails, fastVaa, offerPrice });
+ await placeInitialOfferForTest(offerAuthorityOne, fastOrder, ethRouter, offerPrice);
});
it("Cannot Place Initial Offer (Invalid VAA)", async function () {
@@ -1486,53 +1413,6 @@ describe("Matching Engine", function () {
);
});
- async function checkAfterEffects(args: {
- txDetails: VersionedTransactionResponse;
- fastVaa: PublicKey;
- offerPrice: bigint;
- }) {
- const { txDetails, fastVaa, offerPrice } = args;
-
- // Confirm the auction data.
- const vaaAccount = await VaaAccount.fetch(connection, fastVaa);
- const { fastMarketOrder } = LiquidityLayerMessage.decode(vaaAccount.payload());
-
- const vaaHash = vaaAccount.digest();
- const auctionData = await engine.fetchAuction(vaaHash);
- const { bump } = auctionData;
-
- const { auctionConfigId } = await engine.fetchCustodian();
-
- const offerToken = splToken.getAssociatedTokenAddressSync(
- USDC_MINT_ADDRESS,
- offerAuthorityOne.publicKey,
- );
-
- expect(fastMarketOrder).is.not.undefined;
- const { amountIn, maxFee } = fastMarketOrder!;
-
- const expectedAmountIn = bigintToU64BN(amountIn);
- expect(auctionData).to.eql(
- new Auction(
- bump,
- Array.from(vaaHash),
- { active: {} },
- {
- configId: auctionConfigId,
- vaaSequence: bigintToU64BN(vaaAccount.emitterInfo().sequence),
- sourceChain: ethChain,
- bestOfferToken: offerToken,
- initialOfferToken: offerToken,
- startSlot: numberToU64BN(txDetails.slot),
- amountIn: expectedAmountIn,
- securityDeposit: bigintToU64BN(maxFee),
- offerPrice: bigintToU64BN(offerPrice),
- amountOut: expectedAmountIn,
- },
- ),
- );
- }
-
before("Register To Router Endpoints", async function () {
const ix = await engine.addCctpRouterEndpointIx(
{
@@ -1581,7 +1461,6 @@ describe("Matching Engine", function () {
it(`Improve Offer (Price == ${newOffer})`, async function () {
const { auction, auctionDataBefore } = await placeInitialOfferForTest(
offerAuthorityOne,
- wormholeSequence++,
baseFastOrder,
ethRouter,
);
@@ -1623,7 +1502,6 @@ describe("Matching Engine", function () {
it("Improve Offer By Min Offer Delta", async function () {
const { auction, auctionDataBefore } = await placeInitialOfferForTest(
offerAuthorityOne,
- wormholeSequence++,
baseFastOrder,
ethRouter,
);
@@ -1668,7 +1546,6 @@ describe("Matching Engine", function () {
it("Improve Offer With Same Best Offer Token Account", async function () {
const { auction, auctionDataBefore } = await placeInitialOfferForTest(
offerAuthorityOne,
- wormholeSequence++,
baseFastOrder,
ethRouter,
);
@@ -1708,7 +1585,6 @@ describe("Matching Engine", function () {
it("Cannot Improve Offer (Auction Expired)", async function () {
const { auction, auctionDataBefore } = await placeInitialOfferForTest(
offerAuthorityOne,
- wormholeSequence++,
baseFastOrder,
ethRouter,
);
@@ -1742,7 +1618,6 @@ describe("Matching Engine", function () {
it("Cannot Improve Offer (Invalid Best Offer Token Account)", async function () {
const { auction, auctionDataBefore } = await placeInitialOfferForTest(
offerAuthorityOne,
- wormholeSequence++,
baseFastOrder,
ethRouter,
);
@@ -1762,39 +1637,13 @@ describe("Matching Engine", function () {
connection,
[approveIx, ix],
[offerAuthorityOne],
- "Error Code: BestOfferTokenMismatch",
- );
- });
-
- it("Cannot Improve Offer (Offer Price Not Improved)", async function () {
- const { auction, auctionDataBefore } = await placeInitialOfferForTest(
- offerAuthorityOne,
- wormholeSequence++,
- baseFastOrder,
- ethRouter,
- );
-
- const newOffer = BigInt(auctionDataBefore.info!.offerPrice.toString());
- const [approveIx, ix] = await engine.improveOfferIx(
- {
- auction,
- offerAuthority: offerAuthorityTwo.publicKey,
- },
- newOffer,
- );
-
- await expectIxErr(
- connection,
- [approveIx, ix],
- [offerAuthorityTwo],
- "Error Code: OfferPriceNotImproved",
+ "best_offer_token. Error Code: ConstraintAddress",
);
});
it("Cannot Improve Offer (Carping Not Allowed)", async function () {
const { auction, auctionDataBefore } = await placeInitialOfferForTest(
offerAuthorityOne,
- wormholeSequence++,
baseFastOrder,
ethRouter,
);
@@ -1842,6 +1691,7 @@ describe("Matching Engine", function () {
const { bump, vaaHash, status, info } = auctionDataBefore;
const {
configId,
+ custodyTokenBump,
vaaSequence,
bestOfferToken: prevBestOfferToken,
initialOfferToken,
@@ -1858,6 +1708,7 @@ describe("Matching Engine", function () {
expect(auctionDataAfter).to.eql(
new Auction(bump, vaaHash, status, {
configId,
+ custodyTokenBump,
vaaSequence,
sourceChain,
bestOfferToken,
@@ -1867,6 +1718,7 @@ describe("Matching Engine", function () {
securityDeposit,
offerPrice: bigintToU64BN(offerPrice),
amountOut,
+ endEarly: false,
}),
);
@@ -1917,12 +1769,7 @@ describe("Matching Engine", function () {
fastVaa,
auction,
auctionDataBefore: initialData,
- } = await placeInitialOfferForTest(
- offerAuthorityTwo,
- wormholeSequence++,
- baseFastOrder,
- ethRouter,
- );
+ } = await placeInitialOfferForTest(offerAuthorityTwo, baseFastOrder, ethRouter);
const improveBy = Number(
await engine.computeMinOfferDelta(
@@ -1945,7 +1792,7 @@ describe("Matching Engine", function () {
connection,
initialOfferToken,
);
- const { amount: custodyTokenBefore } = await engine.fetchCctpMintRecipient();
+ const custodyTokenBefore = await engine.fetchAuctionCustodyTokenBalance(auction);
const { duration, gracePeriod } = await engine.fetchAuctionParameters();
await waitUntilSlot(
@@ -1958,7 +1805,15 @@ describe("Matching Engine", function () {
fastVaa,
});
- const txDetails = await expectIxOkDetails(connection, [ix], [offerAuthorityOne]);
+ const computeIx = ComputeBudgetProgram.setComputeUnitLimit({
+ units: 300_000,
+ });
+
+ const txDetails = await expectIxOkDetails(
+ connection,
+ [computeIx, ix],
+ [offerAuthorityOne],
+ );
await checkAfterEffects(
txDetails!,
@@ -1978,7 +1833,7 @@ describe("Matching Engine", function () {
localVariables.set("auction", auction);
});
- it("Cannot Improve Offer", async function () {
+ it("Cannot Improve Offer After Execute Order", async function () {
const auction = localVariables.get("auction") as PublicKey;
expect(localVariables.delete("auction")).is.true;
@@ -1994,7 +1849,7 @@ describe("Matching Engine", function () {
connection,
[approveIx, ix],
[offerAuthorityOne],
- "Error Code: AuctionNotActive",
+ "custody_token. Error Code: AccountNotInitialized",
);
});
@@ -2005,12 +1860,7 @@ describe("Matching Engine", function () {
fastVaa,
auction,
auctionDataBefore: initialData,
- } = await placeInitialOfferForTest(
- offerAuthorityTwo,
- wormholeSequence++,
- baseFastOrder,
- ethRouter,
- );
+ } = await placeInitialOfferForTest(offerAuthorityTwo, baseFastOrder, ethRouter);
const improveBy = Number(
await engine.computeMinOfferDelta(
@@ -2033,7 +1883,7 @@ describe("Matching Engine", function () {
connection,
initialOfferToken,
);
- const { amount: custodyTokenBefore } = await engine.fetchCctpMintRecipient();
+ const custodyTokenBefore = await engine.fetchAuctionCustodyTokenBalance(auction);
const { duration, gracePeriod, penaltyPeriod } =
await engine.fetchAuctionParameters();
@@ -2049,7 +1899,15 @@ describe("Matching Engine", function () {
fastVaa,
});
- const txDetails = await expectIxOkDetails(connection, [ix], [offerAuthorityOne]);
+ const computeIx = ComputeBudgetProgram.setComputeUnitLimit({
+ units: 300_000,
+ });
+
+ const txDetails = await expectIxOkDetails(
+ connection,
+ [computeIx, ix],
+ [offerAuthorityOne],
+ );
await checkAfterEffects(
txDetails!,
@@ -2074,12 +1932,7 @@ describe("Matching Engine", function () {
fastVaa,
auction,
auctionDataBefore: initialData,
- } = await placeInitialOfferForTest(
- offerAuthorityTwo,
- wormholeSequence++,
- baseFastOrder,
- ethRouter,
- );
+ } = await placeInitialOfferForTest(offerAuthorityTwo, baseFastOrder, ethRouter);
const improveBy = Number(
await engine.computeMinOfferDelta(
@@ -2102,7 +1955,7 @@ describe("Matching Engine", function () {
connection,
initialOfferToken,
);
- const { amount: custodyTokenBefore } = await engine.fetchCctpMintRecipient();
+ const custodyTokenBefore = await engine.fetchAuctionCustodyTokenBalance(auction);
const liquidatorToken = splToken.getAssociatedTokenAddressSync(
USDC_MINT_ADDRESS,
@@ -2131,10 +1984,14 @@ describe("Matching Engine", function () {
units: 300_000,
});
+ const { value: lookupTableAccount } = await connection.getAddressLookupTable(
+ lookupTableAddress,
+ );
const txDetails = await expectIxOkDetails(
connection,
[computeIx, ix],
[liquidator],
+ { addressLookupTableAccounts: [lookupTableAccount!] },
);
await checkAfterEffects(
@@ -2161,12 +2018,7 @@ describe("Matching Engine", function () {
fastVaa,
auction,
auctionDataBefore: initialData,
- } = await placeInitialOfferForTest(
- offerAuthorityTwo,
- wormholeSequence++,
- baseFastOrder,
- ethRouter,
- );
+ } = await placeInitialOfferForTest(offerAuthorityTwo, baseFastOrder, ethRouter);
const improveBy = Number(
await engine.computeMinOfferDelta(
@@ -2189,7 +2041,7 @@ describe("Matching Engine", function () {
connection,
initialOfferToken,
);
- const { amount: custodyTokenBefore } = await engine.fetchCctpMintRecipient();
+ const custodyTokenBefore = await engine.fetchAuctionCustodyTokenBalance(auction);
const liquidatorToken = splToken.getAssociatedTokenAddressSync(
USDC_MINT_ADDRESS,
@@ -2218,10 +2070,14 @@ describe("Matching Engine", function () {
units: 300_000,
});
+ const { value: lookupTableAccount } = await connection.getAddressLookupTable(
+ lookupTableAddress,
+ );
const txDetails = await expectIxOkDetails(
connection,
[computeIx, ix],
[liquidator],
+ { addressLookupTableAccounts: [lookupTableAccount!] },
);
await checkAfterEffects(
@@ -2248,7 +2104,6 @@ describe("Matching Engine", function () {
const { fastVaa, auctionDataBefore } = await placeInitialOfferForTest(
offerAuthorityOne,
- wormholeSequence++,
fastOrder,
ethRouter,
);
@@ -2275,7 +2130,6 @@ describe("Matching Engine", function () {
const { fastVaaAccount, auction, auctionDataBefore } =
await placeInitialOfferForTest(
offerAuthorityOne,
- wormholeSequence++,
baseFastOrder,
ethRouter,
baseFastOrder.maxFee,
@@ -2284,7 +2138,6 @@ describe("Matching Engine", function () {
const { fastVaa: anotherFastVaa, fastVaaAccount: anotherFastVaaAccount } =
await placeInitialOfferForTest(
offerAuthorityOne,
- wormholeSequence++,
baseFastOrder,
ethRouter,
baseFastOrder.maxFee,
@@ -2303,18 +2156,12 @@ describe("Matching Engine", function () {
auction,
});
- await expectIxErr(
- connection,
- [ix],
- [offerAuthorityOne],
- "account: auction. Error Code: ConstraintSeeds",
- );
+ await expectIxErr(connection, [ix], [offerAuthorityOne], "Error Code: InvalidVaa");
});
it("Cannot Execute Fast Order (Invalid Best Offer Token Account)", async function () {
const { fastVaa, auctionDataBefore } = await placeInitialOfferForTest(
offerAuthorityOne,
- wormholeSequence++,
baseFastOrder,
ethRouter,
);
@@ -2335,14 +2182,13 @@ describe("Matching Engine", function () {
connection,
[ix],
[offerAuthorityOne],
- "Error Code: BestOfferTokenMismatch",
+ "best_offer_token. Error Code: ConstraintAddress",
);
});
it("Cannot Execute Fast Order (Invalid Initial Offer Token Account)", async function () {
const { fastVaa, auctionDataBefore } = await placeInitialOfferForTest(
offerAuthorityOne,
- wormholeSequence++,
baseFastOrder,
ethRouter,
);
@@ -2363,7 +2209,7 @@ describe("Matching Engine", function () {
connection,
[ix],
[offerAuthorityOne],
- "Error Code: InitialOfferTokenMismatch",
+ "initial_offer_token. Error Code: ConstraintAddress",
);
});
@@ -2372,7 +2218,6 @@ describe("Matching Engine", function () {
// check that the initial offer is refunded.
const { fastVaa, auctionDataBefore } = await placeInitialOfferForTest(
offerAuthorityTwo,
- wormholeSequence++,
baseFastOrder,
ethRouter,
);
@@ -2401,14 +2246,13 @@ describe("Matching Engine", function () {
connection,
[ix],
[offerAuthorityOne],
- "Error Code: AuctionNotActive",
+ "custody_token. Error Code: AccountNotInitialized",
);
});
it("Cannot Execute Fast Order (Auction Period Not Expired)", async function () {
const { fastVaa } = await placeInitialOfferForTest(
offerAuthorityOne,
- wormholeSequence++,
baseFastOrder,
ethRouter,
);
@@ -2429,7 +2273,6 @@ describe("Matching Engine", function () {
it("Cannot Execute Fast Order Solana (Invalid Chain)", async function () {
const { fastVaa, fastVaaAccount } = await placeInitialOfferForTest(
offerAuthorityOne,
- wormholeSequence++,
baseFastOrder,
ethRouter,
);
@@ -2444,7 +2287,7 @@ describe("Matching Engine", function () {
}),
],
[offerAuthorityOne],
- "Error Code: ConstraintSeeds",
+ "Error Code: InvalidEndpoint",
);
});
@@ -2473,14 +2316,6 @@ describe("Matching Engine", function () {
const { bump, vaaHash, info } = auctionDataBefore;
const auctionDataAfter = await engine.fetchAuction({ address: auction });
- expect(auctionDataAfter).to.eql(
- new Auction(
- bump,
- vaaHash,
- { completed: { slot: bigintToU64BN(BigInt(txDetails.slot)) } },
- info,
- ),
- );
const { bestOfferToken, initialOfferToken, securityDeposit, amountIn, offerPrice } =
info!;
@@ -2513,6 +2348,20 @@ describe("Matching Engine", function () {
expect(penalty > 0n).is.true;
expect(userReward > 0n).is.true;
+ expect(auctionDataAfter).to.eql(
+ new Auction(
+ bump,
+ vaaHash,
+ {
+ completed: {
+ slot: bigintToU64BN(BigInt(txDetails.slot)),
+ executePenalty: bigintToU64BN(penalty),
+ },
+ },
+ info,
+ ),
+ );
+
let depositAndFee =
BigInt(offerPrice.add(securityDeposit).toString()) - userReward;
const executorToken = splToken.getAssociatedTokenAddressSync(
@@ -2543,6 +2392,20 @@ describe("Matching Engine", function () {
expect(penalty).equals(0n);
expect(userReward).equals(0n);
+ expect(auctionDataAfter).to.eql(
+ new Auction(
+ bump,
+ vaaHash,
+ {
+ completed: {
+ slot: bigintToU64BN(BigInt(txDetails.slot)),
+ executePenalty: null,
+ },
+ },
+ info,
+ ),
+ );
+
const depositAndFee = BigInt(offerPrice.add(securityDeposit).toString());
if (bestOfferToken.equals(initialOfferToken)) {
@@ -2557,10 +2420,8 @@ describe("Matching Engine", function () {
}
}
- const { amount: custodyTokenAfter } = await engine.fetchCctpMintRecipient();
- expect(custodyTokenAfter).equals(
- custodyTokenBefore - BigInt(amountIn.add(securityDeposit).toString()),
- );
+ const custodyTokenAfter = await engine.fetchAuctionCustodyTokenBalance(auction);
+ expect(custodyTokenAfter).equals(0n);
// Validate the core message.
const message = await engine.getCoreMessage(executor);
@@ -2615,7 +2476,7 @@ describe("Matching Engine", function () {
// Concoct a Circle message.
const burnSource = Array.from(Buffer.alloc(32, "beefdead", "hex"));
const { destinationCctpDomain, burnMessage, encodedCctpMessage, cctpAttestation } =
- await craftCctpTokenBurnMessage(engine, sourceCctpDomain, cctpNonce, amountIn);
+ await craftCctpTokenBurnMessage(sourceCctpDomain, cctpNonce, amountIn);
const fastMessage = new LiquidityLayerMessage({
fastMarketOrder: {
@@ -2674,7 +2535,6 @@ describe("Matching Engine", function () {
payer: payer.publicKey,
fastVaa,
finalizedVaa,
- mint: USDC_MINT_ADDRESS,
},
{
encodedCctpMessage,
@@ -2686,7 +2546,12 @@ describe("Matching Engine", function () {
units: 300_000,
});
- await expectIxErr(connection, [computeIx, ix], [payer], "Error Code: VaaMismatch");
+ const { value: lookupTableAccount } = await connection.getAddressLookupTable(
+ lookupTableAddress,
+ );
+ await expectIxErr(connection, [computeIx, ix], [payer], "Error Code: VaaMismatch", {
+ addressLookupTableAccounts: [lookupTableAccount!],
+ });
});
it("Cannot Prepare Order Response with Emitter Address Mismatch", async function () {
@@ -2699,7 +2564,7 @@ describe("Matching Engine", function () {
// Concoct a Circle message.
const burnSource = Array.from(Buffer.alloc(32, "beefdead", "hex"));
const { destinationCctpDomain, burnMessage, encodedCctpMessage, cctpAttestation } =
- await craftCctpTokenBurnMessage(engine, sourceCctpDomain, cctpNonce, amountIn);
+ await craftCctpTokenBurnMessage(sourceCctpDomain, cctpNonce, amountIn);
const fastMessage = new LiquidityLayerMessage({
fastMarketOrder: {
@@ -2757,7 +2622,6 @@ describe("Matching Engine", function () {
payer: payer.publicKey,
fastVaa,
finalizedVaa,
- mint: USDC_MINT_ADDRESS,
},
{
encodedCctpMessage,
@@ -2769,7 +2633,12 @@ describe("Matching Engine", function () {
units: 300_000,
});
- await expectIxErr(connection, [computeIx, ix], [payer], "Error Code: VaaMismatch");
+ const { value: lookupTableAccount } = await connection.getAddressLookupTable(
+ lookupTableAddress,
+ );
+ await expectIxErr(connection, [computeIx, ix], [payer], "Error Code: VaaMismatch", {
+ addressLookupTableAccounts: [lookupTableAccount!],
+ });
});
it("Cannot Prepare Order Response with Emitter Sequence Mismatch", async function () {
@@ -2782,7 +2651,7 @@ describe("Matching Engine", function () {
// Concoct a Circle message.
const burnSource = Array.from(Buffer.alloc(32, "beefdead", "hex"));
const { destinationCctpDomain, burnMessage, encodedCctpMessage, cctpAttestation } =
- await craftCctpTokenBurnMessage(engine, sourceCctpDomain, cctpNonce, amountIn);
+ await craftCctpTokenBurnMessage(sourceCctpDomain, cctpNonce, amountIn);
const fastMessage = new LiquidityLayerMessage({
fastMarketOrder: {
@@ -2840,7 +2709,6 @@ describe("Matching Engine", function () {
payer: payer.publicKey,
fastVaa,
finalizedVaa,
- mint: USDC_MINT_ADDRESS,
},
{
encodedCctpMessage,
@@ -2852,7 +2720,12 @@ describe("Matching Engine", function () {
units: 300_000,
});
- await expectIxErr(connection, [computeIx, ix], [payer], "Error Code: VaaMismatch");
+ const { value: lookupTableAccount } = await connection.getAddressLookupTable(
+ lookupTableAddress,
+ );
+ await expectIxErr(connection, [computeIx, ix], [payer], "Error Code: VaaMismatch", {
+ addressLookupTableAccounts: [lookupTableAccount!],
+ });
});
// TODO: Test timestamp mismatch
@@ -2868,7 +2741,7 @@ describe("Matching Engine", function () {
// Concoct a Circle message.
const burnSource = Array.from(Buffer.alloc(32, "beefdead", "hex"));
const { destinationCctpDomain, burnMessage, encodedCctpMessage, cctpAttestation } =
- await craftCctpTokenBurnMessage(engine, sourceCctpDomain, cctpNonce, amountIn);
+ await craftCctpTokenBurnMessage(sourceCctpDomain, cctpNonce, amountIn);
const fastMessage = new LiquidityLayerMessage({
fastMarketOrder: {
@@ -2926,7 +2799,6 @@ describe("Matching Engine", function () {
payer: payer.publicKey,
fastVaa,
finalizedVaa,
- mint: USDC_MINT_ADDRESS,
},
{
encodedCctpMessage,
@@ -2938,23 +2810,52 @@ describe("Matching Engine", function () {
units: 300_000,
});
- await expectIxOk(connection, [computeIx, ix], [payer]);
+ const { value: lookupTableAccount } = await connection.getAddressLookupTable(
+ lookupTableAddress,
+ );
+ await expectIxOk(connection, [computeIx, ix], [payer], {
+ addressLookupTableAccounts: [lookupTableAccount!],
+ });
// TODO: validate prepared slow order
const fastVaaHash = await VaaAccount.fetch(connection, fastVaa).then((vaa) =>
vaa.digest(),
);
- const preparedOrderResponse = engine.preparedOrderResponseAddress(
- payer.publicKey,
- fastVaaHash,
+ const preparedOrderResponse = engine.preparedOrderResponseAddress(fastVaaHash);
+
+ const preparedOrderResponseData = await engine.fetchPreparedOrderResponse({
+ address: preparedOrderResponse,
+ });
+ const { bump } = preparedOrderResponseData;
+
+ expect(preparedOrderResponseData).to.eql({
+ bump,
+ preparedBy: payer.publicKey,
+ fastVaaHash: Array.from(fastVaaHash),
+ sourceChain: ethChain,
+ baseFee: bigintToU64BN(
+ finalizedMessage.deposit!.message.slowOrderResponse!.baseFee,
+ ),
+ });
+
+ const { amount: preparedCustodyBalance } = await splToken.getAccount(
+ connection,
+ engine.preparedCustodyTokenAddress(preparedOrderResponse),
+ );
+ expect(preparedCustodyBalance).equals(fastMessage.fastMarketOrder!.amountIn);
+
+ const { amount: cctpMintRecipientBalance } = await splToken.getAccount(
+ connection,
+ engine.cctpMintRecipientAddress(),
);
+ expect(cctpMintRecipientBalance).equals(0n);
// Save for later.
localVariables.set("ix", ix);
localVariables.set("preparedOrderResponse", preparedOrderResponse);
});
- it("Cannot Prepare Order Response for Same VAAs", async function () {
+ it("Prepare Order Response for Same VAAs is No-op", async function () {
const ix = localVariables.get("ix") as TransactionInstruction;
expect(localVariables.delete("ix")).is.true;
@@ -2963,168 +2864,335 @@ describe("Matching Engine", function () {
) as PublicKey;
expect(localVariables.delete("preparedOrderResponse")).is.true;
- await expectIxErr(
- connection,
- [ix],
- [payer],
- `Allocate: account Address { address: ${preparedOrderResponse.toString()}, base: None } already in use`,
- );
- });
- });
-
- describe("Settle Auction", function () {
- describe("Auction Complete", function () {
- it("Cannot Settle Auction in Active Status", async function () {
- const { prepareIx, preparedOrderResponse, auction } =
- await prepareOrderResponse({
- initAuction: true,
- executeOrder: false,
- prepareOrderResponse: false,
- });
-
- const settleIx = await engine.settleAuctionCompleteIx({
- preparedOrderResponse,
- auction,
- preparedBy: payer.publicKey,
- });
-
- const { value: lookupTableAccount } = await connection.getAddressLookupTable(
- lookupTableAddress,
- );
- const computeIx = ComputeBudgetProgram.setComputeUnitLimit({
- units: 500_000,
- });
- await expectIxErr(
- connection,
- [computeIx, prepareIx!, settleIx],
- [payer],
- "Error Code: AuctionNotCompleted",
- {
- addressLookupTableAccounts: [lookupTableAccount!],
- },
- );
+ const preparedOrderResponseDataBefore = await engine.fetchPreparedOrderResponse({
+ address: preparedOrderResponse,
});
- it.skip("Prepare and Settle", async function () {
- const {
- fastVaa,
- fastVaaAccount,
- finalizedVaa,
- prepareIx,
- preparedOrderResponse,
- auction,
- } = await prepareOrderResponse({
- initAuction: true,
- executeOrder: true,
- prepareOrderResponse: false,
- });
-
- const settleIx = await engine.settleAuctionCompleteIx({
- preparedOrderResponse,
- auction,
- });
+ const { value: lookupTableAccount } = await connection.getAddressLookupTable(
+ lookupTableAddress,
+ );
+ await expectIxOk(connection, [ix], [payer], {
+ addressLookupTableAccounts: [lookupTableAccount!],
+ });
- await expectIxOk(connection, [prepareIx!, settleIx], [payer]);
+ const preparedOrderResponseDataAfter = await engine.fetchPreparedOrderResponse({
+ address: preparedOrderResponse,
});
+ expect(preparedOrderResponseDataAfter).to.eql(preparedOrderResponseDataBefore);
});
- describe("Active Auction", function () {
- it("Cannot Settle Executed Auction", async function () {
- const { auction, fastVaa, fastVaaAccount, prepareIx } =
- await prepareOrderResponse({
- executeOrder: true,
- initAuction: true,
- prepareOrderResponse: false,
- });
- expect(prepareIx).is.not.null;
+ it("Prepare Order Response for Active Auction", async function () {
+ const redeemer = Keypair.generate();
- const liquidatorToken = await splToken.getAssociatedTokenAddress(
- USDC_MINT_ADDRESS,
- liquidator.publicKey,
- );
+ const sourceCctpDomain = 0;
+ const cctpNonce = testCctpNonce++;
+ const amountIn = 690000n; // 69 cents
- const sourceCctpDomain = 0;
- const cctpNonce = testCctpNonce++;
- const amountIn = 690000n; // 69 cents
- const { encodedCctpMessage } = await craftCctpTokenBurnMessage(
- engine,
- sourceCctpDomain,
- cctpNonce,
- amountIn,
- );
+ // Concoct a Circle message.
+ const burnSource = Array.from(Buffer.alloc(32, "beefdead", "hex"));
+ const { destinationCctpDomain, burnMessage, encodedCctpMessage, cctpAttestation } =
+ await craftCctpTokenBurnMessage(sourceCctpDomain, cctpNonce, amountIn);
+
+ const fastMarketOrder = {
+ amountIn,
+ minAmountOut: 0n,
+ targetChain: arbChain,
+ redeemer: Array.from(redeemer.publicKey.toBuffer()),
+ sender: new Array(32).fill(0),
+ refundAddress: new Array(32).fill(0),
+ maxFee: 42069n,
+ initAuctionFee: 2000n,
+ deadline: 0,
+ redeemerMessage: Buffer.from("Somebody set up us the bomb"),
+ };
- const settleIx = await engine.settleAuctionActiveCctpIx(
+ const finalizedMessage = new LiquidityLayerMessage({
+ deposit: new LiquidityLayerDeposit(
{
- payer: payer.publicKey,
- fastVaa,
- fastVaaAccount,
- executorToken: liquidatorToken,
- auction,
- encodedCctpMessage,
+ tokenAddress: burnMessage.burnTokenAddress,
+ amount: amountIn,
+ sourceCctpDomain,
+ destinationCctpDomain,
+ cctpNonce,
+ burnSource,
+ mintRecipient: Array.from(engine.cctpMintRecipientAddress().toBuffer()),
},
- { targetChain: ethChain, remoteDomain: solanaChain },
- );
+ {
+ slowOrderResponse: {
+ baseFee: 420n,
+ },
+ },
+ ),
+ });
- const { value: lookupTableAccount } = await connection.getAddressLookupTable(
- lookupTableAddress,
- );
- const computeIx = ComputeBudgetProgram.setComputeUnitLimit({
- units: 500_000,
+ const finalizedVaa = await postLiquidityLayerVaa(
+ connection,
+ payer,
+ MOCK_GUARDIANS,
+ ethRouter,
+ wormholeSequence++,
+ finalizedMessage,
+ );
+
+ const { fastVaa, auction } = await placeInitialOfferForTest(
+ offerAuthorityOne,
+ fastMarketOrder,
+ ethRouter,
+ fastMarketOrder.maxFee,
+ );
+
+ const ix = await engine.prepareOrderResponseCctpIx(
+ {
+ payer: payer.publicKey,
+ fastVaa,
+ finalizedVaa,
+ },
+ {
+ encodedCctpMessage,
+ cctpAttestation,
+ },
+ true, // hasAuction
+ );
+
+ const computeIx = ComputeBudgetProgram.setComputeUnitLimit({
+ units: 300_000,
+ });
+
+ const { value: lookupTableAccount } = await connection.getAddressLookupTable(
+ lookupTableAddress,
+ );
+ await expectIxOk(connection, [computeIx, ix], [payer], {
+ addressLookupTableAccounts: [lookupTableAccount!],
+ });
+
+ // TODO: validate prepared slow order
+ const fastVaaHash = await VaaAccount.fetch(connection, fastVaa).then((vaa) =>
+ vaa.digest(),
+ );
+ const preparedOrderResponse = engine.preparedOrderResponseAddress(fastVaaHash);
+
+ const preparedOrderResponseData = await engine.fetchPreparedOrderResponse({
+ address: preparedOrderResponse,
+ });
+ const { bump } = preparedOrderResponseData;
+
+ expect(preparedOrderResponseData).to.eql({
+ bump,
+ preparedBy: payer.publicKey,
+ fastVaaHash: Array.from(fastVaaHash),
+ sourceChain: ethChain,
+ baseFee: bigintToU64BN(
+ finalizedMessage.deposit!.message.slowOrderResponse!.baseFee,
+ ),
+ });
+
+ const { amount: preparedCustodyBalance } = await splToken.getAccount(
+ connection,
+ engine.preparedCustodyTokenAddress(preparedOrderResponse),
+ );
+ expect(preparedCustodyBalance).equals(fastMarketOrder.amountIn);
+
+ const { amount: cctpMintRecipientBalance } = await splToken.getAccount(
+ connection,
+ engine.cctpMintRecipientAddress(),
+ );
+ expect(cctpMintRecipientBalance).equals(0n);
+
+ const { info } = await engine.fetchAuction({ address: auction });
+ expect(info!.endEarly).is.true;
+ });
+ });
+
+ describe("Settle Auction", function () {
+ describe("Auction Complete", function () {
+ it("Cannot Settle Active Auction", async function () {
+ const { preparedOrderResponse, auction } = await prepareOrderResponse({
+ initAuction: true,
+ executeOrder: false,
+ prepareOrderResponse: true,
+ });
+
+ const ix = await engine.settleAuctionCompleteIx({
+ executor: payer.publicKey,
+ preparedOrderResponse,
+ auction,
});
+
+ await expectIxErr(connection, [ix], [payer], "Error Code: AuctionNotCompleted");
+ });
+
+ it("Cannot Settle Completed Auction with No Penalty (Executor != Best Offer)", async function () {
+ const { preparedOrderResponse, auction } = await prepareOrderResponse({
+ initAuction: true,
+ executeOrder: true,
+ prepareOrderResponse: true,
+ });
+ const { status: statusBefore } = await engine.fetchAuction({
+ address: auction,
+ });
+ expect(statusBefore.completed!.executePenalty).is.null;
+
+ const ix = await engine.settleAuctionCompleteIx({
+ executor: payer.publicKey,
+ preparedOrderResponse,
+ auction,
+ });
+
await expectIxErr(
connection,
- [prepareIx!, settleIx, computeIx],
+ [ix],
[payer],
- "Error Code: AuctionNotActive",
- {
- addressLookupTableAccounts: [lookupTableAccount!],
- },
+ "Error Code: ExecutorTokenMismatch",
);
});
- it("Settle", async function () {
- const { auction, fastVaa, fastVaaAccount, prepareIx } =
- await prepareOrderResponse({
- executeOrder: false,
- initAuction: true,
- prepareOrderResponse: false,
- });
- expect(prepareIx).is.not.null;
+ it("Settle Completed without Penalty", async function () {
+ const { baseFee, preparedOrderResponse, auction } = await prepareOrderResponse({
+ initAuction: true,
+ executeOrder: true,
+ prepareOrderResponse: true,
+ });
- const liquidatorToken = await splToken.getAssociatedTokenAddress(
- USDC_MINT_ADDRESS,
- liquidator.publicKey,
+ const preparedCustodyToken =
+ engine.preparedCustodyTokenAddress(preparedOrderResponse);
+
+ const { amount: preparedCustodyBalanceBefore } = await splToken.getAccount(
+ connection,
+ preparedCustodyToken,
);
- const sourceCctpDomain = 0;
- const cctpNonce = testCctpNonce++;
- const amountIn = 690000n; // 69 cents
- const { encodedCctpMessage } = await craftCctpTokenBurnMessage(
- engine,
- sourceCctpDomain,
- cctpNonce,
- amountIn,
+ const { info, status: statusBefore } = await engine.fetchAuction({
+ address: auction,
+ });
+ expect(statusBefore.completed!.executePenalty).is.null;
+
+ const { bestOfferToken } = info!;
+ const { owner: bestOfferAuthority, amount: bestOfferTokenBalanceBefore } =
+ await splToken.getAccount(connection, bestOfferToken);
+
+ const authorityLamportsBefore = await connection.getBalance(bestOfferAuthority);
+
+ const preparedOrderLamports = await connection
+ .getAccountInfo(preparedOrderResponse)
+ .then((info) => info!.lamports);
+ const preparedCustodyLamports = await connection
+ .getAccountInfo(preparedCustodyToken)
+ .then((info) => info!.lamports);
+
+ const ix = await engine.settleAuctionCompleteIx({
+ executor: bestOfferAuthority,
+ preparedOrderResponse,
+ });
+
+ await expectIxOk(connection, [ix], [payer]);
+
+ const preparedCustodyInfo = await connection.getAccountInfo(
+ preparedCustodyToken,
);
- const settleIx = await engine.settleAuctionActiveCctpIx(
- {
- payer: payer.publicKey,
- fastVaa,
- fastVaaAccount,
- executorToken: liquidatorToken,
- auction,
- encodedCctpMessage,
+ expect(preparedCustodyInfo).is.null;
+ const preparedOrderResponseInfo = await connection.getAccountInfo(
+ preparedOrderResponse,
+ );
+ expect(preparedOrderResponseInfo).is.null;
+
+ const { amount: bestOfferTokenBalanceAfter } = await splToken.getAccount(
+ connection,
+ bestOfferToken,
+ );
+ expect(bestOfferTokenBalanceAfter).equals(
+ preparedCustodyBalanceBefore + bestOfferTokenBalanceBefore,
+ );
+
+ const authorityLamportsAfter = await connection.getBalance(bestOfferAuthority);
+ expect(authorityLamportsAfter).equals(
+ authorityLamportsBefore + preparedOrderLamports + preparedCustodyLamports,
+ );
+
+ const { status: statusAfter } = await engine.fetchAuction({
+ address: auction,
+ });
+ expect(statusAfter).to.eql({
+ settled: {
+ baseFee: bigintToU64BN(baseFee),
+ totalPenalty: null,
},
- { targetChain: ethChain, remoteDomain: solanaChain },
+ });
+ });
+
+ it("Settle Completed with Penalty", async function () {
+ const { baseFee, preparedOrderResponse, auction } = await prepareOrderResponse({
+ initAuction: true,
+ executeOrder: true,
+ prepareOrderResponse: true,
+ executeLate: true,
+ });
+
+ const preparedCustodyToken =
+ engine.preparedCustodyTokenAddress(preparedOrderResponse);
+
+ const { amount: preparedCustodyBalanceBefore } = await splToken.getAccount(
+ connection,
+ preparedCustodyToken,
);
- const { value: lookupTableAccount } = await connection.getAddressLookupTable(
- lookupTableAddress,
+ const { info, status: statusBefore } = await engine.fetchAuction({
+ address: auction,
+ });
+ const executePenalty = statusBefore.completed!.executePenalty;
+ expect(executePenalty).is.not.null;
+
+ const { bestOfferToken } = info!;
+ const { owner: bestOfferAuthority, amount: bestOfferTokenBalanceBefore } =
+ await splToken.getAccount(connection, bestOfferToken);
+
+ const authorityLamportsBefore = await connection.getBalance(bestOfferAuthority);
+
+ const preparedOrderLamports = await connection
+ .getAccountInfo(preparedOrderResponse)
+ .then((info) => info!.lamports);
+ const preparedCustodyLamports = await connection
+ .getAccountInfo(preparedCustodyToken)
+ .then((info) => info!.lamports);
+
+ const ix = await engine.settleAuctionCompleteIx({
+ executor: bestOfferAuthority,
+ preparedOrderResponse,
+ });
+
+ await expectIxOk(connection, [ix], [payer]);
+
+ const preparedCustodyInfo = await connection.getAccountInfo(
+ preparedCustodyToken,
);
- const computeIx = ComputeBudgetProgram.setComputeUnitLimit({
- units: 500_000,
+ expect(preparedCustodyInfo).is.null;
+ const preparedOrderResponseInfo = await connection.getAccountInfo(
+ preparedOrderResponse,
+ );
+ expect(preparedOrderResponseInfo).is.null;
+
+ const { amount: bestOfferTokenBalanceAfter } = await splToken.getAccount(
+ connection,
+ bestOfferToken,
+ );
+ expect(bestOfferTokenBalanceAfter).equals(
+ preparedCustodyBalanceBefore + bestOfferTokenBalanceBefore,
+ );
+
+ const authorityLamportsAfter = await connection.getBalance(bestOfferAuthority);
+ expect(authorityLamportsAfter).equals(
+ authorityLamportsBefore + preparedOrderLamports + preparedCustodyLamports,
+ );
+
+ const { status: statusAfter } = await engine.fetchAuction({
+ address: auction,
});
- await expectIxOk(connection, [prepareIx!, settleIx, computeIx], [payer], {
- addressLookupTableAccounts: [lookupTableAccount!],
+ expect(statusAfter).to.eql({
+ settled: {
+ baseFee: bigintToU64BN(baseFee),
+ totalPenalty: bigintToU64BN(
+ BigInt(executePenalty!.toString()) + baseFee,
+ ),
+ },
});
});
});
@@ -3154,7 +3222,12 @@ describe("Matching Engine", function () {
connection,
feeRecipientToken,
);
- const { amount: custodyBalanceBefore } = await engine.fetchCctpMintRecipient();
+ const preparedCustodyToken =
+ engine.preparedCustodyTokenAddress(preparedOrderResponse);
+ const { amount: custodyBalanceBefore } = await splToken.getAccount(
+ connection,
+ preparedCustodyToken,
+ );
await expectIxOk(connection, [computeIx, settleIx], [payer]);
@@ -3168,13 +3241,23 @@ describe("Matching Engine", function () {
);
expect(feeBalanceAfter).equals(feeBalanceBefore + baseFee);
- const { amount } = deposit.header;
- const { amount: custodyBalanceAfter } = await engine.fetchCctpMintRecipient();
- expect(custodyBalanceAfter).equals(custodyBalanceBefore - amount);
+ const { amount: custodyBalanceAfter } = await splToken.getAccount(
+ connection,
+ preparedCustodyToken,
+ );
+ expect(custodyBalanceAfter).equals(
+ custodyBalanceBefore - deposit.header.amount,
+ );
+
+ const { amount: cctpMintRecipientBalance } =
+ await engine.fetchCctpMintRecipient();
+ expect(cctpMintRecipientBalance).equals(0n);
const fastVaaHash = fastVaaAccount.digest();
const auctionData = await engine.fetchAuction(fastVaaHash);
- const { bump } = auctionData;
+ const { bump, info } = auctionData;
+ expect(info).is.null;
+
expect(auctionData).to.eql(
new Auction(
bump,
@@ -3182,7 +3265,7 @@ describe("Matching Engine", function () {
{
settled: {
baseFee: bigintToU64BN(baseFee),
- penalty: null,
+ totalPenalty: null,
},
},
null,
@@ -3190,167 +3273,15 @@ describe("Matching Engine", function () {
);
});
});
-
- async function prepareOrderResponse(args: {
- initAuction: boolean;
- executeOrder: boolean;
- prepareOrderResponse: boolean;
- }) {
- const { initAuction, executeOrder, prepareOrderResponse } = args;
-
- const redeemer = Keypair.generate();
- const sourceCctpDomain = 0;
- const cctpNonce = testCctpNonce++;
- const amountIn = 690000n; // 69 cents
-
- // Concoct a Circle message.
- const burnSource = Array.from(Buffer.alloc(32, "beefdead", "hex"));
- const { destinationCctpDomain, burnMessage, encodedCctpMessage, cctpAttestation } =
- await craftCctpTokenBurnMessage(engine, sourceCctpDomain, cctpNonce, amountIn);
-
- const maxFee = 42069n;
- const currTime = await connection.getBlockTime(await connection.getSlot());
- const fastMessage = new LiquidityLayerMessage({
- fastMarketOrder: {
- amountIn,
- minAmountOut: 0n,
- targetChain: arbChain,
- redeemer: Array.from(redeemer.publicKey.toBuffer()),
- sender: new Array(32).fill(0),
- refundAddress: new Array(32).fill(0),
- maxFee,
- initAuctionFee: 2000n,
- deadline: currTime! + 2,
- redeemerMessage: Buffer.from("Somebody set up us the bomb"),
- },
- });
-
- const finalizedMessage = new LiquidityLayerMessage({
- deposit: new LiquidityLayerDeposit(
- {
- tokenAddress: burnMessage.burnTokenAddress,
- amount: amountIn,
- sourceCctpDomain,
- destinationCctpDomain,
- cctpNonce,
- burnSource,
- mintRecipient: Array.from(engine.cctpMintRecipientAddress().toBuffer()),
- },
- {
- slowOrderResponse: {
- baseFee: 420n,
- },
- },
- ),
- });
-
- const finalizedVaa = await postLiquidityLayerVaa(
- connection,
- payer,
- MOCK_GUARDIANS,
- ethRouter,
- wormholeSequence++,
- finalizedMessage,
- );
- const finalizedVaaAccount = await VaaAccount.fetch(connection, finalizedVaa);
-
- const fastVaa = await postLiquidityLayerVaa(
- connection,
- payer,
- MOCK_GUARDIANS,
- ethRouter,
- wormholeSequence++,
- fastMessage,
- );
- const fastVaaAccount = await VaaAccount.fetch(connection, fastVaa);
-
- const prepareIx = await engine.prepareOrderResponseCctpIx(
- {
- payer: payer.publicKey,
- fastVaa,
- finalizedVaa,
- },
- {
- encodedCctpMessage,
- cctpAttestation,
- },
- );
-
- const fastVaaHash = fastVaaAccount.digest();
- const preparedBy = payer.publicKey;
- const preparedOrderResponse = engine.preparedOrderResponseAddress(
- preparedBy,
- fastVaaHash,
- );
- const auction = engine.auctionAddress(fastVaaHash);
-
- if (initAuction) {
- const [approveIx, ix] = await engine.placeInitialOfferIx(
- {
- payer: offerAuthorityOne.publicKey,
- fastVaa,
- },
- maxFee,
- );
- await expectIxOk(connection, [approveIx, ix], [offerAuthorityOne]);
-
- if (executeOrder) {
- const { info } = await engine.fetchAuction({ address: auction });
- if (info === null) {
- throw new Error("No auction info found");
- }
- const { configId, bestOfferToken, initialOfferToken, startSlot } = info;
- const auctionConfig = engine.auctionConfigAddress(configId);
- const duration = (await engine.fetchAuctionConfig(configId)).parameters
- .duration;
-
- await new Promise((f) =>
- setTimeout(f, startSlot.toNumber() + duration + 200),
- );
-
- const computeIx = ComputeBudgetProgram.setComputeUnitLimit({
- units: 300_000,
- });
- const ix = await engine.executeFastOrderCctpIx({
- payer: payer.publicKey,
- fastVaa,
- auction,
- auctionConfig,
- bestOfferToken,
- initialOfferToken,
- });
- await expectIxOk(connection, [computeIx, ix], [payer]);
- }
- }
-
- if (prepareOrderResponse) {
- const computeIx = ComputeBudgetProgram.setComputeUnitLimit({
- units: 300_000,
- });
- await expectIxOk(connection, [computeIx, prepareIx], [payer]);
- }
-
- return {
- fastVaa,
- fastVaaAccount,
- finalizedVaa,
- finalizedVaaAccount,
- prepareIx: prepareOrderResponse ? null : prepareIx,
- preparedOrderResponse,
- auction,
- preparedBy,
- };
- }
});
});
async function placeInitialOfferForTest(
offerAuthority: Keypair,
- sequence: bigint,
fastMarketOrder: FastMarketOrder,
emitter: number[],
- feeOffer?: bigint,
- chainName?: wormholeSdk.ChainName,
+ offerPrice?: bigint,
+ sourceChain?: wormholeSdk.ChainName,
): Promise<{
fastVaa: PublicKey;
fastVaaAccount: VaaAccount;
@@ -3358,35 +3289,95 @@ describe("Matching Engine", function () {
auction: PublicKey;
auctionDataBefore: Auction;
}> {
- const fastVaa = await postLiquidityLayerVaa(
+ const {
+ fast: { vaa: fastVaa, vaaAccount: fastVaaAccount },
+ } = await observeCctpOrderVaas({
+ sourceChain,
+ fastMarketOrder,
+ });
+ offerPrice = offerPrice ?? fastMarketOrder.maxFee;
+
+ const offerToken = splToken.getAssociatedTokenAddressSync(
+ USDC_MINT_ADDRESS,
+ offerAuthority.publicKey,
+ );
+
+ const { amount: offerTokenBalanceBefore } = await splToken.getAccount(
connection,
- offerAuthority,
- MOCK_GUARDIANS,
- emitter,
- sequence,
- new LiquidityLayerMessage({ fastMarketOrder }),
- chainName,
+ offerToken,
);
+ const auction = engine.auctionAddress(fastVaaAccount.digest());
+ const auctionCustodyBalanceBefore = await engine.fetchAuctionCustodyTokenBalance(auction);
+
// Place the initial offer.
const [approveIx, ix] = await engine.placeInitialOfferIx(
{
payer: offerAuthority.publicKey,
fastVaa,
},
- feeOffer ?? fastMarketOrder.maxFee,
+ offerPrice,
);
const txDetails = await expectIxOkDetails(connection, [approveIx, ix], [offerAuthority]);
if (txDetails === null) {
throw new Error("Transaction details is null");
}
-
- const fastVaaAccount = await VaaAccount.fetch(connection, fastVaa);
- const auction = engine.auctionAddress(fastVaaAccount.digest());
const auctionDataBefore = await engine.fetchAuction({ address: auction });
- return { fastVaa, fastVaaAccount, txDetails, auction, auctionDataBefore };
+ // Validate balance changes.
+ const { amount: offerTokenBalanceAfter } = await splToken.getAccount(
+ connection,
+ offerToken,
+ );
+
+ const auctionCustodyBalanceAfter = await engine.fetchAuctionCustodyTokenBalance(auction);
+ const balanceChange = fastMarketOrder.amountIn + fastMarketOrder.maxFee;
+
+ expect(offerTokenBalanceAfter).equals(offerTokenBalanceBefore - balanceChange);
+ expect(auctionCustodyBalanceAfter).equals(auctionCustodyBalanceBefore + balanceChange);
+
+ // Confirm the auction data.
+ const vaaHash = fastVaaAccount.digest();
+ const auctionData = await engine.fetchAuction(vaaHash);
+ const { bump, info } = auctionData;
+ const { custodyTokenBump } = info!;
+
+ const { auctionConfigId } = await engine.fetchCustodian();
+
+ expect(fastMarketOrder).is.not.undefined;
+ const { amountIn, maxFee } = fastMarketOrder!;
+
+ const expectedAmountIn = bigintToU64BN(amountIn);
+ expect(auctionData).to.eql(
+ new Auction(
+ bump,
+ Array.from(vaaHash),
+ { active: {} },
+ {
+ configId: auctionConfigId,
+ custodyTokenBump,
+ vaaSequence: bigintToU64BN(fastVaaAccount.emitterInfo().sequence),
+ sourceChain: ethChain,
+ bestOfferToken: offerToken,
+ initialOfferToken: offerToken,
+ startSlot: numberToU64BN(txDetails.slot),
+ amountIn: expectedAmountIn,
+ securityDeposit: bigintToU64BN(maxFee),
+ offerPrice: bigintToU64BN(offerPrice),
+ amountOut: expectedAmountIn,
+ endEarly: false,
+ },
+ ),
+ );
+
+ return {
+ fastVaa,
+ fastVaaAccount,
+ txDetails,
+ auction,
+ auctionDataBefore,
+ };
}
async function improveOfferForTest(
@@ -3415,53 +3406,365 @@ describe("Matching Engine", function () {
auctionDataBefore,
};
}
-});
-async function craftCctpTokenBurnMessage(
- engine: MatchingEngineProgram,
- sourceCctpDomain: number,
- cctpNonce: bigint,
- amount: bigint,
- overrides: { destinationCctpDomain?: number } = {},
-) {
- const { destinationCctpDomain: inputDestinationCctpDomain } = overrides;
-
- const messageTransmitterProgram = engine.messageTransmitterProgram();
- const { version, localDomain } = await messageTransmitterProgram.fetchMessageTransmitterConfig(
- messageTransmitterProgram.messageTransmitterConfigAddress(),
- );
- const destinationCctpDomain = inputDestinationCctpDomain ?? localDomain;
+ async function prepareOrderResponse(args: {
+ initAuction: boolean;
+ executeOrder: boolean;
+ prepareOrderResponse: boolean;
+ executeLate?: boolean;
+ }) {
+ const baseFee = 420n;
+
+ const {
+ initAuction,
+ executeOrder,
+ prepareOrderResponse,
+ executeLate: inputExecuteLate,
+ } = args;
+
+ const executeLate = inputExecuteLate ?? false;
+
+ const redeemer = Keypair.generate();
+ const sourceCctpDomain = 0;
+ const cctpNonce = testCctpNonce++;
+ const amountIn = 690000n; // 69 cents
+
+ // Concoct a Circle message.
+ const burnSource = Array.from(Buffer.alloc(32, "beefdead", "hex"));
+ const { destinationCctpDomain, burnMessage, encodedCctpMessage, cctpAttestation } =
+ await craftCctpTokenBurnMessage(sourceCctpDomain, cctpNonce, amountIn);
+
+ const maxFee = 42069n;
+ const currTime = await connection.getBlockTime(await connection.getSlot());
+ const fastMessage = new LiquidityLayerMessage({
+ fastMarketOrder: {
+ amountIn,
+ minAmountOut: 0n,
+ targetChain: arbChain,
+ redeemer: Array.from(redeemer.publicKey.toBuffer()),
+ sender: new Array(32).fill(0),
+ refundAddress: new Array(32).fill(0),
+ maxFee,
+ initAuctionFee: 2000n,
+ deadline: currTime! + 2,
+ redeemerMessage: Buffer.from("Somebody set up us the bomb"),
+ },
+ });
- const tokenMessengerMinterProgram = engine.tokenMessengerMinterProgram();
- const { tokenMessenger: sourceTokenMessenger } =
- await tokenMessengerMinterProgram.fetchRemoteTokenMessenger(
- tokenMessengerMinterProgram.remoteTokenMessengerAddress(sourceCctpDomain),
+ const finalizedMessage = new LiquidityLayerMessage({
+ deposit: new LiquidityLayerDeposit(
+ {
+ tokenAddress: burnMessage.burnTokenAddress,
+ amount: amountIn,
+ sourceCctpDomain,
+ destinationCctpDomain,
+ cctpNonce,
+ burnSource,
+ mintRecipient: Array.from(engine.cctpMintRecipientAddress().toBuffer()),
+ },
+ {
+ slowOrderResponse: {
+ baseFee,
+ },
+ },
+ ),
+ });
+
+ const finalizedVaa = await postLiquidityLayerVaa(
+ connection,
+ payer,
+ MOCK_GUARDIANS,
+ ethRouter,
+ wormholeSequence++,
+ finalizedMessage,
);
+ const finalizedVaaAccount = await VaaAccount.fetch(connection, finalizedVaa);
- const burnMessage = new CctpTokenBurnMessage(
- {
- version,
- sourceDomain: sourceCctpDomain,
- destinationDomain: destinationCctpDomain,
- nonce: cctpNonce,
- sender: sourceTokenMessenger,
- recipient: Array.from(tokenMessengerMinterProgram.ID.toBuffer()), // targetTokenMessenger
- targetCaller: Array.from(engine.custodianAddress().toBuffer()), // targetCaller
- },
- 0,
- Array.from(wormholeSdk.tryNativeToUint8Array(ETHEREUM_USDC_ADDRESS, "ethereum")), // sourceTokenAddress
- Array.from(engine.cctpMintRecipientAddress().toBuffer()), // mint recipient
- amount,
- new Array(32).fill(0), // burnSource
- );
+ const fastVaa = await postLiquidityLayerVaa(
+ connection,
+ payer,
+ MOCK_GUARDIANS,
+ ethRouter,
+ wormholeSequence++,
+ fastMessage,
+ );
+ const fastVaaAccount = await VaaAccount.fetch(connection, fastVaa);
- const encodedCctpMessage = burnMessage.encode();
- const cctpAttestation = new CircleAttester().createAttestation(encodedCctpMessage);
+ const prepareIx = await engine.prepareOrderResponseCctpIx(
+ {
+ payer: payer.publicKey,
+ fastVaa,
+ finalizedVaa,
+ },
+ {
+ encodedCctpMessage,
+ cctpAttestation,
+ },
+ );
- return {
- destinationCctpDomain,
- burnMessage,
- encodedCctpMessage,
- cctpAttestation,
- };
-}
+ const fastVaaHash = fastVaaAccount.digest();
+ const preparedBy = payer.publicKey;
+ const preparedOrderResponse = engine.preparedOrderResponseAddress(fastVaaHash);
+ const auction = engine.auctionAddress(fastVaaHash);
+
+ if (initAuction) {
+ const [approveIx, ix] = await engine.placeInitialOfferIx(
+ {
+ payer: offerAuthorityOne.publicKey,
+ fastVaa,
+ },
+ maxFee,
+ );
+ await expectIxOk(connection, [approveIx, ix], [offerAuthorityOne]);
+
+ if (executeOrder) {
+ const { info } = await engine.fetchAuction({ address: auction });
+ if (info === null) {
+ throw new Error("No auction info found");
+ }
+ const { configId, bestOfferToken, initialOfferToken, startSlot } = info;
+ const auctionConfig = engine.auctionConfigAddress(configId);
+ const { duration, gracePeriod, penaltyPeriod } =
+ await engine.fetchAuctionParameters(configId);
+
+ const endSlot = (() => {
+ if (executeLate) {
+ return startSlot
+ .addn(duration + gracePeriod + penaltyPeriod - 1)
+ .toNumber();
+ } else {
+ return startSlot.addn(duration + gracePeriod - 1).toNumber();
+ }
+ })();
+
+ await waitUntilSlot(connection, endSlot);
+
+ const computeIx = ComputeBudgetProgram.setComputeUnitLimit({
+ units: 300_000,
+ });
+ const ix = await engine.executeFastOrderCctpIx({
+ payer: payer.publicKey,
+ fastVaa,
+ auction,
+ auctionConfig,
+ bestOfferToken,
+ initialOfferToken,
+ });
+ await expectIxOk(connection, [computeIx, ix], [payer]);
+ }
+ }
+
+ if (prepareOrderResponse) {
+ const computeIx = ComputeBudgetProgram.setComputeUnitLimit({
+ units: 300_000,
+ });
+ const { value: lookupTableAccount } = await connection.getAddressLookupTable(
+ lookupTableAddress,
+ );
+ await expectIxOk(connection, [computeIx, prepareIx], [payer], {
+ addressLookupTableAccounts: [lookupTableAccount!],
+ });
+ }
+
+ return {
+ baseFee,
+ fastMessage,
+ fastVaa,
+ fastVaaAccount,
+ finalizedVaa,
+ finalizedVaaAccount,
+ prepareIx: prepareOrderResponse ? null : prepareIx,
+ preparedOrderResponse,
+ auction,
+ preparedBy,
+ };
+ }
+
+ function newFastMarketOrder(
+ args: {
+ amountIn?: bigint;
+ minAmountOut?: bigint;
+ initAuctionFee?: bigint;
+ targetChain?: wormholeSdk.ChainName;
+ maxFee?: bigint;
+ deadline?: number;
+ redeemerMessage?: Buffer;
+ } = {},
+ ): FastMarketOrder {
+ const {
+ amountIn,
+ targetChain,
+ minAmountOut,
+ maxFee,
+ initAuctionFee,
+ deadline,
+ redeemerMessage,
+ } = args;
+
+ return {
+ amountIn: amountIn ?? 1_000_000_000n,
+ minAmountOut: minAmountOut ?? 0n,
+ targetChain: wormholeSdk.coalesceChainId(targetChain ?? "arbitrum"),
+ redeemer: new Array(32).fill(1),
+ sender: new Array(32).fill(2),
+ refundAddress: new Array(32).fill(3),
+ maxFee: maxFee ?? 42069n,
+ initAuctionFee: initAuctionFee ?? 1_250_000n,
+ deadline: deadline ?? 0,
+ redeemerMessage: redeemerMessage ?? Buffer.from("Somebody set up us the bomb"),
+ };
+ }
+
+ function newSlowOrderResponse(args: { baseFee?: bigint } = {}): SlowOrderResponse {
+ const { baseFee } = args;
+
+ return {
+ baseFee: baseFee ?? 420n,
+ };
+ }
+
+ async function observeCctpOrderVaas(
+ args: {
+ sourceChain?: wormholeSdk.ChainName;
+ fastMarketOrder?: FastMarketOrder;
+ slowOrderResponse?: SlowOrderResponse;
+ finalized?: boolean;
+ } = {},
+ ): Promise<{
+ fast: {
+ vaa: PublicKey;
+ vaaAccount: VaaAccount;
+ };
+ finalized?: {
+ vaa: PublicKey;
+ vaaAccount: VaaAccount;
+ encodedCctpMessage: Buffer;
+ cctpAttestation: Buffer;
+ };
+ }> {
+ let { sourceChain, fastMarketOrder, slowOrderResponse, finalized } = args;
+ sourceChain ??= "ethereum";
+ fastMarketOrder ??= newFastMarketOrder();
+ slowOrderResponse ??= newSlowOrderResponse();
+ finalized ??= false;
+
+ const sourceCctpDomain = CHAIN_TO_DOMAIN[sourceChain];
+ if (sourceCctpDomain === undefined) {
+ throw new Error(`Invalid source chain: ${sourceChain}`);
+ }
+
+ // TODO: consider taking this out this condition when other test methods use this method to
+ // generate VAAs.
+ const finalizedSequence = finalized ? wormholeSequence++ : 0n;
+ const fastSequence = wormholeSequence++;
+
+ const fastVaa = await postLiquidityLayerVaa(
+ connection,
+ payer,
+ MOCK_GUARDIANS,
+ ethRouter,
+ fastSequence,
+ new LiquidityLayerMessage({
+ fastMarketOrder,
+ }),
+ );
+ const fastVaaAccount = await VaaAccount.fetch(connection, fastVaa);
+ const fast = { vaa: fastVaa, vaaAccount: fastVaaAccount };
+
+ if (finalized) {
+ const { amountIn: amount } = fastMarketOrder;
+ const cctpNonce = testCctpNonce++;
+
+ // Concoct a Circle message.
+ const { destinationCctpDomain, burnMessage, encodedCctpMessage, cctpAttestation } =
+ await craftCctpTokenBurnMessage(sourceCctpDomain, cctpNonce, amount);
+
+ const finalizedMessage = new LiquidityLayerMessage({
+ deposit: new LiquidityLayerDeposit(
+ {
+ tokenAddress: burnMessage.burnTokenAddress,
+ amount,
+ sourceCctpDomain,
+ destinationCctpDomain,
+ cctpNonce,
+ burnSource: Array.from(Buffer.alloc(32, "beefdead", "hex")),
+ mintRecipient: Array.from(engine.cctpMintRecipientAddress().toBuffer()),
+ },
+ {
+ slowOrderResponse,
+ },
+ ),
+ });
+
+ const finalizedVaa = await postLiquidityLayerVaa(
+ connection,
+ payer,
+ MOCK_GUARDIANS,
+ ethRouter,
+ finalizedSequence,
+ finalizedMessage,
+ );
+ const finalizedVaaAccount = await VaaAccount.fetch(connection, finalizedVaa);
+ return {
+ fast,
+ finalized: {
+ vaa: finalizedVaa,
+ vaaAccount: finalizedVaaAccount,
+ encodedCctpMessage,
+ cctpAttestation,
+ },
+ };
+ } else {
+ return { fast };
+ }
+ }
+
+ async function craftCctpTokenBurnMessage(
+ sourceCctpDomain: number,
+ cctpNonce: bigint,
+ amount: bigint,
+ overrides: { destinationCctpDomain?: number } = {},
+ ) {
+ const { destinationCctpDomain: inputDestinationCctpDomain } = overrides;
+
+ const messageTransmitterProgram = engine.messageTransmitterProgram();
+ const { version, localDomain } =
+ await messageTransmitterProgram.fetchMessageTransmitterConfig(
+ messageTransmitterProgram.messageTransmitterConfigAddress(),
+ );
+ const destinationCctpDomain = inputDestinationCctpDomain ?? localDomain;
+
+ const tokenMessengerMinterProgram = engine.tokenMessengerMinterProgram();
+ const { tokenMessenger: sourceTokenMessenger } =
+ await tokenMessengerMinterProgram.fetchRemoteTokenMessenger(
+ tokenMessengerMinterProgram.remoteTokenMessengerAddress(sourceCctpDomain),
+ );
+
+ const burnMessage = new CctpTokenBurnMessage(
+ {
+ version,
+ sourceDomain: sourceCctpDomain,
+ destinationDomain: destinationCctpDomain,
+ nonce: cctpNonce,
+ sender: sourceTokenMessenger,
+ recipient: Array.from(tokenMessengerMinterProgram.ID.toBuffer()), // targetTokenMessenger
+ targetCaller: Array.from(engine.custodianAddress().toBuffer()), // targetCaller
+ },
+ 0,
+ Array.from(wormholeSdk.tryNativeToUint8Array(ETHEREUM_USDC_ADDRESS, "ethereum")), // sourceTokenAddress
+ Array.from(engine.cctpMintRecipientAddress().toBuffer()), // mint recipient
+ amount,
+ new Array(32).fill(0), // burnSource
+ );
+
+ const encodedCctpMessage = burnMessage.encode();
+ const cctpAttestation = new CircleAttester().createAttestation(encodedCctpMessage);
+
+ return {
+ destinationCctpDomain,
+ burnMessage,
+ encodedCctpMessage,
+ cctpAttestation,
+ };
+ }
+});
diff --git a/solana/ts/tests/04__interaction.ts b/solana/ts/tests/04__interaction.ts
index b47594429..82156b0b2 100644
--- a/solana/ts/tests/04__interaction.ts
+++ b/solana/ts/tests/04__interaction.ts
@@ -34,6 +34,7 @@ import {
bigintToU64BN,
expectIxErr,
expectIxOk,
+ expectIxOkDetails,
postLiquidityLayerVaa,
waitUntilSlot,
} from "./helpers";
@@ -286,7 +287,7 @@ describe("Matching Engine <> Token Router", function () {
let wormholeSequence = 4000n;
- describe("Settle Auction", function () {
+ describe.skip("Settle Auction", function () {
describe("Settle No Auction (Local)", function () {
it("Settle", async function () {
const { prepareIx, auction, fastVaa, finalizedVaa } =
@@ -400,12 +401,9 @@ describe("Matching Engine <> Token Router", function () {
"Error Code: AuctionPeriodNotExpired",
);
});
+
it("Execute after Auction Period has Expired", async function () {
- const {
- prepareIx,
- auction: auctionAddress,
- fastVaa,
- } = await prepareOrderResponse({
+ const { fastMarketOrder, auction, fastVaa } = await prepareOrderResponse({
initAuction: true,
executeOrder: false,
prepareOrderResponse: false,
@@ -418,31 +416,43 @@ describe("Matching Engine <> Token Router", function () {
liquidator.publicKey,
);
- const auction = await matchingEngine.fetchAuction({ address: auctionAddress });
+ const { info } = await matchingEngine.fetchAuction({ address: auction });
const { duration, gracePeriod } = await matchingEngine.fetchAuctionParameters();
await waitUntilSlot(
connection,
- auction.info!.startSlot.addn(duration + gracePeriod - 1).toNumber(),
+ info!.startSlot.addn(duration + gracePeriod - 1).toNumber(),
);
- const settleIx = await matchingEngine.executeFastOrderLocalIx({
+ const auctionCustodyTokenBalanceBefore =
+ await matchingEngine.fetchAuctionCustodyTokenBalance(auction);
+ const localCustodyTokenBalanceBefore =
+ await matchingEngine.fetchLocalCustodyTokenBalance(foreignChain);
+ expect(localCustodyTokenBalanceBefore).equals(0n);
+
+ const ix = await matchingEngine.executeFastOrderLocalIx({
payer: payer.publicKey,
fastVaa,
- auction: auctionAddress,
+ auction,
executorToken,
});
- const { value: lookupTableAccount } = await connection.getAddressLookupTable(
- lookupTableAddress,
- );
+ const txDetails = await expectIxOkDetails(connection, [ix], [payer]);
- const computeIx = ComputeBudgetProgram.setComputeUnitLimit({
- units: 400_000,
- });
- await expectIxOk(connection, [prepareIx!, settleIx, computeIx], [payer], {
- addressLookupTableAccounts: [lookupTableAccount!],
- });
+ const auctionCustodyTokenBalanceAfter =
+ await matchingEngine.fetchAuctionCustodyTokenBalance(auction);
+ expect(auctionCustodyTokenBalanceAfter).equals(0n);
+ const localCustodyTokenBalanceAfter =
+ await matchingEngine.fetchLocalCustodyTokenBalance(foreignChain);
+
+ const { penalty, userReward } = await matchingEngine.computeDepositPenalty(
+ info!,
+ BigInt(txDetails!.slot),
+ info!.configId,
+ );
+ const { amountIn, maxFee: offerPrice, initAuctionFee } = fastMarketOrder;
+ const userAmount = amountIn - offerPrice - initAuctionFee + userReward;
+ expect(localCustodyTokenBalanceAfter).equals(userAmount);
});
before("Update Local Router Endpoint", async function () {
@@ -754,10 +764,32 @@ describe("Matching Engine <> Token Router", function () {
"solana",
);
- const ix = await tokenRouter.redeemFastFillIx({
- payer: payer.publicKey,
- vaa,
- });
+ const {
+ custodian,
+ preparedFill,
+ matchingEngineCustodian,
+ matchingEngineRedeemedFastFill,
+ matchingEngineRouterEndpoint,
+ matchingEngineLocalCustodyToken,
+ matchingEngineProgram,
+ } = await tokenRouter.redeemFastFillAccounts(vaa, foreignChain);
+
+ const ix = await tokenRouter.program.methods
+ .redeemFastFill()
+ .accounts({
+ payer: payer.publicKey,
+ custodian,
+ vaa,
+ preparedFill,
+ preparedCustodyToken: tokenRouter.preparedCustodyTokenAddress(preparedFill),
+ mint: tokenRouter.mint,
+ matchingEngineCustodian,
+ matchingEngineRedeemedFastFill,
+ matchingEngineRouterEndpoint,
+ matchingEngineLocalCustodyToken,
+ matchingEngineProgram,
+ })
+ .instruction();
await expectIxErr(connection, [ix], [payer], "Error Code: InvalidVaa");
});
@@ -795,10 +827,33 @@ describe("Matching Engine <> Token Router", function () {
message,
"solana",
);
- const ix = await tokenRouter.redeemFastFillIx({
- payer: payer.publicKey,
- vaa,
- });
+
+ const {
+ custodian,
+ preparedFill,
+ matchingEngineCustodian,
+ matchingEngineRedeemedFastFill,
+ matchingEngineRouterEndpoint,
+ matchingEngineLocalCustodyToken,
+ matchingEngineProgram,
+ } = await tokenRouter.redeemFastFillAccounts(vaa, foreignChain);
+
+ const ix = await tokenRouter.program.methods
+ .redeemFastFill()
+ .accounts({
+ payer: payer.publicKey,
+ custodian,
+ vaa,
+ preparedFill,
+ preparedCustodyToken: tokenRouter.preparedCustodyTokenAddress(preparedFill),
+ mint: tokenRouter.mint,
+ matchingEngineCustodian,
+ matchingEngineRedeemedFastFill,
+ matchingEngineRouterEndpoint,
+ matchingEngineLocalCustodyToken,
+ matchingEngineProgram,
+ })
+ .instruction();
await expectIxErr(connection, [ix], [payer], "Error Code: InvalidPayloadId");
});
@@ -828,19 +883,20 @@ describe("Matching Engine <> Token Router", function () {
const maxFee = 42069n;
const currTime = await connection.getBlockTime(await connection.getSlot());
+ const fastMarketOrder = {
+ amountIn,
+ minAmountOut: 0n,
+ targetChain: wormholeSdk.CHAINS.solana,
+ redeemer: Array.from(redeemer.publicKey.toBuffer()),
+ sender: new Array(32).fill(0),
+ refundAddress: new Array(32).fill(0),
+ maxFee,
+ initAuctionFee: 2000n,
+ deadline: currTime! + 2,
+ redeemerMessage: Buffer.from("Somebody set up us the bomb"),
+ };
const fastMessage = new LiquidityLayerMessage({
- fastMarketOrder: {
- amountIn,
- minAmountOut: 0n,
- targetChain: wormholeSdk.CHAINS.solana,
- redeemer: Array.from(redeemer.publicKey.toBuffer()),
- sender: new Array(32).fill(0),
- refundAddress: new Array(32).fill(0),
- maxFee,
- initAuctionFee: 2000n,
- deadline: currTime! + 2,
- redeemerMessage: Buffer.from("Somebody set up us the bomb"),
- },
+ fastMarketOrder,
});
const finalizedMessage = new LiquidityLayerMessage({
@@ -898,10 +954,7 @@ describe("Matching Engine <> Token Router", function () {
const fastVaaHash = fastVaaAccount.digest();
const preparedBy = payer.publicKey;
- const preparedOrderResponse = matchingEngine.preparedOrderResponseAddress(
- preparedBy,
- fastVaaHash,
- );
+ const preparedOrderResponse = matchingEngine.preparedOrderResponseAddress(fastVaaHash);
const auction = matchingEngine.auctionAddress(fastVaaHash);
if (initAuction) {
@@ -921,13 +974,18 @@ describe("Matching Engine <> Token Router", function () {
}
const { configId, bestOfferToken, initialOfferToken, startSlot } = info;
const auctionConfig = matchingEngine.auctionConfigAddress(configId);
- const duration = (await matchingEngine.fetchAuctionConfig(configId)).parameters
- .duration;
+ const { duration, gracePeriod } = await matchingEngine.fetchAuctionParameters(
+ configId,
+ );
- await new Promise((f) => setTimeout(f, startSlot.toNumber() + duration + 200));
+ await waitUntilSlot(
+ connection,
+ startSlot.toNumber() + duration + gracePeriod - 1,
+ );
+ //await new Promise((f) => setTimeout(f, startSlot.toNumber() + duration + 200));
const computeIx = ComputeBudgetProgram.setComputeUnitLimit({
- units: 400_000,
+ units: 300_000,
});
const ix = await matchingEngine.executeFastOrderCctpIx({
payer: payer.publicKey,
@@ -943,12 +1001,19 @@ describe("Matching Engine <> Token Router", function () {
if (prepareOrderResponse) {
const computeIx = ComputeBudgetProgram.setComputeUnitLimit({
- units: 400_000,
+ units: 300_000,
+ });
+ const { value: lookupTableAccount } = await connection.getAddressLookupTable(
+ lookupTableAddress,
+ );
+ await expectIxOk(connection, [computeIx, prepareIx], [payer], {
+ addressLookupTableAccounts: [lookupTableAccount!],
});
- await expectIxOk(connection, [computeIx, prepareIx], [payer]);
}
return {
+ fastMessage,
+ fastMarketOrder,
fastVaa,
fastVaaAccount,
finalizedVaa,
diff --git a/solana/ts/tests/12__testnetFork.ts b/solana/ts/tests/12__testnetFork.ts
index f2ed179e4..62716d9ed 100644
--- a/solana/ts/tests/12__testnetFork.ts
+++ b/solana/ts/tests/12__testnetFork.ts
@@ -33,7 +33,8 @@ const MATCHING_ENGINE_ARTIFACT_PATH = `${__dirname}/artifacts/testnet_matching_e
const TOKEN_ROUTER_ARTIFACT_PATH = `${__dirname}/artifacts/testnet_token_router.so`;
/// FOR NOW ONLY PERFORM THESE TESTS IF YOU HAVE THE MAGIC PRIVATE KEY.
-if (process.env.MAGIC_PRIVATE_KEY !== undefined) {
+if (false) {
+ //process.env.MAGIC_PRIVATE_KEY !== undefined) {
const devnetOwner = Keypair.fromSecretKey(
Buffer.from(process.env.MAGIC_PRIVATE_KEY!, "base64"),
);
diff --git a/solana/ts/tests/helpers/consts.ts b/solana/ts/tests/helpers/consts.ts
index d3da3f8b6..94e1ec613 100644
--- a/solana/ts/tests/helpers/consts.ts
+++ b/solana/ts/tests/helpers/consts.ts
@@ -1,5 +1,5 @@
import { PublicKey, Keypair } from "@solana/web3.js";
-import { CONTRACTS } from "@certusone/wormhole-sdk";
+import { CONTRACTS, type ChainName } from "@certusone/wormhole-sdk";
import { MockGuardians } from "@certusone/wormhole-sdk/lib/cjs/mock";
export const WORMHOLE_CONTRACTS = CONTRACTS.TESTNET;
@@ -38,3 +38,23 @@ export const MOCK_GUARDIANS = new MockGuardians(0, [GUARDIAN_KEY]);
export const USDC_MINT_ADDRESS = new PublicKey("4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU");
export const ETHEREUM_USDC_ADDRESS = "0x07865c6e87b9f70255377e024ace6630c1eaa37f";
+
+export const CHAIN_TO_DOMAIN: Partial<{ [k in ChainName]: number }> = {
+ ethereum: 0,
+ avalanche: 1,
+ optimism: 2,
+ arbitrum: 3,
+ // noble: 4,
+ solana: 5,
+ base: 6,
+ polygon: 7,
+};
+
+export const REGISTERED_TOKEN_ROUTERS: Partial<{ [k in ChainName]: Array }> = {
+ ethereum: Array.from(Buffer.alloc(32, "f0", "hex")),
+ avalanche: Array.from(Buffer.alloc(32, "a1", "hex")),
+ optimism: Array.from(Buffer.alloc(32, "f2", "hex")),
+ arbitrum: Array.from(Buffer.alloc(32, "f3", "hex")),
+ base: Array.from(Buffer.alloc(32, "f6", "hex")),
+ polygon: Array.from(Buffer.alloc(32, "f7", "hex")),
+};
diff --git a/solana/ts/tests/helpers/mock.ts b/solana/ts/tests/helpers/mock.ts
index ddf7a7051..78a898e9c 100644
--- a/solana/ts/tests/helpers/mock.ts
+++ b/solana/ts/tests/helpers/mock.ts
@@ -15,19 +15,19 @@ export async function postLiquidityLayerVaa(
foreignEmitterAddress: Array,
sequence: bigint,
message: LiquidityLayerMessage | Buffer,
- chainName?: ChainName
+ sourceChain?: ChainName,
) {
const foreignEmitter = new MockEmitter(
Buffer.from(foreignEmitterAddress).toString("hex"),
- coalesceChainId(chainName ?? "ethereum"),
- Number(sequence - 1n)
+ coalesceChainId(sourceChain ?? "ethereum"),
+ Number(sequence - 1n),
);
const published = foreignEmitter.publishMessage(
0, // nonce,
Buffer.isBuffer(message) ? message : message.encode(),
0, // consistencyLevel
- 12345678 // timestamp
+ 12345678, // timestamp
);
const vaaBuf = guardians.addSignatures(published, [0]);
diff --git a/solana/ts/tests/helpers/utils.ts b/solana/ts/tests/helpers/utils.ts
index 0ee8bd9f8..0eac5f0d9 100644
--- a/solana/ts/tests/helpers/utils.ts
+++ b/solana/ts/tests/helpers/utils.ts
@@ -226,9 +226,8 @@ export async function waitUntilSlot(connection: Connection, targetSlot: number)
}
export async function getUsdcAtaBalance(connection: Connection, owner: PublicKey) {
- const { amount } = await splToken.getAccount(
- connection,
- splToken.getAssociatedTokenAddressSync(USDC_MINT_ADDRESS, owner),
- );
- return amount;
+ return splToken
+ .getAccount(connection, splToken.getAssociatedTokenAddressSync(USDC_MINT_ADDRESS, owner))
+ .then((token) => token.amount)
+ .catch(() => 0n);
}