From 318e2588f9808c4b322b8bc8e0fb54f45000ead6 Mon Sep 17 00:00:00 2001 From: A5 Pickle Date: Fri, 16 Aug 2024 09:59:54 -0500 Subject: [PATCH] solana: add base fee token --- solana/programs/matching-engine/src/error.rs | 2 + .../src/events/auction_settled.rs | 5 +- .../auction/prepare_order_response/cctp.rs | 18 ++ .../src/processor/auction/settle/complete.rs | 67 +++----- .../src/processor/auction/settle/none/mod.rs | 2 +- .../src/state/prepared_order_response.rs | 1 + .../programs/matching-engine/src/utils/mod.rs | 3 +- solana/ts/scripts/removeme.ts | 44 +++++ solana/ts/src/idl/json/matching_engine.json | 37 ++++- solana/ts/src/idl/ts/matching_engine.ts | 37 ++++- solana/ts/src/matchingEngine/index.ts | 37 +++-- .../state/PreparedOrderResponse.ts | 1 + solana/ts/tests/01__matchingEngine.ts | 157 ++++++++---------- solana/ts/tests/04__interaction.ts | 6 + 14 files changed, 259 insertions(+), 158 deletions(-) create mode 100644 solana/ts/scripts/removeme.ts diff --git a/solana/programs/matching-engine/src/error.rs b/solana/programs/matching-engine/src/error.rs index df814e19..b67e4023 100644 --- a/solana/programs/matching-engine/src/error.rs +++ b/solana/programs/matching-engine/src/error.rs @@ -81,6 +81,8 @@ pub enum MatchingEngineError { FastFillNotRedeemed = 0x435, ReservedSequenceMismatch = 0x438, AuctionAlreadySettled = 0x43a, + InvalidBaseFeeToken = 0x43c, + BaseFeeTokenRequired = 0x43e, CannotCloseAuctionYet = 0x500, AuctionHistoryNotFull = 0x502, diff --git a/solana/programs/matching-engine/src/events/auction_settled.rs b/solana/programs/matching-engine/src/events/auction_settled.rs index eb3c045a..bda0fd33 100644 --- a/solana/programs/matching-engine/src/events/auction_settled.rs +++ b/solana/programs/matching-engine/src/events/auction_settled.rs @@ -18,8 +18,9 @@ pub struct AuctionSettled { pub best_offer_token: Option, /// Depending on whether there was an active auction, this field will have the pubkey of the - /// executor token (if there was an auction) or fee recipient token (if there was no auction). - pub executor_token: Option, + /// base fee token account (if there was an auction) or fee recipient token (if there was no + /// auction). + pub base_fee_token: Option, /// This value will only be some if there was no active auction. pub with_execute: Option, diff --git a/solana/programs/matching-engine/src/processor/auction/prepare_order_response/cctp.rs b/solana/programs/matching-engine/src/processor/auction/prepare_order_response/cctp.rs index 88778eba..9510bdca 100644 --- a/solana/programs/matching-engine/src/processor/auction/prepare_order_response/cctp.rs +++ b/solana/programs/matching-engine/src/processor/auction/prepare_order_response/cctp.rs @@ -97,6 +97,23 @@ pub struct PrepareOrderResponseCctp<'info> { )] prepared_custody_token: Box>, + /// This token account will be the one that collects the base fee only if an auction's order + /// was executed late. Otherwise, the protocol's fee recipient token account will be used for + /// non-existent auctions and the best offer token account will be used for orders executed on + /// time. + #[account( + token::mint = usdc, + constraint = { + require!( + base_fee_token.key() != prepared_custody_token.key(), + MatchingEngineError::InvalidBaseFeeToken + ); + + true + } + )] + base_fee_token: Box>, + usdc: Usdc<'info>, cctp: CctpReceiveMessage<'info>, @@ -216,6 +233,7 @@ fn handle_prepare_order_response_cctp( }, info: PreparedOrderResponseInfo { prepared_by: ctx.accounts.payer.key(), + base_fee_token: ctx.accounts.base_fee_token.key(), source_chain: finalized_vaa.emitter_chain(), base_fee: order_response.base_fee(), fast_vaa_timestamp: fast_vaa.timestamp(), 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 b0276bef..883e805d 100644 --- a/solana/programs/matching-engine/src/processor/auction/settle/complete.rs +++ b/solana/programs/matching-engine/src/processor/auction/settle/complete.rs @@ -5,10 +5,7 @@ use crate::{ utils, }; use anchor_lang::prelude::*; -use anchor_spl::{ - associated_token::get_associated_token_address, - token::{self, TokenAccount}, -}; +use anchor_spl::token::{self, TokenAccount}; #[derive(Accounts)] pub struct SettleAuctionComplete<'info> { @@ -18,20 +15,24 @@ pub struct SettleAuctionComplete<'info> { mut, address = prepared_order_response.prepared_by, )] - executor: UncheckedAccount<'info>, + beneficiary: UncheckedAccount<'info>, + /// This token account will receive the base fee only if there was a penalty when executing the + /// order. If it does not exist when there is a penalty, this instruction handler will revert. + /// + /// CHECK: This account must be the same as the base fee token in the prepared order response. #[account( mut, - token::mint = common::USDC_MINT, + address = prepared_order_response.base_fee_token, )] - executor_token: Box>, + base_fee_token: UncheckedAccount<'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 may exist. If it doesn't and there is a penalty, we will send all - /// of the tokens to the executor token account. + /// of the tokens to the base fee token account. #[account( mut, address = auction.info.as_ref().unwrap().best_offer_token, @@ -40,7 +41,7 @@ pub struct SettleAuctionComplete<'info> { #[account( mut, - close = executor, + close = beneficiary, seeds = [ PreparedOrderResponse::SEED_PREFIX, prepared_order_response.seeds.fast_vaa_hash.as_ref() @@ -101,8 +102,8 @@ fn handle_settle_auction_complete( &[prepared_order_response.seeds.bump], ]; - let executor = &ctx.accounts.executor; - let executor_token = &ctx.accounts.executor_token; + let beneficiary = &ctx.accounts.beneficiary; + let base_fee_token = &ctx.accounts.base_fee_token; let best_offer_token = &ctx.accounts.best_offer_token; let token_program = &ctx.accounts.token_program; let prepared_custody_token = &ctx.accounts.prepared_custody_token; @@ -131,11 +132,15 @@ fn handle_settle_auction_complete( ) } _ => { + let base_fee_token_data = + utils::checked_deserialize_token_account(base_fee_token, &common::USDC_MINT) + .ok_or_else(|| MatchingEngineError::BaseFeeTokenRequired)?; + // If the token account happens to not exist anymore, we will give everything to the - // executor. + // base fee token account. match utils::checked_deserialize_token_account(best_offer_token, &common::USDC_MINT) { Some(best_offer) => { - if executor_token.key() == best_offer_token.key() { + if base_fee_token.key() == best_offer_token.key() { ( None, // executor_result TokenAccountResult { @@ -145,31 +150,9 @@ fn handle_settle_auction_complete( .into(), ) } else { - // 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. - - // We require that the executor token account be an ATA. - require_keys_eq!( - executor_token.key(), - get_associated_token_address( - &executor_token.owner, - &executor_token.mint - ), - ErrorCode::AccountNotAssociatedTokenAccount - ); - - // And enforce that the owner of this ATA is the executor. - require_keys_eq!( - executor.key(), - executor_token.owner, - ErrorCode::ConstraintTokenOwner, - ); - ( TokenAccountResult { - balance_before: executor_token.amount, + balance_before: base_fee_token_data.amount, amount: base_fee, } .into(), @@ -183,7 +166,7 @@ fn handle_settle_auction_complete( } None => ( TokenAccountResult { - balance_before: executor_token.amount, + balance_before: base_fee_token_data.amount, amount: repayment, } .into(), @@ -193,7 +176,7 @@ fn handle_settle_auction_complete( } }; - // Transfer executor his bounty if there are any. + // Transfer base fee token his bounty if there are any. let settled_executor_result = match executor_result { Some(TokenAccountResult { balance_before, @@ -204,7 +187,7 @@ fn handle_settle_auction_complete( token_program.to_account_info(), token::Transfer { from: prepared_custody_token.to_account_info(), - to: executor_token.to_account_info(), + to: base_fee_token.to_account_info(), authority: prepared_order_response.to_account_info(), }, &[prepared_order_response_signer_seeds], @@ -213,7 +196,7 @@ fn handle_settle_auction_complete( )?; SettledTokenAccountInfo { - key: executor_token.key(), + key: base_fee_token.key(), balance_after: balance_before.saturating_add(amount), } .into() @@ -252,7 +235,7 @@ fn handle_settle_auction_complete( emit!(crate::events::AuctionSettled { auction: ctx.accounts.auction.key(), best_offer_token: settled_best_offer_result, - executor_token: settled_executor_result, + base_fee_token: settled_executor_result, with_execute: Default::default(), }); @@ -261,7 +244,7 @@ fn handle_settle_auction_complete( token_program.to_account_info(), token::CloseAccount { account: prepared_custody_token.to_account_info(), - destination: executor.to_account_info(), + destination: beneficiary.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/none/mod.rs b/solana/programs/matching-engine/src/processor/auction/settle/none/mod.rs index 7abf9a3a..bb26d7a4 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 @@ -84,7 +84,7 @@ fn settle_none_and_prepare_fill(accounts: SettleNoneAndPrepareFill<'_, '_>) -> R emit!(crate::events::AuctionSettled { auction: auction.key(), best_offer_token: Default::default(), - executor_token: crate::events::SettledTokenAccountInfo { + base_fee_token: crate::events::SettledTokenAccountInfo { key: fee_recipient_token.key(), balance_after: fee_recipient_token.amount.saturating_add(fee) } diff --git a/solana/programs/matching-engine/src/state/prepared_order_response.rs b/solana/programs/matching-engine/src/state/prepared_order_response.rs index d0cad2d9..90a5a26b 100644 --- a/solana/programs/matching-engine/src/state/prepared_order_response.rs +++ b/solana/programs/matching-engine/src/state/prepared_order_response.rs @@ -11,6 +11,7 @@ pub struct PreparedOrderResponseSeeds { #[derive(Debug, AnchorSerialize, AnchorDeserialize, Clone, InitSpace)] pub struct PreparedOrderResponseInfo { pub prepared_by: Pubkey, + pub base_fee_token: Pubkey, pub fast_vaa_timestamp: u32, pub source_chain: u16, diff --git a/solana/programs/matching-engine/src/utils/mod.rs b/solana/programs/matching-engine/src/utils/mod.rs index f9f5309b..95a8a397 100644 --- a/solana/programs/matching-engine/src/utils/mod.rs +++ b/solana/programs/matching-engine/src/utils/mod.rs @@ -45,7 +45,7 @@ pub fn require_local_endpoint(endpoint: &RouterEndpoint) -> Result { pub fn checked_deserialize_token_account( acc_info: &AccountInfo, expected_mint: &Pubkey, -) -> Option { +) -> Option> { if acc_info.owner != &token::ID { None } else { @@ -54,5 +54,6 @@ pub fn checked_deserialize_token_account( token::TokenAccount::try_deserialize(&mut &data[..]) .ok() .filter(|token_data| &token_data.mint == expected_mint && !token_data.is_frozen()) + .map(Box::new) } } diff --git a/solana/ts/scripts/removeme.ts b/solana/ts/scripts/removeme.ts new file mode 100644 index 00000000..f3c9e16f --- /dev/null +++ b/solana/ts/scripts/removeme.ts @@ -0,0 +1,44 @@ +import { Connection, PublicKey } from "@solana/web3.js"; +import { Chain, toChain, toChainId } from "@wormhole-foundation/sdk-base"; +import { toNative, toUniversal } from "@wormhole-foundation/sdk-definitions"; +import * as matchingEngineSdk from "../src/matchingEngine"; +import * as tokenRouterSdk from "../src/tokenRouter"; +import "@wormhole-foundation/sdk-evm/address"; + +const USDC_MINT = new PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"); + +// Here we go. +main(); + +// impl + +async function main() { + const connection = new Connection("https://api.mainnet-beta.solana.com", "confirmed"); + + const matchingEngine = new matchingEngineSdk.MatchingEngineProgram( + connection, + matchingEngineSdk.mainnet(), + USDC_MINT, + ); + + const endpoints = [ + new PublicKey("GHwrXHqrF7D4Hkd1pK3qVr1edcJwXCee8XjbuRzSVpLY"), + new PublicKey("GA6fw8zJxAMNBpBDidtGR2qRWsk4xAzm7tWUSKhnRGCu"), + new PublicKey("9fd1z3q2Ef9s2NR2tM3856eJ2caDLNjJNJvb9wNpCbKa"), + ]; + + console.log("Registered Endpoints"); + console.log(); + + for (const endpoint of endpoints) { + const endpointInfo = await matchingEngine.fetchRouterEndpointInfo({ address: endpoint }); + console.log(`Endpoint: ${endpoint.toString()}`); + + const chain = toChain(endpointInfo.chain); + const checkAddress = matchingEngine.routerEndpointAddress(toChainId(chain)); + console.log(`Check PDA Address: ${checkAddress.toString()}`); + console.log(` Chain: ${chain}`); + console.log(` Address: ${toNative(chain, Uint8Array.from(endpointInfo.address))}`); + console.log(); + } +} diff --git a/solana/ts/src/idl/json/matching_engine.json b/solana/ts/src/idl/json/matching_engine.json index a94b37b8..b2ed77ac 100644 --- a/solana/ts/src/idl/json/matching_engine.json +++ b/solana/ts/src/idl/json/matching_engine.json @@ -1369,6 +1369,15 @@ "name": "prepared_custody_token", "writable": true }, + { + "name": "base_fee_token", + "docs": [ + "This token account will be the one that collects the base fee only if an auction's order", + "was executed late. Otherwise, the protocol's fee recipient token account will be used for", + "non-existent auctions and the best offer token account will be used for orders executed on", + "time." + ] + }, { "name": "usdc", "accounts": [ @@ -1834,14 +1843,19 @@ ], "accounts": [ { - "name": "executor", + "name": "beneficiary", "docs": [ "finalized VAA." ], "writable": true }, { - "name": "executor_token", + "name": "base_fee_token", + "docs": [ + "This token account will receive the base fee only if there was a penalty when executing the", + "order. If it does not exist when there is a penalty, this instruction handler will revert.", + "" + ], "writable": true }, { @@ -1851,7 +1865,7 @@ "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).", "", - "of the tokens to the executor token account." + "of the tokens to the base fee token account." ], "writable": true }, @@ -3032,6 +3046,14 @@ "code": 7082, "name": "AuctionAlreadySettled" }, + { + "code": 7084, + "name": "InvalidBaseFeeToken" + }, + { + "code": 7086, + "name": "BaseFeeTokenRequired" + }, { "code": 7280, "name": "CannotCloseAuctionYet" @@ -3474,10 +3496,11 @@ } }, { - "name": "executor_token", + "name": "base_fee_token", "docs": [ "Depending on whether there was an active auction, this field will have the pubkey of the", - "executor token (if there was an auction) or fee recipient token (if there was no auction)." + "base fee token account (if there was an auction) or fee recipient token (if there was no", + "auction)." ], "type": { "option": { @@ -4103,6 +4126,10 @@ "name": "prepared_by", "type": "pubkey" }, + { + "name": "base_fee_token", + "type": "pubkey" + }, { "name": "fast_vaa_timestamp", "type": "u32" diff --git a/solana/ts/src/idl/ts/matching_engine.ts b/solana/ts/src/idl/ts/matching_engine.ts index 75ce8915..055f7e14 100644 --- a/solana/ts/src/idl/ts/matching_engine.ts +++ b/solana/ts/src/idl/ts/matching_engine.ts @@ -1375,6 +1375,15 @@ export type MatchingEngine = { "name": "preparedCustodyToken", "writable": true }, + { + "name": "baseFeeToken", + "docs": [ + "This token account will be the one that collects the base fee only if an auction's order", + "was executed late. Otherwise, the protocol's fee recipient token account will be used for", + "non-existent auctions and the best offer token account will be used for orders executed on", + "time." + ] + }, { "name": "usdc", "accounts": [ @@ -1840,14 +1849,19 @@ export type MatchingEngine = { ], "accounts": [ { - "name": "executor", + "name": "beneficiary", "docs": [ "finalized VAA." ], "writable": true }, { - "name": "executorToken", + "name": "baseFeeToken", + "docs": [ + "This token account will receive the base fee only if there was a penalty when executing the", + "order. If it does not exist when there is a penalty, this instruction handler will revert.", + "" + ], "writable": true }, { @@ -1857,7 +1871,7 @@ export type MatchingEngine = { "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).", "", - "of the tokens to the executor token account." + "of the tokens to the base fee token account." ], "writable": true }, @@ -3038,6 +3052,14 @@ export type MatchingEngine = { "code": 7082, "name": "auctionAlreadySettled" }, + { + "code": 7084, + "name": "invalidBaseFeeToken" + }, + { + "code": 7086, + "name": "baseFeeTokenRequired" + }, { "code": 7280, "name": "cannotCloseAuctionYet" @@ -3480,10 +3502,11 @@ export type MatchingEngine = { } }, { - "name": "executorToken", + "name": "baseFeeToken", "docs": [ "Depending on whether there was an active auction, this field will have the pubkey of the", - "executor token (if there was an auction) or fee recipient token (if there was no auction)." + "base fee token account (if there was an auction) or fee recipient token (if there was no", + "auction)." ], "type": { "option": { @@ -4109,6 +4132,10 @@ export type MatchingEngine = { "name": "preparedBy", "type": "pubkey" }, + { + "name": "baseFeeToken", + "type": "pubkey" + }, { "name": "fastVaaTimestamp", "type": "u32" diff --git a/solana/ts/src/matchingEngine/index.ts b/solana/ts/src/matchingEngine/index.ts index 5764ddcc..5b51455f 100644 --- a/solana/ts/src/matchingEngine/index.ts +++ b/solana/ts/src/matchingEngine/index.ts @@ -147,7 +147,7 @@ export type SettledTokenAccountInfo = { export type AuctionSettled = { auction: PublicKey; bestOfferToken: SettledTokenAccountInfo | null; - executorToken: SettledTokenAccountInfo | null; + baseFeeToken: SettledTokenAccountInfo | null; withExecute: MessageProtocol | null; }; @@ -1472,11 +1472,15 @@ export class MatchingEngineProgram { payer: PublicKey; fastVaa: PublicKey; finalizedVaa: PublicKey; + baseFeeToken?: PublicKey; }, args: CctpMessageArgs, ): Promise { const { payer, fastVaa, finalizedVaa } = accounts; + let { baseFeeToken } = accounts; + baseFeeToken ??= await splToken.getAssociatedTokenAddress(this.mint, payer); + const fastVaaAcct = await VaaAccount.fetch(this.program.provider.connection, fastVaa); const fromEndpoint = this.routerEndpointAddress(fastVaaAcct.emitterInfo().chain); @@ -1516,6 +1520,7 @@ export class MatchingEngineProgram { finalizedVaa: this.liquidityLayerVaaComposite(finalizedVaa), preparedOrderResponse, preparedCustodyToken: this.preparedCustodyTokenAddress(preparedOrderResponse), + baseFeeToken, usdc: this.usdcComposite(), cctp: { mintRecipient: this.cctpMintRecipientComposite(), @@ -1555,7 +1560,7 @@ export class MatchingEngineProgram { let { executor, fastVaa, finalizedVaa, auction, bestOfferToken } = accounts; const prepareOrderResponseIx = await this.prepareOrderResponseCctpIx( - { payer: executor, fastVaa, finalizedVaa }, + { payer: executor, fastVaa, finalizedVaa, baseFeeToken: bestOfferToken }, args, ); const fastVaaAccount = await VaaAccount.fetch(this.program.provider.connection, fastVaa); @@ -1574,10 +1579,11 @@ export class MatchingEngineProgram { } const settleAuctionCompletedIx = await this.settleAuctionCompleteIx({ - executor, + beneficiary: executor, auction, preparedOrderResponse, bestOfferToken, + baseFeeToken: bestOfferToken, }); const preparedTx: PreparedTransaction = { @@ -1595,23 +1601,24 @@ export class MatchingEngineProgram { } async settleAuctionCompleteIx(accounts: { - executor: PublicKey; preparedOrderResponse: PublicKey; auction?: PublicKey; + beneficiary?: PublicKey; + baseFeeToken?: PublicKey; bestOfferToken?: PublicKey; - executorToken?: PublicKey; }) { - const { executor, preparedOrderResponse } = accounts; + const { preparedOrderResponse } = accounts; - let { auction, bestOfferToken, executorToken } = accounts; - executorToken ??= splToken.getAssociatedTokenAddressSync(this.mint, executor); + let { auction, beneficiary, baseFeeToken, bestOfferToken } = accounts; - if (auction === undefined) { - const { seeds } = await this.fetchPreparedOrderResponse({ + if (auction === undefined || beneficiary === undefined || baseFeeToken === undefined) { + const { seeds, info } = await this.fetchPreparedOrderResponse({ address: preparedOrderResponse, }); - auction = this.auctionAddress(seeds.fastVaaHash); + auction ??= this.auctionAddress(seeds.fastVaaHash); + beneficiary ??= info.preparedBy; + baseFeeToken ??= info.baseFeeToken; } if (bestOfferToken === undefined) { @@ -1626,8 +1633,8 @@ export class MatchingEngineProgram { return this.program.methods .settleAuctionComplete() .accounts({ - executor, - executorToken, + beneficiary, + baseFeeToken, preparedOrderResponse, preparedCustodyToken: this.preparedCustodyTokenAddress(preparedOrderResponse), auction, @@ -1650,8 +1657,10 @@ export class MatchingEngineProgram { confirmOptions?: ConfirmOptions, ): Promise { const { executor, fastVaa, finalizedVaa, auction } = accounts; + + const { feeRecipientToken } = await this.fetchCustodian(); const prepareOrderResponseIx = await this.prepareOrderResponseCctpIx( - { payer: executor, fastVaa, finalizedVaa }, + { payer: executor, fastVaa, finalizedVaa, baseFeeToken: feeRecipientToken }, args, ); const fastVaaAccount = await VaaAccount.fetch(this.program.provider.connection, fastVaa); diff --git a/solana/ts/src/matchingEngine/state/PreparedOrderResponse.ts b/solana/ts/src/matchingEngine/state/PreparedOrderResponse.ts index 91749a47..40d20f45 100644 --- a/solana/ts/src/matchingEngine/state/PreparedOrderResponse.ts +++ b/solana/ts/src/matchingEngine/state/PreparedOrderResponse.ts @@ -10,6 +10,7 @@ export type PreparedOrderResponseSeeds = { export type PreparedOrderResponseInfo = { preparedBy: PublicKey; + baseFeeToken: PublicKey; fastVaaTimestamp: number; sourceChain: number; baseFee: BN; diff --git a/solana/ts/tests/01__matchingEngine.ts b/solana/ts/tests/01__matchingEngine.ts index 57461d0f..6a2a72cc 100644 --- a/solana/ts/tests/01__matchingEngine.ts +++ b/solana/ts/tests/01__matchingEngine.ts @@ -3578,7 +3578,7 @@ describe("Matching Engine", function () { await settleAuctionCompleteForTest( { - executor: payer.publicKey, + beneficiary: payer.publicKey, preparedOrderResponse: result!.preparedOrderResponse, auction: engine.auctionAddress(fastVaaAccount.digest()), bestOfferToken: splToken.getAssociatedTokenAddressSync( @@ -3596,7 +3596,7 @@ describe("Matching Engine", function () { it("Cannot Settle Active Auction", async function () { await settleAuctionCompleteForTest( { - executor: payer.publicKey, + beneficiary: payer.publicKey, }, { prepareSigners: [payer], @@ -3606,98 +3606,60 @@ describe("Matching Engine", function () { ); }); - it("Settle Completed without Penalty", async function () { + it("Cannot Settle Completed (Beneficiary != Prepared By)", async function () { await settleAuctionCompleteForTest( { - executor: playerOne.publicKey, + beneficiary: playerOne.publicKey, }, { - prepareSigners: [playerOne], - executeWithinGracePeriod: true, + executeWithinGracePeriod: false, + executorIsPreparer: false, + errorMsg: "beneficiary. Error Code: ConstraintAddress", }, ); }); - it("Settle Completed With Order Response Prepared Before Active Auction", async function () { + it("Settle Completed without Penalty", async function () { await settleAuctionCompleteForTest( { - executor: playerOne.publicKey, + beneficiary: playerOne.publicKey, }, { prepareSigners: [playerOne], executeWithinGracePeriod: true, - prepareAfterExecuteOrder: false, - }, - ); - }); - - it("Cannot Settle Completed with Penalty (Executor != Prepared By)", async function () { - await settleAuctionCompleteForTest( - { - executor: playerOne.publicKey, - }, - { - executeWithinGracePeriod: false, - executorIsPreparer: false, - errorMsg: "executor. Error Code: ConstraintAddress", }, ); }); - it("Settle Completed with Penalty (Executor == Best Offer)", async function () { + it("Settle Completed With Order Response Prepared Before Active Auction", async function () { await settleAuctionCompleteForTest( { - executor: playerOne.publicKey, + beneficiary: playerOne.publicKey, }, { prepareSigners: [playerOne], - executeWithinGracePeriod: false, + executeWithinGracePeriod: true, + prepareAfterExecuteOrder: false, }, ); }); - it("Cannot Settle Completed with Penalty (Executor is not ATA)", async function () { - const executorTokenSigner = Keypair.generate(); - const executorToken = executorTokenSigner.publicKey; - - await expectIxOk( - connection, - [ - SystemProgram.createAccount({ - fromPubkey: payer.publicKey, - newAccountPubkey: executorToken, - lamports: await connection.getMinimumBalanceForRentExemption( - splToken.ACCOUNT_SIZE, - ), - space: splToken.ACCOUNT_SIZE, - programId: splToken.TOKEN_PROGRAM_ID, - }), - splToken.createInitializeAccount3Instruction( - executorToken, - engine.mint, - playerTwo.publicKey, - ), - ], - [payer, executorTokenSigner], - ); - + it("Settle Completed with Penalty (Base Fee Token == Best Offer Token)", async function () { await settleAuctionCompleteForTest( { - executor: playerTwo.publicKey, - executorToken, + beneficiary: playerOne.publicKey, }, { - prepareSigners: [playerTwo], + prepareSigners: [playerOne], executeWithinGracePeriod: false, - errorMsg: "Error Code: AccountNotAssociatedTokenAccount", }, ); }); - it("Settle Completed with Penalty (Executor != Best Offer)", async function () { + it("Settle Completed with Penalty (Base Fee Token != Best Offer Token)", async function () { await settleAuctionCompleteForTest( { - executor: playerTwo.publicKey, + beneficiary: playerTwo.publicKey, }, { prepareSigners: [playerTwo], @@ -4159,13 +4121,19 @@ describe("Matching Engine", function () { const vaaTimestamp = timestamp - 7200 + timeToWait; if (settlementType == "complete") { const result = await settleAuctionCompleteForTest( - { executor: playerOne.publicKey }, + { beneficiary: playerOne.publicKey }, { vaaTimestamp, prepareSigners: [playerOne] }, ); return result!.auction; } else if (settlementType == "none") { const result = await settleAuctionNoneCctpForTest( - { payer: playerOne.publicKey }, + { + payer: playerOne.publicKey, + baseFeeToken: splToken.getAssociatedTokenAddressSync( + USDC_MINT_ADDRESS, + playerOne.publicKey, + ), + }, { vaaTimestamp, signers: [playerOne] }, ); return result!.auction; @@ -4479,6 +4447,7 @@ describe("Matching Engine", function () { payer: PublicKey; fastVaa?: PublicKey; finalizedVaa?: PublicKey; + baseFeeToken?: PublicKey; }, opts: ForTestOpts & ObserveCctpOrderVaasOpts & PrepareOrderResponseForTestOptionalOpts = {}, ): Promise Token Router", function () { payer: PublicKey; fastVaa?: PublicKey; finalizedVaa?: PublicKey; + baseFeeToken?: PublicKey; }, opts: ForTestOpts & ObserveCctpOrderVaasOpts & PrepareOrderResponseForTestOptionalOpts = {}, ): Promise Token Router", function () { toChainId(fastMarketOrder!.targetChain), ); + const baseFeeToken = + accounts.baseFeeToken ?? + splToken.getAssociatedTokenAddressSync(USDC_MINT_ADDRESS, payer.publicKey); + const { baseFee } = deposit!.message.payload! as SlowOrderResponse; expect(preparedOrderResponseData).to.eql( new matchingEngineSdk.PreparedOrderResponse( @@ -981,6 +986,7 @@ describe("Matching Engine <> Token Router", function () { }, { preparedBy: accounts.payer, + baseFeeToken, fastVaaTimestamp: fastVaaAccount.timestamp(), sourceChain: fastVaaAccount.emitterInfo().chain, baseFee: uint64ToBN(baseFee),