1
+ use std:: collections:: HashSet ;
2
+
1
3
use cosmwasm_std:: {
2
4
entry_point,
3
5
to_binary,
@@ -11,6 +13,7 @@ use cosmwasm_std::{
11
13
StdResult ,
12
14
Timestamp ,
13
15
WasmQuery ,
16
+ StdError ,
14
17
} ;
15
18
16
19
use pyth_sdk:: {
@@ -35,6 +38,7 @@ use crate::state::{
35
38
ConfigInfo ,
36
39
PriceInfo ,
37
40
VALID_TIME_PERIOD ,
41
+ PythDataSource ,
38
42
} ;
39
43
40
44
use p2w_sdk:: BatchPriceAttestation ;
@@ -52,14 +56,17 @@ pub fn migrate(_deps: DepsMut, _env: Env, _msg: MigrateMsg) -> StdResult<Respons
52
56
pub fn instantiate (
53
57
deps : DepsMut ,
54
58
_env : Env ,
55
- _info : MessageInfo ,
59
+ info : MessageInfo ,
56
60
msg : InstantiateMsg ,
57
61
) -> StdResult < Response > {
58
62
// Save general wormhole and pyth info
59
63
let state = ConfigInfo {
64
+ owner : info. sender . to_string ( ) ,
60
65
wormhole_contract : msg. wormhole_contract ,
61
- pyth_emitter : msg. pyth_emitter . as_slice ( ) . to_vec ( ) ,
62
- pyth_emitter_chain : msg. pyth_emitter_chain ,
66
+ data_sources : HashSet :: from ( [ PythDataSource {
67
+ emitter : msg. pyth_emitter ,
68
+ pyth_emitter_chain : msg. pyth_emitter_chain ,
69
+ } ] ) ,
63
70
} ;
64
71
config ( deps. storage ) . save ( & state) ?;
65
72
@@ -82,6 +89,8 @@ pub fn parse_vaa(deps: DepsMut, block_time: u64, data: &Binary) -> StdResult<Par
82
89
pub fn execute ( deps : DepsMut , env : Env , info : MessageInfo , msg : ExecuteMsg ) -> StdResult < Response > {
83
90
match msg {
84
91
ExecuteMsg :: SubmitVaa { data } => submit_vaa ( deps, env, info, & data) ,
92
+ ExecuteMsg :: AddDataSource { data_source } => add_data_source ( deps, env, info, data_source ) ,
93
+ ExecuteMsg :: RemoveDataSource { data_source } => remove_data_source ( deps, env, info, data_source ) ,
85
94
}
86
95
}
87
96
@@ -104,10 +113,75 @@ fn submit_vaa(
104
113
process_batch_attestation ( deps, env, & batch_attestation)
105
114
}
106
115
116
+ fn add_data_source (
117
+ deps : DepsMut ,
118
+ _env : Env ,
119
+ info : MessageInfo ,
120
+ data_source : PythDataSource ,
121
+ ) -> StdResult < Response > {
122
+ let mut state = config_read ( deps. storage ) . load ( ) ?;
123
+
124
+ if state. owner != info. sender {
125
+ return ContractError :: PermissionDenied . std_err ( ) ;
126
+ }
127
+
128
+ if state. data_sources . insert ( data_source. clone ( ) ) == false {
129
+ return Err ( StdError :: GenericErr { msg : format ! ( "Data source already exists" ) } ) ;
130
+ }
131
+
132
+ config ( deps. storage ) . save ( & state) ?;
133
+
134
+ Ok ( Response :: new ( )
135
+ . add_attribute ( "action" , "add_data_source" )
136
+ . add_attribute (
137
+ "data_source_emitter" ,
138
+ format ! ( "{}" , data_source. emitter) ,
139
+ )
140
+ . add_attribute (
141
+ "data_source_emitter_chain" ,
142
+ format ! ( "{}" , data_source. pyth_emitter_chain)
143
+ ) )
144
+ }
145
+
146
+ fn remove_data_source (
147
+ deps : DepsMut ,
148
+ _env : Env ,
149
+ info : MessageInfo ,
150
+ data_source : PythDataSource ,
151
+ ) -> StdResult < Response > {
152
+ let mut state = config_read ( deps. storage ) . load ( ) ?;
153
+
154
+ if state. owner != info. sender {
155
+ return ContractError :: PermissionDenied . std_err ( ) ;
156
+ }
157
+
158
+ if state. data_sources . remove ( & data_source) == false {
159
+ return Err ( StdError :: GenericErr { msg : format ! ( "Data source does not exist" ) } ) ;
160
+ }
161
+
162
+ config ( deps. storage ) . save ( & state) ?;
163
+
164
+ Ok ( Response :: new ( )
165
+ . add_attribute ( "action" , "remove_data_source" )
166
+ . add_attribute (
167
+ "data_source_emitter" ,
168
+ format ! ( "{}" , data_source. emitter) ,
169
+ )
170
+ . add_attribute (
171
+ "data_source_emitter_chain" ,
172
+ format ! ( "{}" , data_source. pyth_emitter_chain)
173
+ ) )
174
+ }
175
+
176
+
107
177
// This checks the emitter to be the pyth emitter in wormhole and it comes from emitter chain
108
178
// (Solana)
109
179
fn verify_vaa_sender ( state : & ConfigInfo , vaa : & ParsedVAA ) -> StdResult < ( ) > {
110
- if vaa. emitter_address != state. pyth_emitter || vaa. emitter_chain != state. pyth_emitter_chain {
180
+ let vaa_data_source = PythDataSource {
181
+ emitter : vaa. emitter_address . clone ( ) . into ( ) ,
182
+ pyth_emitter_chain : vaa. emitter_chain
183
+ } ;
184
+ if !state. data_sources . contains ( & vaa_data_source) {
111
185
return ContractError :: InvalidVAA . std_err ( ) ;
112
186
}
113
187
Ok ( ( ) )
@@ -246,6 +320,7 @@ mod test {
246
320
MockApi ,
247
321
MockQuerier ,
248
322
MockStorage ,
323
+ mock_info,
249
324
} ;
250
325
use cosmwasm_std:: OwnedDeps ;
251
326
@@ -277,6 +352,15 @@ mod test {
277
352
price_feed
278
353
}
279
354
355
+ fn create_data_sources ( pyth_emitter : Vec < u8 > , pyth_emitter_chain : u16 ) -> HashSet < PythDataSource > {
356
+ HashSet :: from ( [
357
+ PythDataSource {
358
+ emitter : pyth_emitter. into ( ) ,
359
+ pyth_emitter_chain
360
+ }
361
+ ] )
362
+ }
363
+
280
364
/// Updates the price feed with the given attestation time stamp and
281
365
/// returns the update status (true means updated, false means ignored)
282
366
fn do_update_price_feed (
@@ -297,8 +381,7 @@ mod test {
297
381
#[ test]
298
382
fn test_verify_vaa_sender_ok ( ) {
299
383
let config_info = ConfigInfo {
300
- pyth_emitter : vec ! [ 1u8 ] ,
301
- pyth_emitter_chain : 3 ,
384
+ data_sources : create_data_sources ( vec ! [ 1u8 ] , 3 ) ,
302
385
..Default :: default ( )
303
386
} ;
304
387
@@ -312,8 +395,7 @@ mod test {
312
395
#[ test]
313
396
fn test_verify_vaa_sender_fail_wrong_emitter_address ( ) {
314
397
let config_info = ConfigInfo {
315
- pyth_emitter : vec ! [ 1u8 ] ,
316
- pyth_emitter_chain : 3 ,
398
+ data_sources : create_data_sources ( vec ! [ 1u8 ] , 3 ) ,
317
399
..Default :: default ( )
318
400
} ;
319
401
@@ -329,8 +411,7 @@ mod test {
329
411
#[ test]
330
412
fn test_verify_vaa_sender_fail_wrong_emitter_chain ( ) {
331
413
let config_info = ConfigInfo {
332
- pyth_emitter : vec ! [ 1u8 ] ,
333
- pyth_emitter_chain : 3 ,
414
+ data_sources : create_data_sources ( vec ! [ 1u8 ] , 3 ) ,
334
415
..Default :: default ( )
335
416
} ;
336
417
@@ -517,4 +598,107 @@ mod test {
517
598
ContractError :: AssetNotFound . std_err( )
518
599
) ;
519
600
}
601
+
602
+ #[ test]
603
+ fn test_add_data_source_ok_with_owner ( ) {
604
+ let ( mut deps, env) = setup_test ( ) ;
605
+ config ( & mut deps. storage ) . save ( & ConfigInfo {
606
+ owner : String :: from ( "123" ) ,
607
+ ..Default :: default ( )
608
+ } ) . unwrap ( ) ;
609
+
610
+ let data_source = PythDataSource { emitter : vec ! [ 1u8 ] . into ( ) , pyth_emitter_chain : 1 } ;
611
+
612
+ assert ! ( add_data_source( deps. as_mut( ) , env. clone( ) , mock_info( "123" , & [ ] ) , data_source. clone( ) ) . is_ok( ) ) ;
613
+
614
+ // Adding an existing data source should result an error
615
+ assert ! ( add_data_source( deps. as_mut( ) , env. clone( ) , mock_info( "123" , & [ ] ) , data_source. clone( ) ) . is_err( ) ) ;
616
+ }
617
+
618
+ #[ test]
619
+ fn test_add_data_source_err_without_owner ( ) {
620
+ let ( mut deps, env) = setup_test ( ) ;
621
+ config ( & mut deps. storage ) . save ( & ConfigInfo {
622
+ owner : String :: from ( "123" ) ,
623
+ ..Default :: default ( )
624
+ } ) . unwrap ( ) ;
625
+
626
+ let data_source = PythDataSource { emitter : vec ! [ 1u8 ] . into ( ) , pyth_emitter_chain : 1 } ;
627
+
628
+ assert ! ( add_data_source( deps. as_mut( ) , env, mock_info( "321" , & [ ] ) , data_source) . is_err( ) ) ;
629
+ }
630
+
631
+ #[ test]
632
+ fn test_remove_data_source_ok_with_owner ( ) {
633
+ let ( mut deps, env) = setup_test ( ) ;
634
+ config ( & mut deps. storage ) . save ( & ConfigInfo {
635
+ owner : String :: from ( "123" ) ,
636
+ data_sources : create_data_sources ( vec ! [ 1u8 ] , 1 ) ,
637
+ ..Default :: default ( )
638
+ } ) . unwrap ( ) ;
639
+
640
+ let data_source = PythDataSource { emitter : vec ! [ 1u8 ] . into ( ) , pyth_emitter_chain : 1 } ;
641
+
642
+ assert ! ( remove_data_source( deps. as_mut( ) , env. clone( ) , mock_info( "123" , & [ ] ) , data_source. clone( ) ) . is_ok( ) ) ;
643
+
644
+ // Removing a non existent data source should result an error
645
+ assert ! ( remove_data_source( deps. as_mut( ) , env. clone( ) , mock_info( "123" , & [ ] ) , data_source. clone( ) ) . is_err( ) ) ;
646
+ }
647
+
648
+ #[ test]
649
+ fn test_remove_data_source_err_without_owner ( ) {
650
+ let ( mut deps, env) = setup_test ( ) ;
651
+ config ( & mut deps. storage ) . save ( & ConfigInfo {
652
+ owner : String :: from ( "123" ) ,
653
+ data_sources : create_data_sources ( vec ! [ 1u8 ] , 1 ) ,
654
+ ..Default :: default ( )
655
+ } ) . unwrap ( ) ;
656
+
657
+ let data_source = PythDataSource { emitter : vec ! [ 1u8 ] . into ( ) , pyth_emitter_chain : 1 } ;
658
+
659
+ assert ! ( remove_data_source( deps. as_mut( ) , env, mock_info( "321" , & [ ] ) , data_source) . is_err( ) ) ;
660
+ }
661
+
662
+ #[ test]
663
+ fn test_verify_vaa_works_after_adding_data_source ( ) {
664
+ let ( mut deps, env) = setup_test ( ) ;
665
+ config ( & mut deps. storage ) . save ( & ConfigInfo {
666
+ owner : String :: from ( "123" ) ,
667
+ ..Default :: default ( )
668
+ } ) . unwrap ( ) ;
669
+
670
+ let mut vaa = create_zero_vaa ( ) ;
671
+ vaa. emitter_address = vec ! [ 1u8 ] ;
672
+ vaa. emitter_chain = 3 ;
673
+
674
+ // Should result an error because there is no data source
675
+ assert_eq ! ( verify_vaa_sender( & config_read( & deps. storage) . load( ) . unwrap( ) , & vaa) , ContractError :: InvalidVAA . std_err( ) ) ;
676
+
677
+ let data_source = PythDataSource { emitter : vec ! [ 1u8 ] . into ( ) , pyth_emitter_chain : 3 } ;
678
+ assert ! ( add_data_source( deps. as_mut( ) , env. clone( ) , mock_info( "123" , & [ ] ) , data_source. clone( ) ) . is_ok( ) ) ;
679
+
680
+ assert_eq ! ( verify_vaa_sender( & config_read( & deps. storage) . load( ) . unwrap( ) , & vaa) , Ok ( ( ) ) ) ;
681
+ }
682
+
683
+ #[ test]
684
+ fn test_verify_vaa_err_after_removing_data_source ( ) {
685
+ let ( mut deps, env) = setup_test ( ) ;
686
+ config ( & mut deps. storage ) . save ( & ConfigInfo {
687
+ owner : String :: from ( "123" ) ,
688
+ data_sources : create_data_sources ( vec ! [ 1u8 ] , 3 ) ,
689
+ ..Default :: default ( )
690
+ } ) . unwrap ( ) ;
691
+
692
+ let mut vaa = create_zero_vaa ( ) ;
693
+ vaa. emitter_address = vec ! [ 1u8 ] ;
694
+ vaa. emitter_chain = 3 ;
695
+
696
+ assert_eq ! ( verify_vaa_sender( & config_read( & deps. storage) . load( ) . unwrap( ) , & vaa) , Ok ( ( ) ) ) ;
697
+
698
+ let data_source = PythDataSource { emitter : vec ! [ 1u8 ] . into ( ) , pyth_emitter_chain : 3 } ;
699
+ assert ! ( remove_data_source( deps. as_mut( ) , env. clone( ) , mock_info( "123" , & [ ] ) , data_source. clone( ) ) . is_ok( ) ) ;
700
+
701
+ // Should result an error because data source should not exist anymore
702
+ assert_eq ! ( verify_vaa_sender( & config_read( & deps. storage) . load( ) . unwrap( ) , & vaa) , ContractError :: InvalidVAA . std_err( ) ) ;
703
+ }
520
704
}
0 commit comments