Skip to content
This repository was archived by the owner on Jun 16, 2025. It is now read-only.

Commit c7abb1b

Browse files
authored
solana: add executor to reserve fast fill (#172)
Co-authored-by: A5 Pickle <[email protected]>
1 parent 7a4e50a commit c7abb1b

File tree

5 files changed

+76
-24
lines changed

5 files changed

+76
-24
lines changed

solana/programs/matching-engine/src/processor/auction/execute_fast_order/local.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,14 +41,15 @@ pub struct ExecuteFastOrderLocal<'info> {
4141
reserved_sequence: Account<'info, ReservedFastFillSequence>,
4242

4343
/// When the reserved sequence account was created, the beneficiary was set to the best offer
44-
/// token's owner. This account will receive the lamports from the reserved sequence account.
44+
/// token's owner if it existed (and if not, to whomever executed the reserve fast fill sequence
45+
/// instruction). This account will receive the lamports from the reserved sequence account.
4546
///
4647
/// CHECK: This account's address must equal the one encoded in the reserved sequence account.
4748
#[account(
4849
mut,
4950
address = reserved_sequence.beneficiary,
5051
)]
51-
best_offer_participant: UncheckedAccount<'info>,
52+
reserve_beneficiary: UncheckedAccount<'info>,
5253

5354
#[account(
5455
init,
@@ -150,5 +151,5 @@ pub fn execute_fast_order_local(ctx: Context<ExecuteFastOrderLocal>) -> Result<(
150151
// participant.
151152
ctx.accounts
152153
.reserved_sequence
153-
.close(ctx.accounts.best_offer_participant.to_account_info())
154+
.close(ctx.accounts.reserve_beneficiary.to_account_info())
154155
}

solana/programs/matching-engine/src/processor/fast_fill/reserve_sequence/active_auction.rs

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,9 @@ pub struct ReserveFastFillSequenceActiveAuction<'info> {
6363

6464
/// Best offer token account, whose owner will be the beneficiary of the reserved fast fill
6565
/// sequence account when it is closed.
66+
///
67+
/// CHECK: This account may not exist. If it does, it should equal the best offer token pubkey
68+
/// in the auction account.
6669
#[account(
6770
constraint = {
6871
// We know from the auction constraint that the auction is active, so the auction info
@@ -81,16 +84,36 @@ pub struct ReserveFastFillSequenceActiveAuction<'info> {
8184
true
8285
}
8386
)]
84-
best_offer_token: Account<'info, token::TokenAccount>,
87+
best_offer_token: UncheckedAccount<'info>,
88+
89+
/// CHECK: If the best offer token does not exist anymore, this executor will be the beneficiary
90+
/// of the reserved fast fill sequence account when it is closed. Otherwise, this account must
91+
/// equal the best offer token account's owner.
92+
executor: UncheckedAccount<'info>,
8593
}
8694

8795
pub fn reserve_fast_fill_sequence_active_auction(
8896
ctx: Context<ReserveFastFillSequenceActiveAuction>,
8997
) -> Result<()> {
98+
let best_offer_token = &ctx.accounts.best_offer_token;
99+
let beneficiary = ctx.accounts.executor.key();
100+
101+
// If the token account does exist, we will constrain that the executor is the best offer token.
102+
if let Ok(token) =
103+
token::TokenAccount::try_deserialize(&mut &best_offer_token.data.borrow()[..])
104+
{
105+
require_keys_eq!(
106+
*best_offer_token.owner,
107+
token::ID,
108+
ErrorCode::ConstraintTokenTokenProgram
109+
);
110+
require_keys_eq!(token.owner, beneficiary, ErrorCode::ConstraintTokenOwner);
111+
}
112+
90113
super::set_reserved_sequence_data(
91114
&mut ctx.accounts.reserve_sequence,
92115
&ctx.bumps.reserve_sequence,
93116
ctx.accounts.auction.vaa_hash,
94-
ctx.accounts.best_offer_token.owner,
117+
beneficiary,
95118
)
96119
}

solana/ts/src/idl/json/matching_engine.json

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -904,10 +904,11 @@
904904
"writable": true
905905
},
906906
{
907-
"name": "best_offer_participant",
907+
"name": "reserve_beneficiary",
908908
"docs": [
909909
"When the reserved sequence account was created, the beneficiary was set to the best offer",
910-
"token's owner. This account will receive the lamports from the reserved sequence account.",
910+
"token's owner if it existed (and if not, to whomever executed the reserve fast fill sequence",
911+
"instruction). This account will receive the lamports from the reserved sequence account.",
911912
""
912913
],
913914
"writable": true
@@ -1646,7 +1647,16 @@
16461647
"name": "best_offer_token",
16471648
"docs": [
16481649
"Best offer token account, whose owner will be the beneficiary of the reserved fast fill",
1649-
"sequence account when it is closed."
1650+
"sequence account when it is closed.",
1651+
"",
1652+
"in the auction account."
1653+
]
1654+
},
1655+
{
1656+
"name": "executor",
1657+
"docs": [
1658+
"of the reserved fast fill sequence account when it is closed. Otherwise, this account must",
1659+
"equal the best offer token account's owner."
16501660
]
16511661
}
16521662
],

