1
1
use crate :: {
2
2
error:: MatchingEngineError ,
3
+ events:: SettledTokenAccountInfo ,
3
4
state:: { Auction , AuctionStatus , PreparedOrderResponse } ,
4
5
} ;
5
6
use anchor_lang:: prelude:: * ;
6
- use anchor_spl:: { associated_token:: get_associated_token_address, token} ;
7
+ use anchor_spl:: {
8
+ associated_token:: get_associated_token_address,
9
+ token:: { self , TokenAccount } ,
10
+ } ;
7
11
8
12
#[ derive( Accounts ) ]
9
13
pub struct SettleAuctionComplete < ' info > {
@@ -16,21 +20,22 @@ pub struct SettleAuctionComplete<'info> {
16
20
17
21
#[ account(
18
22
mut ,
19
- token:: mint = best_offer_token . mint ,
23
+ token:: mint = common :: USDC_MINT ,
20
24
token:: authority = executor,
21
25
) ]
22
- executor_token : Account < ' info , token :: TokenAccount > ,
26
+ executor_token : Account < ' info , TokenAccount > ,
23
27
24
28
/// Destination token account, which the redeemer may not own. But because the redeemer is a
25
29
/// signer and is the one encoded in the Deposit Fill message, he may have the tokens be sent
26
30
/// to any account he chooses (this one).
27
31
///
28
- /// CHECK: This token account must already exist.
32
+ /// CHECK: This token account may exist. If it doesn't and there is a penalty, we will send all
33
+ /// of the tokens to the executor token account.
29
34
#[ account(
30
35
mut ,
31
36
address = auction. info. as_ref( ) . unwrap( ) . best_offer_token,
32
37
) ]
33
- best_offer_token : Account < ' info , token :: TokenAccount > ,
38
+ best_offer_token : UncheckedAccount < ' info > ,
34
39
35
40
#[ account(
36
41
mut ,
@@ -52,7 +57,7 @@ pub struct SettleAuctionComplete<'info> {
52
57
] ,
53
58
bump,
54
59
) ]
55
- prepared_custody_token : Account < ' info , token :: TokenAccount > ,
60
+ prepared_custody_token : Account < ' info , TokenAccount > ,
56
61
57
62
#[ account(
58
63
mut ,
@@ -101,10 +106,14 @@ fn handle_settle_auction_complete(
101
106
let token_program = & ctx. accounts . token_program ;
102
107
let prepared_custody_token = & ctx. accounts . prepared_custody_token ;
103
108
104
- // We may deduct from this account if the winning participant was penalized.
105
- let mut repayment = ctx. accounts . prepared_custody_token . amount ;
109
+ let repayment = ctx. accounts . prepared_custody_token . amount ;
110
+
111
+ struct TokenAccountResult {
112
+ balance_before : u64 ,
113
+ amount : u64 ,
114
+ }
106
115
107
- match execute_penalty {
116
+ let ( executor_result , best_offer_result ) = match execute_penalty {
108
117
None => {
109
118
// If there is no penalty, we require that the executor token and best offer token be
110
119
// equal. The winning offer should not be penalized for calling this instruction when he
@@ -118,6 +127,19 @@ fn handle_settle_auction_complete(
118
127
best_offer_token. key( ) ,
119
128
MatchingEngineError :: ExecutorTokenMismatch
120
129
) ;
130
+
131
+ // If the token account happens to not exist anymore, we will revert.
132
+ match TokenAccount :: try_deserialize ( & mut & best_offer_token. data . borrow ( ) [ ..] ) {
133
+ Ok ( best_offer) => (
134
+ None , // executor_result
135
+ TokenAccountResult {
136
+ balance_before : best_offer. amount ,
137
+ amount : repayment,
138
+ }
139
+ . into ( ) ,
140
+ ) ,
141
+ Err ( err) => return Err ( err) ,
142
+ }
121
143
}
122
144
_ => {
123
145
// If there is a penalty, we want to return the lamports back to the person who paid to
@@ -131,55 +153,121 @@ fn handle_settle_auction_complete(
131
153
MatchingEngineError :: ExecutorNotPreparedBy
132
154
) ;
133
155
134
- if executor_token. key ( ) != best_offer_token. key ( ) {
135
- // Because the auction participant was penalized for executing the order late, he
136
- // will be deducted the base fee. This base fee will be sent to the executor token
137
- // account if it is not the same as the best offer token account.
138
-
139
- // We require that the executor token account be an ATA.
140
- require_keys_eq ! (
141
- executor_token. key( ) ,
142
- get_associated_token_address( & executor_token. owner, & executor_token. mint) ,
143
- ErrorCode :: AccountNotAssociatedTokenAccount
144
- ) ;
145
-
146
- // Transfer base fee to the executor.
147
- token:: transfer (
148
- CpiContext :: new_with_signer (
149
- token_program. to_account_info ( ) ,
150
- token:: Transfer {
151
- from : prepared_custody_token. to_account_info ( ) ,
152
- to : executor_token. to_account_info ( ) ,
153
- authority : prepared_order_response. to_account_info ( ) ,
154
- } ,
155
- & [ prepared_order_response_signer_seeds] ,
156
- ) ,
157
- base_fee,
158
- ) ?;
159
-
160
- repayment = repayment. saturating_sub ( base_fee) ;
156
+ // If the token account happens to not exist anymore, we will give everything to the
157
+ // executor.
158
+ match TokenAccount :: try_deserialize ( & mut & best_offer_token. data . borrow ( ) [ ..] ) {
159
+ Ok ( best_offer) => {
160
+ if executor_token. key ( ) == best_offer_token. key ( ) {
161
+ (
162
+ None , // executor_result
163
+ TokenAccountResult {
164
+ balance_before : best_offer. amount ,
165
+ amount : repayment,
166
+ }
167
+ . into ( ) ,
168
+ )
169
+ } else {
170
+ // Because the auction participant was penalized for executing the order
171
+ // late, he will be deducted the base fee. This base fee will be sent to the
172
+ // executor token account if it is not the same as the best offer token
173
+ // account.
174
+
175
+ // We require that the executor token account be an ATA.
176
+ require_keys_eq ! (
177
+ executor_token. key( ) ,
178
+ get_associated_token_address(
179
+ & executor_token. owner,
180
+ & executor_token. mint
181
+ ) ,
182
+ ErrorCode :: AccountNotAssociatedTokenAccount
183
+ ) ;
184
+
185
+ (
186
+ TokenAccountResult {
187
+ balance_before : executor_token. amount ,
188
+ amount : base_fee,
189
+ }
190
+ . into ( ) ,
191
+ TokenAccountResult {
192
+ balance_before : best_offer. amount ,
193
+ amount : repayment. saturating_sub ( base_fee) ,
194
+ }
195
+ . into ( ) ,
196
+ )
197
+ }
198
+ }
199
+ Err ( _) => (
200
+ TokenAccountResult {
201
+ balance_before : executor_token. amount ,
202
+ amount : repayment,
203
+ }
204
+ . into ( ) ,
205
+ None , // best_offer_result
206
+ ) ,
207
+ }
208
+ }
209
+ } ;
210
+
211
+ // Transfer executor his bounty if there are any.
212
+ let settled_executor_result = match executor_result {
213
+ Some ( TokenAccountResult {
214
+ balance_before,
215
+ amount,
216
+ } ) => {
217
+ token:: transfer (
218
+ CpiContext :: new_with_signer (
219
+ token_program. to_account_info ( ) ,
220
+ token:: Transfer {
221
+ from : prepared_custody_token. to_account_info ( ) ,
222
+ to : executor_token. to_account_info ( ) ,
223
+ authority : prepared_order_response. to_account_info ( ) ,
224
+ } ,
225
+ & [ prepared_order_response_signer_seeds] ,
226
+ ) ,
227
+ amount,
228
+ ) ?;
229
+
230
+ SettledTokenAccountInfo {
231
+ key : executor_token. key ( ) ,
232
+ balance_after : balance_before. saturating_add ( amount) ,
161
233
}
234
+ . into ( )
162
235
}
236
+ None => None ,
163
237
} ;
164
238
165
- // Transfer the funds back to the highest bidder.
166
- token:: transfer (
167
- CpiContext :: new_with_signer (
168
- token_program. to_account_info ( ) ,
169
- token:: Transfer {
170
- from : prepared_custody_token. to_account_info ( ) ,
171
- to : best_offer_token. to_account_info ( ) ,
172
- authority : prepared_order_response. to_account_info ( ) ,
173
- } ,
174
- & [ prepared_order_response_signer_seeds] ,
175
- ) ,
176
- repayment,
177
- ) ?;
239
+ // Transfer the funds back to the highest bidder if there are any.
240
+ let settled_best_offer_result = match best_offer_result {
241
+ Some ( TokenAccountResult {
242
+ balance_before,
243
+ amount,
244
+ } ) => {
245
+ token:: transfer (
246
+ CpiContext :: new_with_signer (
247
+ token_program. to_account_info ( ) ,
248
+ token:: Transfer {
249
+ from : prepared_custody_token. to_account_info ( ) ,
250
+ to : best_offer_token. to_account_info ( ) ,
251
+ authority : prepared_order_response. to_account_info ( ) ,
252
+ } ,
253
+ & [ prepared_order_response_signer_seeds] ,
254
+ ) ,
255
+ amount,
256
+ ) ?;
257
+
258
+ SettledTokenAccountInfo {
259
+ key : best_offer_token. key ( ) ,
260
+ balance_after : balance_before. saturating_add ( amount) ,
261
+ }
262
+ . into ( )
263
+ }
264
+ None => None ,
265
+ } ;
178
266
179
267
emit ! ( crate :: events:: AuctionSettled {
180
268
auction: ctx. accounts. auction. key( ) ,
181
- best_offer_token: best_offer_token . key ( ) . into ( ) ,
182
- token_balance_after : best_offer_token . amount . saturating_add ( repayment ) ,
269
+ best_offer_token: settled_best_offer_result ,
270
+ executor_token : settled_executor_result ,
183
271
with_execute: Default :: default ( ) ,
184
272
} ) ;
185
273
0 commit comments