1
- use anchor_lang:: prelude:: * ;
1
+ use std:: cell:: Ref ;
2
+
3
+ use anchor_lang:: { prelude:: * , Discriminator } ;
2
4
use anchor_spl:: token:: spl_token;
3
5
use solana_program:: {
4
6
entrypoint:: ProgramResult ,
5
7
instruction:: { AccountMeta , Instruction } ,
8
+ keccak,
6
9
program:: invoke_signed_unchecked,
7
10
program_pack:: Pack ,
8
11
system_instruction,
9
12
} ;
10
13
11
- use crate :: ID ;
14
+ use crate :: {
15
+ error:: MatchingEngineError ,
16
+ state:: { AuctionConfig , Custodian , FastMarketOrder , MessageProtocol , RouterEndpoint } ,
17
+ ID ,
18
+ } ;
12
19
13
20
#[ inline( always) ]
14
21
pub fn require_min_account_infos_len ( accounts : & [ AccountInfo ] , at_least_len : usize ) -> Result < ( ) > {
@@ -20,11 +27,119 @@ pub fn require_min_account_infos_len(accounts: &[AccountInfo], at_least_len: usi
20
27
}
21
28
22
29
#[ inline( always) ]
23
- pub fn check_custodian_owner_is_program_id ( custodian : & AccountInfo ) -> Result < ( ) > {
24
- require_eq ! ( custodian. owner, & ID , ErrorCode :: ConstraintOwner ) ;
30
+ pub fn require_owned_by_this_program ( account : & AccountInfo , account_name : & str ) -> Result < ( ) > {
31
+ if account. owner != & ID {
32
+ return Err ( ErrorCode :: ConstraintOwner . into ( ) )
33
+ . map_err ( |e : Error | e. with_account_name ( account_name) ) ;
34
+ }
35
+
25
36
Ok ( ( ) )
26
37
}
27
38
39
+ #[ inline( always) ]
40
+ pub fn try_custodian_account (
41
+ custodian_info : & AccountInfo ,
42
+ check_if_paused : bool ,
43
+ ) -> Result < Box < Custodian > > {
44
+ super :: helpers:: require_owned_by_this_program ( custodian_info, "custodian" ) ?;
45
+
46
+ let custodian =
47
+ Custodian :: try_deserialize ( & mut & custodian_info. data . borrow ( ) [ ..] ) . map ( Box :: new) ?;
48
+
49
+ // Make sure the custodian is not paused.
50
+ if check_if_paused && custodian. paused {
51
+ return Err ( MatchingEngineError :: Paused . into ( ) ) ;
52
+ }
53
+
54
+ Ok ( custodian)
55
+ }
56
+
57
+ #[ inline( always) ]
58
+ pub fn try_auction_config_account (
59
+ auction_config_info : & AccountInfo ,
60
+ expected_config_id : Option < u32 > ,
61
+ ) -> Result < Box < AuctionConfig > > {
62
+ super :: helpers:: require_owned_by_this_program ( auction_config_info, "auction_config" ) ?;
63
+
64
+ let auction_config =
65
+ AuctionConfig :: try_deserialize ( & mut & auction_config_info. data . borrow ( ) [ ..] )
66
+ . map ( Box :: new) ?;
67
+
68
+ // Make sure the custodian is not paused.
69
+ if let Some ( expected_config_id) = expected_config_id {
70
+ if auction_config. id != expected_config_id {
71
+ msg ! ( "Auction config id is invalid" ) ;
72
+ return Err ( ErrorCode :: ConstraintRaw . into ( ) )
73
+ . map_err ( |e : Error | e. with_account_name ( "auction_config" ) ) ;
74
+ }
75
+ }
76
+
77
+ Ok ( auction_config)
78
+ }
79
+
80
+ #[ inline( always) ]
81
+ pub fn try_live_endpoint_account (
82
+ endpoint_info : & AccountInfo ,
83
+ endpoint_name : & str ,
84
+ ) -> Result < Box < RouterEndpoint > > {
85
+ super :: helpers:: require_owned_by_this_program ( endpoint_info, endpoint_name) ?;
86
+
87
+ let endpoint =
88
+ RouterEndpoint :: try_deserialize ( & mut & endpoint_info. data . borrow ( ) [ ..] ) . map ( Box :: new) ?;
89
+
90
+ if endpoint. protocol == MessageProtocol :: None {
91
+ return Err ( MatchingEngineError :: EndpointDisabled . into ( ) ) ;
92
+ }
93
+
94
+ Ok ( endpoint)
95
+ }
96
+
97
+ #[ inline( always) ]
98
+ pub fn try_live_endpoint_accounts_path (
99
+ from_endpoint_info : & AccountInfo ,
100
+ to_endpoint_info : & AccountInfo ,
101
+ ) -> Result < ( Box < RouterEndpoint > , Box < RouterEndpoint > ) > {
102
+ let from_endpoint = try_live_endpoint_account ( from_endpoint_info, "from_endpoint" ) ?;
103
+ let to_endpoint = try_live_endpoint_account ( to_endpoint_info, "to_endpoint" ) ?;
104
+
105
+ if from_endpoint. chain == to_endpoint. chain {
106
+ return Err ( MatchingEngineError :: SameEndpoint . into ( ) ) ;
107
+ }
108
+
109
+ Ok ( ( from_endpoint, to_endpoint) )
110
+ }
111
+
112
+ pub fn try_usdc_account < ' a , ' b > ( usdc_info : & ' a AccountInfo < ' b > ) -> Result < & ' a AccountInfo < ' b > > {
113
+ if usdc_info. key != & common:: USDC_MINT {
114
+ return Err ( MatchingEngineError :: InvalidMint . into ( ) )
115
+ . map_err ( |e : Error | e. with_account_name ( "usdc" ) ) ;
116
+ }
117
+
118
+ Ok ( usdc_info)
119
+ }
120
+
121
+ /// Read from an account info
122
+ pub fn try_fast_market_order_account < ' a > (
123
+ fast_market_order_info : & ' a AccountInfo ,
124
+ ) -> Result < Ref < ' a , FastMarketOrder > > {
125
+ let data = fast_market_order_info. data . borrow ( ) ;
126
+
127
+ if data. len ( ) < 8 {
128
+ return Err ( ErrorCode :: AccountDiscriminatorNotFound . into ( ) ) ;
129
+ }
130
+
131
+ if & data[ 0 ..8 ] != & FastMarketOrder :: DISCRIMINATOR {
132
+ return Err ( ErrorCode :: AccountDiscriminatorMismatch . into ( ) ) ;
133
+ }
134
+
135
+ // TODO: Move up?
136
+ super :: helpers:: require_owned_by_this_program ( fast_market_order_info, "fast_market_order" ) ?;
137
+
138
+ Ok ( Ref :: map ( data, |data| {
139
+ bytemuck:: from_bytes ( & data[ 8 ..8 + std:: mem:: size_of :: < FastMarketOrder > ( ) ] )
140
+ } ) )
141
+ }
142
+
28
143
pub fn create_account_reliably (
29
144
payer_key : & Pubkey ,
30
145
account_key : & Pubkey ,
@@ -180,3 +295,72 @@ pub fn create_usdc_token_account_reliably(
180
295
181
296
solana_program:: program:: invoke_signed_unchecked ( & init_token_account_ix, accounts, & [ ] )
182
297
}
298
+
299
+ /// VaaMessageBodyHeader for the digest calculation
300
+ ///
301
+ /// This is the header of the vaa message body. It is used to calculate the
302
+ /// digest of the fast market order.
303
+ #[ derive( Debug ) ]
304
+ pub struct VaaMessageBodyHeader {
305
+ pub consistency_level : u8 ,
306
+ pub timestamp : u32 ,
307
+ pub sequence : u64 ,
308
+ pub emitter_chain : u16 ,
309
+ pub emitter_address : [ u8 ; 32 ] ,
310
+ }
311
+
312
+ impl VaaMessageBodyHeader {
313
+ // TODO: Remove
314
+ pub fn new (
315
+ consistency_level : u8 ,
316
+ timestamp : u32 ,
317
+ sequence : u64 ,
318
+ emitter_chain : u16 ,
319
+ emitter_address : [ u8 ; 32 ] ,
320
+ ) -> Self {
321
+ Self {
322
+ consistency_level,
323
+ timestamp,
324
+ sequence,
325
+ emitter_chain,
326
+ emitter_address,
327
+ }
328
+ }
329
+
330
+ /// This function creates both the message body for the fast market order, including the payload.
331
+ pub fn message_body ( & self , fast_market_order : & FastMarketOrder ) -> Vec < u8 > {
332
+ let mut message_body = vec ! [ ] ;
333
+ message_body. extend_from_slice ( & self . timestamp . to_be_bytes ( ) ) ;
334
+ message_body. extend_from_slice ( & [ 0 , 0 , 0 , 0 ] ) ; // 0 nonce
335
+ message_body. extend_from_slice ( & self . emitter_chain . to_be_bytes ( ) ) ;
336
+ message_body. extend_from_slice ( & self . emitter_address ) ;
337
+ message_body. extend_from_slice ( & self . sequence . to_be_bytes ( ) ) ;
338
+ message_body. extend_from_slice ( & [ self . consistency_level ] ) ;
339
+ message_body. push ( 11_u8 ) ;
340
+ message_body. extend_from_slice ( & fast_market_order. amount_in . to_be_bytes ( ) ) ;
341
+ message_body. extend_from_slice ( & fast_market_order. min_amount_out . to_be_bytes ( ) ) ;
342
+ message_body. extend_from_slice ( & fast_market_order. target_chain . to_be_bytes ( ) ) ;
343
+ message_body. extend_from_slice ( & fast_market_order. redeemer ) ;
344
+ message_body. extend_from_slice ( & fast_market_order. sender ) ;
345
+ message_body. extend_from_slice ( & fast_market_order. refund_address ) ;
346
+ message_body. extend_from_slice ( & fast_market_order. max_fee . to_be_bytes ( ) ) ;
347
+ message_body. extend_from_slice ( & fast_market_order. init_auction_fee . to_be_bytes ( ) ) ;
348
+ message_body. extend_from_slice ( & fast_market_order. deadline . to_be_bytes ( ) ) ;
349
+ message_body. extend_from_slice ( & fast_market_order. redeemer_message_length . to_be_bytes ( ) ) ;
350
+ if fast_market_order. redeemer_message_length > 0 {
351
+ message_body. extend_from_slice (
352
+ & fast_market_order. redeemer_message
353
+ [ ..usize:: from ( fast_market_order. redeemer_message_length ) ] ,
354
+ ) ;
355
+ }
356
+ message_body
357
+ }
358
+
359
+ /// The digest is the hash of the message hash.
360
+ pub fn digest ( & self , fast_market_order : & FastMarketOrder ) -> keccak:: Hash {
361
+ wormhole_svm_definitions:: compute_keccak_digest (
362
+ keccak:: hashv ( & [ & self . message_body ( fast_market_order) ] ) ,
363
+ None ,
364
+ )
365
+ }
366
+ }
0 commit comments