solana/ts/src/idl/ts/matching_engine.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -910,10 +910,11 @@ export type MatchingEngine = {
910910
"writable": true
911911
},
912912
{
913-
"name": "bestOfferParticipant",
913+
"name": "reserveBeneficiary",
914914
"docs": [
915915
"When the reserved sequence account was created, the beneficiary was set to the best offer",
916-
"token's owner. This account will receive the lamports from the reserved sequence account.",
916+
"token's owner if it existed (and if not, to whomever executed the reserve fast fill sequence",
917+
"instruction). This account will receive the lamports from the reserved sequence account.",
917918
""
918919
],
919920
"writable": true
@@ -1652,7 +1653,16 @@ export type MatchingEngine = {
16521653
"name": "bestOfferToken",
16531654
"docs": [
16541655
"Best offer token account, whose owner will be the beneficiary of the reserved fast fill",
1655-
"sequence account when it is closed."
1656+
"sequence account when it is closed.",
1657+
"",
1658+
"in the auction account."
1659+
]
1660+
},
1661+
{
1662+
"name": "executor",
1663+
"docs": [
1664+
"of the reserved fast fill sequence account when it is closed. Otherwise, this account must",
1665+
"equal the best offer token account's owner."
16561666
]
16571667
}
16581668
],

solana/ts/src/matchingEngine/index.ts

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1789,7 +1789,7 @@ export class MatchingEngineProgram {
17891789
sourceChain ??= fastVaaAccount.emitterInfo().chain;
17901790

17911791
const message = LiquidityLayerMessage.decode(fastVaaAccount.payload());
1792-
if (message.fastMarketOrder == undefined) {
1792+
if (message.fastMarketOrder === undefined) {
17931793
throw new Error("Message not FastMarketOrder");
17941794
}
17951795

@@ -2135,6 +2135,7 @@ export class MatchingEngineProgram {
21352135
auction?: PublicKey;
21362136
auctionConfig?: PublicKey;
21372137
bestOfferToken?: PublicKey;
2138+
executor?: PublicKey;
21382139
},
21392140
opts: ReserveFastFillSequenceCompositeOpts = {},
21402141
): Promise<TransactionInstruction> {
@@ -2153,7 +2154,7 @@ export class MatchingEngineProgram {
21532154
);
21542155
const { fastVaaHash } = definedOpts;
21552156

2156-
let { auctionConfig, auction, bestOfferToken } = accounts;
2157+
let { auctionConfig, auction, bestOfferToken, executor } = accounts;
21572158
auction ??= this.auctionAddress(fastVaaHash);
21582159

21592160
if (bestOfferToken === undefined || auctionConfig === undefined) {
@@ -2165,13 +2166,24 @@ export class MatchingEngineProgram {
21652166
bestOfferToken ??= info.bestOfferToken;
21662167
}
21672168

2169+
if (executor === undefined) {
2170+
const token = await splToken
2171+
.getAccount(this.program.provider.connection, bestOfferToken)
2172+
.catch((_) => null);
2173+
if (token === null) {
2174+
throw new Error("Executor must be provided because best offer token is not found");
2175+
}
2176+
executor = token.owner;
2177+
}
2178+
21682179
return this.program.methods
21692180
.reserveFastFillSequenceActiveAuction()
21702181
.accounts({
21712182
reserveSequence,
21722183
auction,
21732184
auctionConfig,
21742185
bestOfferToken,
2186+
executor,
21752187
})
21762188
.instruction();
21772189
}
@@ -2229,7 +2241,7 @@ export class MatchingEngineProgram {
22292241
bestOfferToken?: PublicKey;
22302242
initialOfferToken?: PublicKey;
22312243
initialParticipant?: PublicKey;
2232-
bestOfferParticipant?: PublicKey;
2244+
reserveBeneficiary?: PublicKey;
22332245
},
22342246
opts: {
22352247
sourceChain?: ChainId;
@@ -2247,7 +2259,7 @@ export class MatchingEngineProgram {
22472259
executorToken,
22482260
initialOfferToken,
22492261
initialParticipant,
2250-
bestOfferParticipant,
2262+
reserveBeneficiary,
22512263
} = accounts;
22522264
let { sourceChain, orderSender, sequence } = opts;
22532265
executorToken ??= splToken.getAssociatedTokenAddressSync(this.mint, payer);
@@ -2271,11 +2283,12 @@ export class MatchingEngineProgram {
22712283
orderSender ??= Array.from(fastMarketOrder.sender.toUint8Array());
22722284
}
22732285

2274-
if (sequence === undefined) {
2275-
const { fastFillSeeds } = await this.fetchReservedFastFillSequence({
2286+
if (sequence === undefined || reserveBeneficiary === undefined) {
2287+
const reservedData = await this.fetchReservedFastFillSequence({
22762288
address: reservedSequence,
22772289
});
2278-
sequence = fastFillSeeds.sequence;
2290+
sequence ??= reservedData.fastFillSeeds.sequence;
2291+
reserveBeneficiary ??= reservedData.beneficiary;
22792292
}
22802293

22812294
let auctionInfo: AuctionInfo | undefined;
@@ -2302,11 +2315,6 @@ export class MatchingEngineProgram {
23022315
{ auctionInfo },
23032316
);
23042317

2305-
if (bestOfferParticipant === undefined) {
2306-
const token = await splToken.getAccount(connection, activeAuction.bestOfferToken);
2307-
bestOfferParticipant = token.owner;
2308-
}
2309-
23102318
return this.program.methods
23112319
.executeFastOrderLocal()
23122320
.accounts({
@@ -2320,7 +2328,7 @@ export class MatchingEngineProgram {
23202328
initialParticipant,
23212329
},
23222330
reservedSequence,
2323-
bestOfferParticipant,
2331+
reserveBeneficiary,
23242332
fastFill: this.fastFillAddress(sourceChain, orderSender, sequence),
23252333
eventAuthority: this.eventAuthorityAddress(),
23262334
program: this.ID,

0 commit comments

Comments
 (0)