diff --git a/solana/ts/src/matchingEngine/index.ts b/solana/ts/src/matchingEngine/index.ts index 126c1f9b..f9719b71 100644 --- a/solana/ts/src/matchingEngine/index.ts +++ b/solana/ts/src/matchingEngine/index.ts @@ -15,24 +15,23 @@ import { SystemProgram, TransactionInstruction, } from "@solana/web3.js"; +import { ChainId, isChainId, toChainId } from "@wormhole-foundation/sdk-base"; import { PreparedTransaction, PreparedTransactionOptions } from ".."; -import IDL from "../idl/json/matching_engine.json"; -import { MatchingEngine } from "../idl/ts/matching_engine"; import { MessageTransmitterProgram, TokenMessengerMinterProgram } from "../cctp"; import { LiquidityLayerMessage, Uint64, VaaHash, - cctpMessageAddress, - coreMessageAddress, reclaimCctpMessageIx, uint64ToBN, uint64ToBigInt, - writeUint64BE, } from "../common"; +import IDL from "../idl/json/matching_engine.json"; +import { MatchingEngine } from "../idl/ts/matching_engine"; import { UpgradeManagerProgram } from "../upgradeManager"; import { BPF_LOADER_UPGRADEABLE_PROGRAM_ID, programDataAddress } from "../utils"; import { VaaAccount } from "../wormhole"; +import { programDerivedAddresses } from "./pdas"; import { Auction, AuctionConfig, @@ -54,9 +53,6 @@ import { ReservedFastFillSequence, RouterEndpoint, } from "./state"; -import { ChainId, toChainId, isChainId, Chain } from "@wormhole-foundation/sdk-base"; -import { programDerivedAddresses } from "./pdas"; -import { FastTransfer } from "@wormhole-foundation/example-liquidity-layer-definitions"; export const PROGRAM_IDS = [ "MatchingEngine11111111111111111111111111111", @@ -1860,9 +1856,7 @@ export class MatchingEngineProgram { initialOfferToken?: PublicKey; initialParticipant?: PublicKey; }, - opts: { - targetChain?: ChainId; - } = {}, + opts: { targetChain?: ChainId } = {}, ) { const connection = this.program.provider.connection; @@ -1882,10 +1876,8 @@ export class MatchingEngineProgram { if (targetChain === undefined) { fastVaaAccount ??= await VaaAccount.fetch(connection, fastVaa); - const { fastMarketOrder } = LiquidityLayerMessage.decode(fastVaaAccount.payload()); - if (fastMarketOrder === undefined) { - throw new Error("Message not FastMarketOrder"); - } + const { payload: fastMarketOrder } = fastVaaAccount.vaa("FastTransfer:FastMarketOrder"); + targetChain ??= toChainId(fastMarketOrder.targetChain); } diff --git a/solana/ts/src/protocol/matchingEngine.ts b/solana/ts/src/protocol/matchingEngine.ts index 2a92f93d..d439b502 100644 --- a/solana/ts/src/protocol/matchingEngine.ts +++ b/solana/ts/src/protocol/matchingEngine.ts @@ -196,18 +196,48 @@ export class SolanaMatchingEngine yield this.createUnsignedTx({ transaction }, "MatchingEngine.improveOffer"); } - async *executeFastOrder(sender: AnySolanaAddress, vaa: FastTransfer.VAA) { + async *executeFastOrder( + sender: AnySolanaAddress, + vaa: FastTransfer.VAA, + participant?: AnySolanaAddress, + ) { + if (vaa.payloadLiteral !== "FastTransfer:FastMarketOrder") throw new Error("Invalid VAA"); + const payer = new SolanaAddress(sender).unwrap(); + const initialParticipant = participant + ? new SolanaAddress(participant).unwrap() + : undefined; + const fastVaa = coreUtils.derivePostedVaaKey( this.coreBridgeProgramId(), Buffer.from(vaa.hash), ); - const ix = await this.executeFastOrderCctpIx({ - payer, - fastVaa, - }); + const digest = keccak256(vaa.hash); + const auction = this.auctionAddress(digest); + const reservedSequence = this.reservedFastFillSequenceAddress(digest); + + const { targetChain } = vaa.payload; + + const ix = + targetChain === "Solana" + ? await this.executeFastOrderLocalIx({ + payer, + fastVaa, + auction, + reservedSequence, + initialParticipant, + }) + : await this.executeFastOrderCctpIx( + { + payer, + fastVaa, + auction, + initialParticipant, + }, + { targetChain: toChainId(targetChain) }, + ); const computeIx = ComputeBudgetProgram.setComputeUnitLimit({ units: 300_000, @@ -218,7 +248,7 @@ export class SolanaMatchingEngine } async *settleAuctionComplete() { - throw new Error("Method not implemented."); + throw "Not implemented"; } settleAuction(): AsyncGenerator, any, unknown> { @@ -234,8 +264,6 @@ export class SolanaMatchingEngine getPenaltyBlocks(): Promise { throw new Error("Method not implemented."); } - getInitialPenaltyBps(): Promise; - getInitialPenaltyBps(): Promise; getInitialPenaltyBps(): Promise { throw new Error("Method not implemented."); } diff --git a/solana/ts/src/testing/mock.ts b/solana/ts/src/testing/mock.ts index 83757bfb..4e192d47 100644 --- a/solana/ts/src/testing/mock.ts +++ b/solana/ts/src/testing/mock.ts @@ -22,7 +22,6 @@ export function getSdkSigner( return { signer, address }; } -// TODO: return VaaAccount, too export async function postLiquidityLayerVaav2( connection: Connection, payer: Keypair | SignAndSendSigner, diff --git a/solana/ts/src/testing/utils.ts b/solana/ts/src/testing/utils.ts index 7dd41a90..d9912c91 100644 --- a/solana/ts/src/testing/utils.ts +++ b/solana/ts/src/testing/utils.ts @@ -11,15 +11,15 @@ import { TransactionMessage, VersionedTransaction, } from "@solana/web3.js"; +import { Network } from "@wormhole-foundation/sdk-base"; +import { SignAndSendSigner as SdkSigner, signAndSendWait } from "@wormhole-foundation/sdk-connect"; +import { UniversalAddress, VAA } from "@wormhole-foundation/sdk-definitions"; +import { SolanaSendSigner, SolanaUnsignedTransaction } from "@wormhole-foundation/sdk-solana"; +import { SolanaWormholeCore, utils as coreUtils } from "@wormhole-foundation/sdk-solana-core"; import { expect } from "chai"; import { execSync } from "child_process"; import { Err, Ok } from "ts-results"; import { CORE_BRIDGE_PID, USDC_MINT_ADDRESS } from "./consts"; -import { SolanaSendSigner, SolanaUnsignedTransaction } from "@wormhole-foundation/sdk-solana"; -import { SolanaWormholeCore, utils as coreUtils } from "@wormhole-foundation/sdk-solana-core"; -import { SignAndSendSigner as SdkSigner, signAndSendWait } from "@wormhole-foundation/sdk-connect"; -import { UniversalAddress, VAA, deserialize } from "@wormhole-foundation/sdk-definitions"; -import { Chain, Network } from "@wormhole-foundation/sdk-base"; export function toUniversalAddress(address: number[] | Buffer | Array): UniversalAddress { return new UniversalAddress(new Uint8Array(address)); diff --git a/solana/ts/tests/01__matchingEngine.ts b/solana/ts/tests/01__matchingEngine.ts index 48e8057b..e49ebaaa 100644 --- a/solana/ts/tests/01__matchingEngine.ts +++ b/solana/ts/tests/01__matchingEngine.ts @@ -2064,12 +2064,7 @@ describe("Matching Engine", function () { fastMarketOrder: baseFastOrder, }, ); - const { - fastVaa, - fastVaaAccount, - auction, - auctionDataBefore: initialData, - } = result!; + const { fastVaaAccount, auction, auctionDataBefore: initialData } = result!; const improveBy = Number( await engine.computeMinOfferDelta( @@ -2104,7 +2099,6 @@ describe("Matching Engine", function () { playerOne.publicKey, fastVaaAccount.vaa("FastTransfer:FastMarketOrder"), ); - const txDetails = await expectTxsOkDetails(playerOneSigner, txs, connection); await checkAfterEffects( @@ -2127,16 +2121,14 @@ describe("Matching Engine", function () { it("Execute Fast Order (Tx)", async function () { const result = await placeInitialOfferCctpForTest( - { - payer: playerTwo.publicKey, - }, + { payer: playerTwo.publicKey }, { signers: [playerTwoSigner], finalized: false, fastMarketOrder: baseFastOrder, }, ); - const { fastVaa, auction, auctionDataBefore: initialData } = result!; + const { fastVaaAccount, auctionDataBefore: initialData } = result!; const { duration, gracePeriod } = await engine.fetchAuctionParameters(); await waitUntilSlot( @@ -2144,28 +2136,12 @@ describe("Matching Engine", function () { initialData.info!.startSlot.addn(duration + gracePeriod - 1).toNumber(), ); - const { value: lookupTableAccount } = await connection.getAddressLookupTable( - lookupTableAddress, - ); - - const tx = await engine.executeFastOrderTx( - { payer: playerTwo.publicKey, fastVaa, auction }, - [playerTwo], - { - feeMicroLamports: 10, - computeUnits: 400_000, - addressLookupTableAccounts: [lookupTableAccount!], - }, - { commitment: "confirmed" }, + const txs = engine.executeFastOrder( + playerTwo.publicKey, + fastVaaAccount.vaa("FastTransfer:FastMarketOrder"), ); - const computeIx = ComputeBudgetProgram.setComputeUnitLimit({ - units: 400_000, - }); - - await expectIxOkDetails(connection, [computeIx, ...tx.ixs], [playerTwo], { - addressLookupTableAccounts: [lookupTableAccount!], - }); + await expectTxsOkDetails(playerTwoSigner, txs, connection); }); it("Reclaim by Closing CCTP Message", async function () { @@ -2183,10 +2159,7 @@ describe("Matching Engine", function () { const cctpAttestation = new CircleAttester().createAttestation(message); const ix = await engine.reclaimCctpMessageIx( - { - payer: playerOne.publicKey, - cctpMessage, - }, + { payer: playerOne.publicKey, cctpMessage }, cctpAttestation, ); @@ -2270,16 +2243,12 @@ describe("Matching Engine", function () { .toNumber(), ); - const ix = await engine.executeFastOrderCctpIx({ - payer: playerOne.publicKey, - fastVaa, - }); - - const computeIx = ComputeBudgetProgram.setComputeUnitLimit({ - units: 300_000, - }); + const txs = engine.executeFastOrder( + playerOne.publicKey, + fastVaaAccount.vaa("FastTransfer:FastMarketOrder"), + ); - const txDetails = await expectIxOkDetails(connection, [computeIx, ix], [playerOne]); + const txDetails = await expectTxsOkDetails(playerOneSigner, txs, connection); await checkAfterEffects( txDetails!, @@ -2358,24 +2327,11 @@ describe("Matching Engine", function () { .toNumber(), ); - const ix = await engine.executeFastOrderCctpIx({ - payer: liquidator.publicKey, - fastVaa, - }); - - const computeIx = ComputeBudgetProgram.setComputeUnitLimit({ - units: 300_000, - }); - - const { value: lookupTableAccount } = await connection.getAddressLookupTable( - lookupTableAddress, - ); - const txDetails = await expectIxOkDetails( - connection, - [computeIx, ix], - [liquidator], - { addressLookupTableAccounts: [lookupTableAccount!] }, + const txs = engine.executeFastOrder( + liquidator.publicKey, + fastVaaAccount.vaa("FastTransfer:FastMarketOrder"), ); + const txDetails = await expectTxsOkDetails(liquidatorSigner, txs, connection); await checkAfterEffects( txDetails!, @@ -2429,17 +2385,11 @@ describe("Matching Engine", function () { // Place the initial offer with a token account that will be closed. const result = await placeInitialOfferCctpForTest( - { - payer: tmpOwner.publicKey, - }, + { payer: tmpOwner.publicKey }, { signers: [tmpOwnerSiner], finalized: false, fastMarketOrder: baseFastOrder }, ); - const { - fastVaaAccount, - fastVaa, - auction, - auctionDataBefore: initialData, - } = result!; + + const { fastVaaAccount, auction, auctionDataBefore: initialData } = result!; // Burn funds out and close tmp ATA. const { amount: burnAmount } = await splToken.getAccount(connection, tmpAta); @@ -2497,25 +2447,12 @@ describe("Matching Engine", function () { .toNumber(), ); - const ix = await engine.executeFastOrderCctpIx({ - payer: liquidator.publicKey, - fastVaa, - initialParticipant: payer.publicKey, - }); - - const computeIx = ComputeBudgetProgram.setComputeUnitLimit({ - units: 300_000, - }); - - const { value: lookupTableAccount } = await connection.getAddressLookupTable( - lookupTableAddress, - ); - const txDetails = await expectIxOkDetails( - connection, - [computeIx, ix], - [liquidator], - { addressLookupTableAccounts: [lookupTableAccount!] }, + const txs = engine.executeFastOrder( + liquidator.publicKey, + fastVaaAccount.vaa("FastTransfer:FastMarketOrder"), + payer.publicKey, ); + const txDetails = await expectTxsOkDetails(liquidatorSigner, txs, connection); await checkAfterEffects( txDetails!, @@ -2643,25 +2580,13 @@ describe("Matching Engine", function () { .toNumber(), ); - const ix = await engine.executeFastOrderCctpIx({ - payer: liquidator.publicKey, - fastVaa, - }); - - const computeIx = ComputeBudgetProgram.setComputeUnitLimit({ - units: 300_000, - }); - - const { value: lookupTableAccount } = await connection.getAddressLookupTable( - lookupTableAddress, - ); - const txDetails = await expectIxOkDetails( - connection, - [computeIx, ix], - [liquidator], - { addressLookupTableAccounts: [lookupTableAccount!] }, + const txs = engine.executeFastOrder( + liquidator.publicKey, + fastVaaAccount.vaa("FastTransfer:FastMarketOrder"), ); + const txDetails = await expectTxsOkDetails(liquidatorSigner, txs, connection); + await checkAfterEffects( txDetails!, auction, @@ -2683,9 +2608,7 @@ describe("Matching Engine", function () { // Start the auction with offer two so that we can // check that the initial offer is refunded. const result = await placeInitialOfferCctpForTest( - { - payer: playerTwo.publicKey, - }, + { payer: playerTwo.publicKey }, { signers: [playerTwoSigner], finalized: false, @@ -2796,24 +2719,11 @@ describe("Matching Engine", function () { .toNumber(), ); - const ix = await engine.executeFastOrderCctpIx({ - payer: liquidator.publicKey, - fastVaa, - }); - - const computeIx = ComputeBudgetProgram.setComputeUnitLimit({ - units: 300_000, - }); - - const { value: lookupTableAccount } = await connection.getAddressLookupTable( - lookupTableAddress, - ); - const txDetails = await expectIxOkDetails( - connection, - [computeIx, ix], - [liquidator], - { addressLookupTableAccounts: [lookupTableAccount!] }, + const txs = engine.executeFastOrder( + liquidator.publicKey, + fastVaaAccount.vaa("FastTransfer:FastMarketOrder"), ); + const txDetails = await expectTxsOkDetails(liquidatorSigner, txs, connection); await checkAfterEffects( txDetails!, @@ -2893,25 +2803,13 @@ describe("Matching Engine", function () { .toNumber(), ); - const ix = await engine.executeFastOrderCctpIx({ - payer: liquidator.publicKey, - fastVaa, - }); - - const computeIx = ComputeBudgetProgram.setComputeUnitLimit({ - units: 300_000, - }); - - const { value: lookupTableAccount } = await connection.getAddressLookupTable( - lookupTableAddress, - ); - const txDetails = await expectIxOkDetails( - connection, - [computeIx, ix], - [liquidator], - { addressLookupTableAccounts: [lookupTableAccount!] }, + const txs = engine.executeFastOrder( + liquidator.publicKey, + fastVaaAccount.vaa("FastTransfer:FastMarketOrder"), ); + const txDetails = await expectTxsOkDetails(liquidatorSigner, txs, connection); + await checkAfterEffects( txDetails!, auction, @@ -3095,16 +2993,14 @@ describe("Matching Engine", function () { // Start the auction with offer two so that we can // check that the initial offer is refunded. const result = await placeInitialOfferCctpForTest( - { - payer: playerTwo.publicKey, - }, + { payer: playerTwo.publicKey }, { signers: [playerTwoSigner], finalized: false, fastMarketOrder: baseFastOrder, }, ); - const { fastVaa, auctionDataBefore } = result!; + const { auctionDataBefore, fastVaaAccount } = result!; const { duration, gracePeriod } = await engine.fetchAuctionParameters(); await waitUntilSlot( @@ -3112,27 +3008,25 @@ describe("Matching Engine", function () { auctionDataBefore.info!.startSlot.addn(duration + gracePeriod - 1).toNumber(), ); - const computeIx = ComputeBudgetProgram.setComputeUnitLimit({ - units: 250_000, - }); - const ix = await engine.executeFastOrderCctpIx({ - payer: playerOne.publicKey, - fastVaa, - }); + localVariables.set("acct", fastVaaAccount); - await expectIxOk(connection, [computeIx, ix], [playerOne]); + const txs = engine.executeFastOrder( + playerOne.publicKey, + fastVaaAccount.vaa("FastTransfer:FastMarketOrder"), + ); - localVariables.set("ix", ix); + await expectTxsOk(playerOneSigner, txs); }); it("Cannot Execute Fast Order on Auction Completed", async function () { - const ix = localVariables.get("ix") as TransactionInstruction; - expect(localVariables.delete("ix")).is.true; - - await expectIxErr( - connection, - [ix], - [playerOne], + const fastVaaAccount = localVariables.get("acct") as VaaAccount; + const txs = engine.executeFastOrder( + playerOne.publicKey, + fastVaaAccount.vaa("FastTransfer:FastMarketOrder"), + ); + await expectTxsErr( + playerOneSigner, + txs, "custody_token. Error Code: AccountNotInitialized", ); }); @@ -3146,19 +3040,14 @@ describe("Matching Engine", function () { fastMarketOrder: baseFastOrder, }, ); - const { fastVaa } = result!; - - const ix = await engine.executeFastOrderCctpIx({ - payer: playerOne.publicKey, - fastVaa, - }); + const { fastVaaAccount } = result!; - await expectIxErr( - connection, - [ix], - [playerOne], - "Error Code: AuctionPeriodNotExpired", + const txs = engine.executeFastOrder( + playerOne.publicKey, + fastVaaAccount.vaa("FastTransfer:FastMarketOrder"), ); + + await expectTxsErr(playerOneSigner, txs, "Error Code: AuctionPeriodNotExpired"); }); async function checkAfterEffects( @@ -3583,9 +3472,7 @@ describe("Matching Engine", function () { it("Cannot Settle Active Auction", async function () { await settleAuctionCompleteForTest( - { - executor: payer.publicKey, - }, + { executor: payer.publicKey }, { prepareSigners: [payer], executeOrder: false, @@ -3596,9 +3483,7 @@ describe("Matching Engine", function () { it("Cannot Settle Completed Auction with No Penalty (Executor != Best Offer)", async function () { await settleAuctionCompleteForTest( - { - executor: payer.publicKey, - }, + { executor: payer.publicKey }, { prepareSigners: [payer], executeWithinGracePeriod: true, @@ -3609,9 +3494,7 @@ describe("Matching Engine", function () { it("Settle Completed without Penalty", async function () { await settleAuctionCompleteForTest( - { - executor: playerOne.publicKey, - }, + { executor: playerOne.publicKey }, { prepareSigners: [playerOne], executeWithinGracePeriod: true, @@ -3621,9 +3504,7 @@ describe("Matching Engine", function () { it("Settle Completed With Order Response Prepared Before Active Auction", async function () { await settleAuctionCompleteForTest( - { - executor: playerOne.publicKey, - }, + { executor: playerOne.publicKey }, { prepareSigners: [playerOne], executeWithinGracePeriod: true, @@ -3634,9 +3515,7 @@ describe("Matching Engine", function () { it("Cannot Settle Completed with Penalty (Executor != Prepared By)", async function () { await settleAuctionCompleteForTest( - { - executor: playerOne.publicKey, - }, + { executor: playerOne.publicKey }, { executeWithinGracePeriod: false, executorIsPreparer: false, @@ -3647,9 +3526,7 @@ describe("Matching Engine", function () { it("Settle Completed with Penalty (Executor == Best Offer)", async function () { await settleAuctionCompleteForTest( - { - executor: playerOne.publicKey, - }, + { executor: playerOne.publicKey }, { prepareSigners: [playerOne], executeWithinGracePeriod: false,