1use crate::{Client, Error, Pubkey, Result};
4use serde::de::DeserializeOwned;
5use serde::{Deserialize, Serialize, Serializer};
6use serde_json::{json, Value};
7
8#[derive(Debug, Clone, PartialEq, Eq)]
9pub enum RestrictionAddress {
10 Pubkey(Pubkey),
11 String(String),
12}
13
14impl RestrictionAddress {
15 fn as_rpc_string(&self) -> String {
16 match self {
17 Self::Pubkey(pubkey) => pubkey.to_base58(),
18 Self::String(value) => value.clone(),
19 }
20 }
21}
22
23impl Serialize for RestrictionAddress {
24 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
25 where
26 S: Serializer,
27 {
28 serializer.serialize_str(&self.as_rpc_string())
29 }
30}
31
32impl From<Pubkey> for RestrictionAddress {
33 fn from(value: Pubkey) -> Self {
34 Self::Pubkey(value)
35 }
36}
37
38impl From<&Pubkey> for RestrictionAddress {
39 fn from(value: &Pubkey) -> Self {
40 Self::Pubkey(*value)
41 }
42}
43
44impl From<String> for RestrictionAddress {
45 fn from(value: String) -> Self {
46 Self::String(value)
47 }
48}
49
50impl From<&str> for RestrictionAddress {
51 fn from(value: &str) -> Self {
52 Self::String(value.to_string())
53 }
54}
55
56#[derive(Debug, Clone, PartialEq, Eq)]
57pub enum RestrictionAsset {
58 Pubkey(Pubkey),
59 String(String),
60}
61
62impl RestrictionAsset {
63 fn as_rpc_string(&self) -> String {
64 match self {
65 Self::Pubkey(pubkey) => pubkey.to_base58(),
66 Self::String(value) => value.clone(),
67 }
68 }
69}
70
71impl Serialize for RestrictionAsset {
72 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
73 where
74 S: Serializer,
75 {
76 serializer.serialize_str(&self.as_rpc_string())
77 }
78}
79
80impl From<Pubkey> for RestrictionAsset {
81 fn from(value: Pubkey) -> Self {
82 Self::Pubkey(value)
83 }
84}
85
86impl From<&Pubkey> for RestrictionAsset {
87 fn from(value: &Pubkey) -> Self {
88 Self::Pubkey(*value)
89 }
90}
91
92impl From<String> for RestrictionAsset {
93 fn from(value: String) -> Self {
94 Self::String(value)
95 }
96}
97
98impl From<&str> for RestrictionAsset {
99 fn from(value: &str) -> Self {
100 Self::String(value.to_string())
101 }
102}
103
104#[derive(Debug, Clone, Copy, PartialEq, Eq)]
105pub enum BridgeChain {
106 Solana,
107 Ethereum,
108 Bsc,
109 Bnb,
110 NeoX,
111}
112
113impl BridgeChain {
114 pub const fn as_str(self) -> &'static str {
115 match self {
116 Self::Solana => "solana",
117 Self::Ethereum => "ethereum",
118 Self::Bsc => "bsc",
119 Self::Bnb => "bnb",
120 Self::NeoX => "neox",
121 }
122 }
123}
124
125impl Serialize for BridgeChain {
126 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
127 where
128 S: Serializer,
129 {
130 serializer.serialize_str(self.as_str())
131 }
132}
133
134impl From<BridgeChain> for String {
135 fn from(value: BridgeChain) -> Self {
136 value.as_str().to_string()
137 }
138}
139
140#[derive(Debug, Clone, Copy, PartialEq, Eq)]
141pub enum BridgeAsset {
142 Sol,
143 Eth,
144 Bnb,
145 Gas,
146 Neo,
147 Usdc,
148 Usdt,
149}
150
151impl BridgeAsset {
152 pub const fn as_str(self) -> &'static str {
153 match self {
154 Self::Sol => "sol",
155 Self::Eth => "eth",
156 Self::Bnb => "bnb",
157 Self::Gas => "gas",
158 Self::Neo => "neo",
159 Self::Usdc => "usdc",
160 Self::Usdt => "usdt",
161 }
162 }
163}
164
165impl Serialize for BridgeAsset {
166 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
167 where
168 S: Serializer,
169 {
170 serializer.serialize_str(self.as_str())
171 }
172}
173
174impl From<BridgeAsset> for String {
175 fn from(value: BridgeAsset) -> Self {
176 value.as_str().to_string()
177 }
178}
179
180#[derive(Debug, Clone, PartialEq, Eq)]
181pub enum RestrictionStringOrU64 {
182 String(String),
183 U64(u64),
184}
185
186impl Serialize for RestrictionStringOrU64 {
187 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
188 where
189 S: Serializer,
190 {
191 match self {
192 Self::String(value) => serializer.serialize_str(value),
193 Self::U64(value) => serializer.serialize_u64(*value),
194 }
195 }
196}
197
198impl From<u64> for RestrictionStringOrU64 {
199 fn from(value: u64) -> Self {
200 Self::U64(value)
201 }
202}
203
204impl From<u8> for RestrictionStringOrU64 {
205 fn from(value: u8) -> Self {
206 Self::U64(value as u64)
207 }
208}
209
210impl From<String> for RestrictionStringOrU64 {
211 fn from(value: String) -> Self {
212 Self::String(value)
213 }
214}
215
216impl From<&str> for RestrictionStringOrU64 {
217 fn from(value: &str) -> Self {
218 Self::String(value.to_string())
219 }
220}
221
222impl From<lichen_core::ProtocolModuleId> for RestrictionStringOrU64 {
223 fn from(value: lichen_core::ProtocolModuleId) -> Self {
224 Self::String(value.as_str().to_string())
225 }
226}
227
228#[derive(Debug, Clone, PartialEq, Eq)]
229pub enum RestrictionReasonInput {
230 Label(String),
231 Id(u8),
232}
233
234impl Serialize for RestrictionReasonInput {
235 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
236 where
237 S: Serializer,
238 {
239 match self {
240 Self::Label(value) => serializer.serialize_str(value),
241 Self::Id(value) => serializer.serialize_u8(*value),
242 }
243 }
244}
245
246impl From<lichen_core::RestrictionReason> for RestrictionReasonInput {
247 fn from(value: lichen_core::RestrictionReason) -> Self {
248 Self::Label(value.as_str().to_string())
249 }
250}
251
252impl From<u8> for RestrictionReasonInput {
253 fn from(value: u8) -> Self {
254 Self::Id(value)
255 }
256}
257
258impl From<String> for RestrictionReasonInput {
259 fn from(value: String) -> Self {
260 Self::Label(value)
261 }
262}
263
264impl From<&str> for RestrictionReasonInput {
265 fn from(value: &str) -> Self {
266 Self::Label(value.to_string())
267 }
268}
269
270#[derive(Debug, Clone, PartialEq, Eq)]
271pub enum RestrictionLiftReasonInput {
272 Label(String),
273 Id(u8),
274}
275
276impl Serialize for RestrictionLiftReasonInput {
277 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
278 where
279 S: Serializer,
280 {
281 match self {
282 Self::Label(value) => serializer.serialize_str(value),
283 Self::Id(value) => serializer.serialize_u8(*value),
284 }
285 }
286}
287
288impl From<lichen_core::RestrictionLiftReason> for RestrictionLiftReasonInput {
289 fn from(value: lichen_core::RestrictionLiftReason) -> Self {
290 Self::Label(value.as_str().to_string())
291 }
292}
293
294impl From<u8> for RestrictionLiftReasonInput {
295 fn from(value: u8) -> Self {
296 Self::Id(value)
297 }
298}
299
300impl From<String> for RestrictionLiftReasonInput {
301 fn from(value: String) -> Self {
302 Self::Label(value)
303 }
304}
305
306impl From<&str> for RestrictionLiftReasonInput {
307 fn from(value: &str) -> Self {
308 Self::Label(value.to_string())
309 }
310}
311
312#[derive(Debug, Clone, PartialEq, Eq)]
313pub enum RestrictionModeInput {
314 Label(String),
315 Id(u8),
316}
317
318impl Serialize for RestrictionModeInput {
319 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
320 where
321 S: Serializer,
322 {
323 match self {
324 Self::Label(value) => serializer.serialize_str(value),
325 Self::Id(value) => serializer.serialize_u8(*value),
326 }
327 }
328}
329
330impl From<lichen_core::RestrictionMode> for RestrictionModeInput {
331 fn from(value: lichen_core::RestrictionMode) -> Self {
332 Self::Label(value.as_str().to_string())
333 }
334}
335
336impl From<u8> for RestrictionModeInput {
337 fn from(value: u8) -> Self {
338 Self::Id(value)
339 }
340}
341
342impl From<String> for RestrictionModeInput {
343 fn from(value: String) -> Self {
344 Self::Label(value)
345 }
346}
347
348impl From<&str> for RestrictionModeInput {
349 fn from(value: &str) -> Self {
350 Self::Label(value.to_string())
351 }
352}
353
354#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
355#[serde(tag = "type", rename_all = "snake_case")]
356pub enum RestrictionTargetInput {
357 Account {
358 account: RestrictionAddress,
359 },
360 AccountAsset {
361 account: RestrictionAddress,
362 asset: RestrictionAsset,
363 },
364 Asset {
365 asset: RestrictionAsset,
366 },
367 Contract {
368 contract: RestrictionAddress,
369 },
370 CodeHash {
371 code_hash: String,
372 },
373 BridgeRoute {
374 chain: String,
375 asset: String,
376 },
377 ProtocolModule {
378 module: RestrictionStringOrU64,
379 },
380}
381
382impl RestrictionTargetInput {
383 pub fn account(account: impl Into<RestrictionAddress>) -> Self {
384 Self::Account {
385 account: account.into(),
386 }
387 }
388
389 pub fn account_asset(
390 account: impl Into<RestrictionAddress>,
391 asset: impl Into<RestrictionAsset>,
392 ) -> Self {
393 Self::AccountAsset {
394 account: account.into(),
395 asset: asset.into(),
396 }
397 }
398
399 pub fn asset(asset: impl Into<RestrictionAsset>) -> Self {
400 Self::Asset {
401 asset: asset.into(),
402 }
403 }
404
405 pub fn contract(contract: impl Into<RestrictionAddress>) -> Self {
406 Self::Contract {
407 contract: contract.into(),
408 }
409 }
410
411 pub fn code_hash(code_hash: impl Into<String>) -> Self {
412 Self::CodeHash {
413 code_hash: code_hash.into(),
414 }
415 }
416
417 pub fn bridge_route(chain: impl Into<String>, asset: impl Into<String>) -> Self {
418 Self::BridgeRoute {
419 chain: chain.into(),
420 asset: asset.into(),
421 }
422 }
423
424 pub fn protocol_module(module: impl Into<RestrictionStringOrU64>) -> Self {
425 Self::ProtocolModule {
426 module: module.into(),
427 }
428 }
429}
430
431#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize)]
432pub struct RestrictionListParams {
433 #[serde(skip_serializing_if = "Option::is_none")]
434 pub limit: Option<u64>,
435 #[serde(skip_serializing_if = "Option::is_none")]
436 pub after_id: Option<u64>,
437 #[serde(skip_serializing_if = "Option::is_none")]
438 pub cursor: Option<RestrictionStringOrU64>,
439}
440
441#[derive(Debug, Clone, Serialize)]
442pub struct RestrictionBuilderBaseParams {
443 pub proposer: RestrictionAddress,
444 pub governance_authority: RestrictionAddress,
445 #[serde(skip_serializing_if = "Option::is_none")]
446 pub recent_blockhash: Option<String>,
447}
448
449#[derive(Debug, Clone, Serialize)]
450pub struct RestrictCommonParams {
451 pub proposer: RestrictionAddress,
452 pub governance_authority: RestrictionAddress,
453 pub reason: RestrictionReasonInput,
454 #[serde(skip_serializing_if = "Option::is_none")]
455 pub recent_blockhash: Option<String>,
456 #[serde(skip_serializing_if = "Option::is_none")]
457 pub evidence_hash: Option<String>,
458 #[serde(skip_serializing_if = "Option::is_none")]
459 pub evidence_uri_hash: Option<String>,
460 #[serde(skip_serializing_if = "Option::is_none")]
461 pub expires_at_slot: Option<u64>,
462}
463
464#[derive(Debug, Clone, Serialize)]
465pub struct RestrictAccountParams {
466 pub proposer: RestrictionAddress,
467 pub governance_authority: RestrictionAddress,
468 pub account: RestrictionAddress,
469 pub reason: RestrictionReasonInput,
470 #[serde(skip_serializing_if = "Option::is_none")]
471 pub mode: Option<RestrictionModeInput>,
472 #[serde(skip_serializing_if = "Option::is_none")]
473 pub recent_blockhash: Option<String>,
474 #[serde(skip_serializing_if = "Option::is_none")]
475 pub evidence_hash: Option<String>,
476 #[serde(skip_serializing_if = "Option::is_none")]
477 pub evidence_uri_hash: Option<String>,
478 #[serde(skip_serializing_if = "Option::is_none")]
479 pub expires_at_slot: Option<u64>,
480}
481
482#[derive(Debug, Clone, Serialize)]
483pub struct UnrestrictAccountParams {
484 pub proposer: RestrictionAddress,
485 pub governance_authority: RestrictionAddress,
486 pub account: RestrictionAddress,
487 pub lift_reason: RestrictionLiftReasonInput,
488 #[serde(skip_serializing_if = "Option::is_none")]
489 pub restriction_id: Option<u64>,
490 #[serde(skip_serializing_if = "Option::is_none")]
491 pub recent_blockhash: Option<String>,
492}
493
494#[derive(Debug, Clone, Serialize)]
495pub struct RestrictAccountAssetParams {
496 pub proposer: RestrictionAddress,
497 pub governance_authority: RestrictionAddress,
498 pub account: RestrictionAddress,
499 pub asset: RestrictionAsset,
500 pub reason: RestrictionReasonInput,
501 #[serde(skip_serializing_if = "Option::is_none")]
502 pub mode: Option<RestrictionModeInput>,
503 #[serde(skip_serializing_if = "Option::is_none")]
504 pub recent_blockhash: Option<String>,
505 #[serde(skip_serializing_if = "Option::is_none")]
506 pub evidence_hash: Option<String>,
507 #[serde(skip_serializing_if = "Option::is_none")]
508 pub evidence_uri_hash: Option<String>,
509 #[serde(skip_serializing_if = "Option::is_none")]
510 pub expires_at_slot: Option<u64>,
511}
512
513#[derive(Debug, Clone, Serialize)]
514pub struct UnrestrictAccountAssetParams {
515 pub proposer: RestrictionAddress,
516 pub governance_authority: RestrictionAddress,
517 pub account: RestrictionAddress,
518 pub asset: RestrictionAsset,
519 pub lift_reason: RestrictionLiftReasonInput,
520 #[serde(skip_serializing_if = "Option::is_none")]
521 pub restriction_id: Option<u64>,
522 #[serde(skip_serializing_if = "Option::is_none")]
523 pub recent_blockhash: Option<String>,
524}
525
526#[derive(Debug, Clone, Serialize)]
527pub struct SetFrozenAssetAmountParams {
528 pub proposer: RestrictionAddress,
529 pub governance_authority: RestrictionAddress,
530 pub account: RestrictionAddress,
531 pub asset: RestrictionAsset,
532 pub amount: u64,
533 pub reason: RestrictionReasonInput,
534 #[serde(skip_serializing_if = "Option::is_none")]
535 pub recent_blockhash: Option<String>,
536 #[serde(skip_serializing_if = "Option::is_none")]
537 pub evidence_hash: Option<String>,
538 #[serde(skip_serializing_if = "Option::is_none")]
539 pub evidence_uri_hash: Option<String>,
540 #[serde(skip_serializing_if = "Option::is_none")]
541 pub expires_at_slot: Option<u64>,
542}
543
544#[derive(Debug, Clone, Serialize)]
545pub struct ContractRestrictionParams {
546 pub proposer: RestrictionAddress,
547 pub governance_authority: RestrictionAddress,
548 pub contract: RestrictionAddress,
549 pub reason: RestrictionReasonInput,
550 #[serde(skip_serializing_if = "Option::is_none")]
551 pub recent_blockhash: Option<String>,
552 #[serde(skip_serializing_if = "Option::is_none")]
553 pub evidence_hash: Option<String>,
554 #[serde(skip_serializing_if = "Option::is_none")]
555 pub evidence_uri_hash: Option<String>,
556 #[serde(skip_serializing_if = "Option::is_none")]
557 pub expires_at_slot: Option<u64>,
558}
559
560#[derive(Debug, Clone, Serialize)]
561pub struct ResumeContractParams {
562 pub proposer: RestrictionAddress,
563 pub governance_authority: RestrictionAddress,
564 pub contract: RestrictionAddress,
565 pub lift_reason: RestrictionLiftReasonInput,
566 #[serde(skip_serializing_if = "Option::is_none")]
567 pub restriction_id: Option<u64>,
568 #[serde(skip_serializing_if = "Option::is_none")]
569 pub recent_blockhash: Option<String>,
570}
571
572#[derive(Debug, Clone, Serialize)]
573pub struct CodeHashRestrictionParams {
574 pub proposer: RestrictionAddress,
575 pub governance_authority: RestrictionAddress,
576 pub code_hash: String,
577 pub reason: RestrictionReasonInput,
578 #[serde(skip_serializing_if = "Option::is_none")]
579 pub recent_blockhash: Option<String>,
580 #[serde(skip_serializing_if = "Option::is_none")]
581 pub evidence_hash: Option<String>,
582 #[serde(skip_serializing_if = "Option::is_none")]
583 pub evidence_uri_hash: Option<String>,
584 #[serde(skip_serializing_if = "Option::is_none")]
585 pub expires_at_slot: Option<u64>,
586}
587
588#[derive(Debug, Clone, Serialize)]
589pub struct UnbanCodeHashParams {
590 pub proposer: RestrictionAddress,
591 pub governance_authority: RestrictionAddress,
592 pub code_hash: String,
593 pub lift_reason: RestrictionLiftReasonInput,
594 #[serde(skip_serializing_if = "Option::is_none")]
595 pub restriction_id: Option<u64>,
596 #[serde(skip_serializing_if = "Option::is_none")]
597 pub recent_blockhash: Option<String>,
598}
599
600#[derive(Debug, Clone, Serialize)]
601pub struct BridgeRouteRestrictionParams {
602 pub proposer: RestrictionAddress,
603 pub governance_authority: RestrictionAddress,
604 pub chain: String,
605 pub asset: String,
606 pub reason: RestrictionReasonInput,
607 #[serde(skip_serializing_if = "Option::is_none")]
608 pub recent_blockhash: Option<String>,
609 #[serde(skip_serializing_if = "Option::is_none")]
610 pub evidence_hash: Option<String>,
611 #[serde(skip_serializing_if = "Option::is_none")]
612 pub evidence_uri_hash: Option<String>,
613 #[serde(skip_serializing_if = "Option::is_none")]
614 pub expires_at_slot: Option<u64>,
615}
616
617#[derive(Debug, Clone, Serialize)]
618pub struct ResumeBridgeRouteParams {
619 pub proposer: RestrictionAddress,
620 pub governance_authority: RestrictionAddress,
621 pub chain: String,
622 pub asset: String,
623 pub lift_reason: RestrictionLiftReasonInput,
624 #[serde(skip_serializing_if = "Option::is_none")]
625 pub restriction_id: Option<u64>,
626 #[serde(skip_serializing_if = "Option::is_none")]
627 pub recent_blockhash: Option<String>,
628}
629
630#[derive(Debug, Clone, Serialize)]
631pub struct ExtendRestrictionParams {
632 pub proposer: RestrictionAddress,
633 pub governance_authority: RestrictionAddress,
634 pub restriction_id: u64,
635 #[serde(skip_serializing_if = "Option::is_none")]
636 pub new_expires_at_slot: Option<u64>,
637 #[serde(skip_serializing_if = "Option::is_none")]
638 pub evidence_hash: Option<String>,
639 #[serde(skip_serializing_if = "Option::is_none")]
640 pub recent_blockhash: Option<String>,
641}
642
643#[derive(Debug, Clone, Serialize)]
644pub struct LiftRestrictionParams {
645 pub proposer: RestrictionAddress,
646 pub governance_authority: RestrictionAddress,
647 pub restriction_id: u64,
648 pub lift_reason: RestrictionLiftReasonInput,
649 #[serde(skip_serializing_if = "Option::is_none")]
650 pub recent_blockhash: Option<String>,
651}
652
653#[derive(Debug, Clone, Serialize)]
654pub struct MovementRestrictionParams {
655 pub account: RestrictionAddress,
656 pub asset: RestrictionAsset,
657 #[serde(skip_serializing_if = "Option::is_none")]
658 pub amount: Option<u64>,
659}
660
661#[derive(Debug, Clone, Serialize)]
662pub struct TransferRestrictionParams {
663 pub from: RestrictionAddress,
664 pub to: RestrictionAddress,
665 pub asset: RestrictionAsset,
666 #[serde(skip_serializing_if = "Option::is_none")]
667 pub amount: Option<u64>,
668}
669
670#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
671pub struct RestrictionTargetDetails {
672 #[serde(rename = "type")]
673 pub kind: String,
674 #[serde(default)]
675 pub account: Option<String>,
676 #[serde(default)]
677 pub asset: Option<String>,
678 #[serde(default)]
679 pub contract: Option<String>,
680 #[serde(default)]
681 pub code_hash: Option<String>,
682 #[serde(default)]
683 pub chain: Option<String>,
684 #[serde(default)]
685 pub chain_id: Option<String>,
686 #[serde(default)]
687 pub module: Option<String>,
688 #[serde(default)]
689 pub module_id: Option<u64>,
690}
691
692#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
693pub struct RestrictionModeDetails {
694 pub kind: String,
695 #[serde(default)]
696 pub frozen_amount: Option<u64>,
697}
698
699#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
700pub struct RestrictionRecord {
701 pub id: u64,
702 pub status: String,
703 pub target_type: String,
704 pub target: String,
705 pub target_details: RestrictionTargetDetails,
706 pub mode: String,
707 pub mode_details: RestrictionModeDetails,
708 #[serde(default)]
709 pub frozen_amount: Option<u64>,
710 pub reason: String,
711 #[serde(default)]
712 pub evidence_hash: Option<String>,
713 #[serde(default)]
714 pub evidence_uri_hash: Option<String>,
715 pub proposer: String,
716 pub authority: String,
717 #[serde(default)]
718 pub approval_authority: Option<String>,
719 pub created_slot: u64,
720 pub created_epoch: u64,
721 #[serde(default)]
722 pub expires_at_slot: Option<u64>,
723 #[serde(default)]
724 pub supersedes: Option<u64>,
725 #[serde(default)]
726 pub lifted_by: Option<String>,
727 #[serde(default)]
728 pub lifted_slot: Option<u64>,
729 #[serde(default)]
730 pub lift_reason: Option<String>,
731}
732
733#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
734pub struct EffectiveRestrictionRecord {
735 #[serde(flatten)]
736 pub record: RestrictionRecord,
737 pub effective_status: String,
738 pub active: bool,
739}
740
741#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
742pub struct GetRestrictionResponse {
743 pub id: u64,
744 pub slot: u64,
745 pub found: bool,
746 pub restriction: Option<EffectiveRestrictionRecord>,
747}
748
749#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
750pub struct RestrictionListResponse {
751 pub restrictions: Vec<EffectiveRestrictionRecord>,
752 pub count: u64,
753 pub has_more: bool,
754 #[serde(default)]
755 pub next_cursor: Option<String>,
756 pub slot: u64,
757 #[serde(default)]
758 pub active_only: Option<bool>,
759}
760
761#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
762pub struct RestrictionTargetStatus {
763 pub slot: u64,
764 pub target_type: String,
765 pub target: String,
766 pub target_details: RestrictionTargetDetails,
767 pub restricted: bool,
768 pub active: bool,
769 pub restriction_ids: Vec<u64>,
770 pub active_restriction_ids: Vec<u64>,
771 pub restrictions: Vec<EffectiveRestrictionRecord>,
772 pub active_restrictions: Vec<EffectiveRestrictionRecord>,
773}
774
775#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
776pub struct ContractLifecycleRestrictionStatus {
777 pub contract: String,
778 pub slot: u64,
779 pub found: bool,
780 pub is_executable: bool,
781 pub lifecycle_status: String,
782 pub lifecycle_updated_slot: u64,
783 #[serde(default)]
784 pub lifecycle_restriction_id: Option<u64>,
785 pub derived_from_restriction: bool,
786 pub active: bool,
787 pub active_restriction_ids: Vec<u64>,
788 pub active_restrictions: Vec<EffectiveRestrictionRecord>,
789}
790
791#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
792pub struct CodeHashRestrictionStatus {
793 pub code_hash: String,
794 pub slot: u64,
795 pub blocked: bool,
796 pub deploy_blocked: bool,
797 pub active_restriction_ids: Vec<u64>,
798 pub active_restrictions: Vec<RestrictionRecord>,
799}
800
801#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
802pub struct BridgeRouteRestrictionStatus {
803 pub chain: String,
804 pub chain_id: String,
805 pub asset: String,
806 pub slot: u64,
807 pub paused: bool,
808 pub route_paused: bool,
809 pub active_restriction_ids: Vec<u64>,
810 pub active_restrictions: Vec<RestrictionRecord>,
811}
812
813#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
814pub struct MovementRestrictionStatus {
815 pub operation: String,
816 pub account: String,
817 pub asset: String,
818 pub amount: u64,
819 pub spendable: u64,
820 pub slot: u64,
821 pub allowed: bool,
822 pub blocked: bool,
823 pub active_restriction_ids: Vec<u64>,
824 pub active_restrictions: Vec<RestrictionRecord>,
825}
826
827#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
828pub struct TransferRestrictionStatus {
829 pub operation: String,
830 pub from: String,
831 pub to: String,
832 pub asset: String,
833 pub amount: u64,
834 pub source_spendable: u64,
835 pub recipient_spendable: u64,
836 pub slot: u64,
837 pub allowed: bool,
838 pub blocked: bool,
839 pub send_allowed: bool,
840 pub receive_allowed: bool,
841 pub source_blocked: bool,
842 pub recipient_blocked: bool,
843 pub source_restriction_ids: Vec<u64>,
844 pub source_restrictions: Vec<RestrictionRecord>,
845 pub recipient_restriction_ids: Vec<u64>,
846 pub recipient_restrictions: Vec<RestrictionRecord>,
847 pub active_restriction_ids: Vec<u64>,
848 pub active_restrictions: Vec<RestrictionRecord>,
849}
850
851#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
852pub struct RestrictionBuilderInstruction {
853 pub program_id: String,
854 pub accounts: Vec<String>,
855 pub instruction_type: u64,
856 #[serde(default)]
857 pub governance_action_type: Option<u64>,
858 pub data_hex: String,
859}
860
861#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
862pub struct UnsignedRestrictionGovernanceTx {
863 pub method: String,
864 pub unsigned: bool,
865 pub encoding: String,
866 pub wire_format: String,
867 pub tx_type: String,
868 pub transaction_base64: String,
869 pub transaction: String,
870 pub wire_size: u64,
871 pub message_hash: String,
872 pub signature_count: u64,
873 pub recent_blockhash: String,
874 #[serde(default)]
875 pub slot: Option<u64>,
876 pub proposer: String,
877 pub governance_authority: String,
878 pub action_label: String,
879 pub action: Value,
880 pub instruction: RestrictionBuilderInstruction,
881}
882
883#[derive(Debug, Clone)]
884pub struct RestrictionGovernanceClient {
885 client: Client,
886}
887
888impl RestrictionGovernanceClient {
889 pub fn new(client: Client) -> Self {
890 Self { client }
891 }
892
893 pub fn from_rpc_url(rpc_url: impl Into<String>) -> Self {
894 Self::new(Client::new(rpc_url))
895 }
896
897 async fn rpc<T>(&self, method: &str, params: Value) -> Result<T>
898 where
899 T: DeserializeOwned,
900 {
901 let result = self.client.rpc_call(method, params).await?;
902 serde_json::from_value(result).map_err(|err| Error::ParseError(err.to_string()))
903 }
904
905 fn one_param<T: Serialize>(params: T) -> Result<Value> {
906 Ok(json!([serde_json::to_value(params).map_err(|err| {
907 Error::SerializationError(err.to_string())
908 })?]))
909 }
910
911 pub async fn get_restriction(&self, restriction_id: u64) -> Result<GetRestrictionResponse> {
912 self.rpc("getRestriction", json!([restriction_id])).await
913 }
914
915 pub async fn list_restrictions(
916 &self,
917 params: Option<RestrictionListParams>,
918 ) -> Result<RestrictionListResponse> {
919 self.rpc(
920 "listRestrictions",
921 Self::one_param(params.unwrap_or_default())?,
922 )
923 .await
924 }
925
926 pub async fn list_active_restrictions(
927 &self,
928 params: Option<RestrictionListParams>,
929 ) -> Result<RestrictionListResponse> {
930 self.rpc(
931 "listActiveRestrictions",
932 Self::one_param(params.unwrap_or_default())?,
933 )
934 .await
935 }
936
937 pub async fn get_restriction_status(
938 &self,
939 target: RestrictionTargetInput,
940 ) -> Result<RestrictionTargetStatus> {
941 self.rpc("getRestrictionStatus", Self::one_param(target)?)
942 .await
943 }
944
945 pub async fn get_account_restriction_status(
946 &self,
947 account: impl Into<RestrictionAddress>,
948 ) -> Result<RestrictionTargetStatus> {
949 self.rpc(
950 "getAccountRestrictionStatus",
951 json!([account.into().as_rpc_string()]),
952 )
953 .await
954 }
955
956 pub async fn get_asset_restriction_status(
957 &self,
958 asset: impl Into<RestrictionAsset>,
959 ) -> Result<RestrictionTargetStatus> {
960 self.rpc(
961 "getAssetRestrictionStatus",
962 json!([asset.into().as_rpc_string()]),
963 )
964 .await
965 }
966
967 pub async fn get_account_asset_restriction_status(
968 &self,
969 account: impl Into<RestrictionAddress>,
970 asset: impl Into<RestrictionAsset>,
971 ) -> Result<RestrictionTargetStatus> {
972 self.rpc(
973 "getAccountAssetRestrictionStatus",
974 json!([account.into().as_rpc_string(), asset.into().as_rpc_string()]),
975 )
976 .await
977 }
978
979 pub async fn get_contract_lifecycle_status(
980 &self,
981 contract: impl Into<RestrictionAddress>,
982 ) -> Result<ContractLifecycleRestrictionStatus> {
983 self.rpc(
984 "getContractLifecycleStatus",
985 json!([contract.into().as_rpc_string()]),
986 )
987 .await
988 }
989
990 pub async fn get_code_hash_restriction_status(
991 &self,
992 code_hash: impl Into<String>,
993 ) -> Result<CodeHashRestrictionStatus> {
994 self.rpc("getCodeHashRestrictionStatus", json!([code_hash.into()]))
995 .await
996 }
997
998 pub async fn get_bridge_route_restriction_status(
999 &self,
1000 chain: impl Into<String>,
1001 asset: impl Into<String>,
1002 ) -> Result<BridgeRouteRestrictionStatus> {
1003 self.rpc(
1004 "getBridgeRouteRestrictionStatus",
1005 json!([chain.into(), asset.into()]),
1006 )
1007 .await
1008 }
1009
1010 pub async fn can_send(
1011 &self,
1012 params: MovementRestrictionParams,
1013 ) -> Result<MovementRestrictionStatus> {
1014 self.rpc("canSend", Self::one_param(params)?).await
1015 }
1016
1017 pub async fn can_receive(
1018 &self,
1019 params: MovementRestrictionParams,
1020 ) -> Result<MovementRestrictionStatus> {
1021 self.rpc("canReceive", Self::one_param(params)?).await
1022 }
1023
1024 pub async fn can_transfer(
1025 &self,
1026 params: TransferRestrictionParams,
1027 ) -> Result<TransferRestrictionStatus> {
1028 self.rpc("canTransfer", Self::one_param(params)?).await
1029 }
1030
1031 pub async fn build_restrict_account_tx(
1032 &self,
1033 params: RestrictAccountParams,
1034 ) -> Result<UnsignedRestrictionGovernanceTx> {
1035 self.rpc("buildRestrictAccountTx", Self::one_param(params)?)
1036 .await
1037 }
1038
1039 pub async fn build_unrestrict_account_tx(
1040 &self,
1041 params: UnrestrictAccountParams,
1042 ) -> Result<UnsignedRestrictionGovernanceTx> {
1043 self.rpc("buildUnrestrictAccountTx", Self::one_param(params)?)
1044 .await
1045 }
1046
1047 pub async fn build_restrict_account_asset_tx(
1048 &self,
1049 params: RestrictAccountAssetParams,
1050 ) -> Result<UnsignedRestrictionGovernanceTx> {
1051 self.rpc("buildRestrictAccountAssetTx", Self::one_param(params)?)
1052 .await
1053 }
1054
1055 pub async fn build_unrestrict_account_asset_tx(
1056 &self,
1057 params: UnrestrictAccountAssetParams,
1058 ) -> Result<UnsignedRestrictionGovernanceTx> {
1059 self.rpc("buildUnrestrictAccountAssetTx", Self::one_param(params)?)
1060 .await
1061 }
1062
1063 pub async fn build_set_frozen_asset_amount_tx(
1064 &self,
1065 params: SetFrozenAssetAmountParams,
1066 ) -> Result<UnsignedRestrictionGovernanceTx> {
1067 self.rpc("buildSetFrozenAssetAmountTx", Self::one_param(params)?)
1068 .await
1069 }
1070
1071 pub async fn build_suspend_contract_tx(
1072 &self,
1073 params: ContractRestrictionParams,
1074 ) -> Result<UnsignedRestrictionGovernanceTx> {
1075 self.rpc("buildSuspendContractTx", Self::one_param(params)?)
1076 .await
1077 }
1078
1079 pub async fn build_resume_contract_tx(
1080 &self,
1081 params: ResumeContractParams,
1082 ) -> Result<UnsignedRestrictionGovernanceTx> {
1083 self.rpc("buildResumeContractTx", Self::one_param(params)?)
1084 .await
1085 }
1086
1087 pub async fn build_quarantine_contract_tx(
1088 &self,
1089 params: ContractRestrictionParams,
1090 ) -> Result<UnsignedRestrictionGovernanceTx> {
1091 self.rpc("buildQuarantineContractTx", Self::one_param(params)?)
1092 .await
1093 }
1094
1095 pub async fn build_terminate_contract_tx(
1096 &self,
1097 params: ContractRestrictionParams,
1098 ) -> Result<UnsignedRestrictionGovernanceTx> {
1099 self.rpc("buildTerminateContractTx", Self::one_param(params)?)
1100 .await
1101 }
1102
1103 pub async fn build_ban_code_hash_tx(
1104 &self,
1105 params: CodeHashRestrictionParams,
1106 ) -> Result<UnsignedRestrictionGovernanceTx> {
1107 self.rpc("buildBanCodeHashTx", Self::one_param(params)?)
1108 .await
1109 }
1110
1111 pub async fn build_unban_code_hash_tx(
1112 &self,
1113 params: UnbanCodeHashParams,
1114 ) -> Result<UnsignedRestrictionGovernanceTx> {
1115 self.rpc("buildUnbanCodeHashTx", Self::one_param(params)?)
1116 .await
1117 }
1118
1119 pub async fn build_pause_bridge_route_tx(
1120 &self,
1121 params: BridgeRouteRestrictionParams,
1122 ) -> Result<UnsignedRestrictionGovernanceTx> {
1123 self.rpc("buildPauseBridgeRouteTx", Self::one_param(params)?)
1124 .await
1125 }
1126
1127 pub async fn build_resume_bridge_route_tx(
1128 &self,
1129 params: ResumeBridgeRouteParams,
1130 ) -> Result<UnsignedRestrictionGovernanceTx> {
1131 self.rpc("buildResumeBridgeRouteTx", Self::one_param(params)?)
1132 .await
1133 }
1134
1135 pub async fn build_extend_restriction_tx(
1136 &self,
1137 params: ExtendRestrictionParams,
1138 ) -> Result<UnsignedRestrictionGovernanceTx> {
1139 self.rpc("buildExtendRestrictionTx", Self::one_param(params)?)
1140 .await
1141 }
1142
1143 pub async fn build_lift_restriction_tx(
1144 &self,
1145 params: LiftRestrictionParams,
1146 ) -> Result<UnsignedRestrictionGovernanceTx> {
1147 self.rpc("buildLiftRestrictionTx", Self::one_param(params)?)
1148 .await
1149 }
1150}
1151
1152#[cfg(test)]
1153mod tests {
1154 use super::*;
1155 use serde_json::json;
1156
1157 fn key(byte: u8) -> Pubkey {
1158 Pubkey([byte; 32])
1159 }
1160
1161 #[test]
1162 fn serializes_read_and_preflight_payloads() {
1163 let account = key(3);
1164 let recipient = key(4);
1165 let asset = key(5);
1166
1167 assert_eq!(
1168 RestrictionGovernanceClient::one_param(RestrictionTargetInput::account_asset(
1169 account, "native"
1170 ))
1171 .unwrap(),
1172 json!([{
1173 "type": "account_asset",
1174 "account": account.to_base58(),
1175 "asset": "native"
1176 }])
1177 );
1178
1179 assert_eq!(
1180 RestrictionGovernanceClient::one_param(RestrictionListParams {
1181 limit: Some(10),
1182 after_id: Some(2),
1183 cursor: None,
1184 })
1185 .unwrap(),
1186 json!([{ "limit": 10, "after_id": 2 }])
1187 );
1188
1189 assert_eq!(
1190 RestrictionGovernanceClient::one_param(TransferRestrictionParams {
1191 from: account.into(),
1192 to: recipient.into(),
1193 asset: asset.into(),
1194 amount: Some(25),
1195 })
1196 .unwrap(),
1197 json!([{
1198 "from": account.to_base58(),
1199 "to": recipient.to_base58(),
1200 "asset": asset.to_base58(),
1201 "amount": 25
1202 }])
1203 );
1204 }
1205
1206 #[test]
1207 fn serializes_builder_payloads() {
1208 let proposer = key(1);
1209 let authority = key(2);
1210 let account = key(3);
1211 let asset = key(5);
1212
1213 assert_eq!(
1214 RestrictionGovernanceClient::one_param(RestrictAccountParams {
1215 proposer: proposer.into(),
1216 governance_authority: authority.into(),
1217 account: account.into(),
1218 reason: "testnet_drill".into(),
1219 mode: Some("outgoing_only".into()),
1220 recent_blockhash: Some("bb".repeat(32)),
1221 evidence_hash: Some("aa".repeat(32)),
1222 evidence_uri_hash: None,
1223 expires_at_slot: Some(123),
1224 })
1225 .unwrap(),
1226 json!([{
1227 "proposer": proposer.to_base58(),
1228 "governance_authority": authority.to_base58(),
1229 "account": account.to_base58(),
1230 "reason": "testnet_drill",
1231 "mode": "outgoing_only",
1232 "recent_blockhash": "bb".repeat(32),
1233 "evidence_hash": "aa".repeat(32),
1234 "expires_at_slot": 123
1235 }])
1236 );
1237
1238 assert_eq!(
1239 RestrictionGovernanceClient::one_param(SetFrozenAssetAmountParams {
1240 proposer: proposer.into(),
1241 governance_authority: authority.into(),
1242 account: account.into(),
1243 asset: asset.into(),
1244 amount: 500,
1245 reason: lichen_core::RestrictionReason::StolenFunds.into(),
1246 recent_blockhash: None,
1247 evidence_hash: None,
1248 evidence_uri_hash: None,
1249 expires_at_slot: None,
1250 })
1251 .unwrap(),
1252 json!([{
1253 "proposer": proposer.to_base58(),
1254 "governance_authority": authority.to_base58(),
1255 "account": account.to_base58(),
1256 "asset": asset.to_base58(),
1257 "amount": 500,
1258 "reason": "stolen_funds"
1259 }])
1260 );
1261
1262 assert_eq!(
1263 RestrictionGovernanceClient::one_param(ResumeBridgeRouteParams {
1264 proposer: proposer.into(),
1265 governance_authority: authority.into(),
1266 chain: BridgeChain::NeoX.into(),
1267 asset: BridgeAsset::Gas.into(),
1268 lift_reason: lichen_core::RestrictionLiftReason::TestnetDrillComplete.into(),
1269 restriction_id: Some(12),
1270 recent_blockhash: None,
1271 })
1272 .unwrap(),
1273 json!([{
1274 "proposer": proposer.to_base58(),
1275 "governance_authority": authority.to_base58(),
1276 "chain": "neox",
1277 "asset": "gas",
1278 "lift_reason": "testnet_drill_complete",
1279 "restriction_id": 12
1280 }])
1281 );
1282 }
1283
1284 #[test]
1285 fn deserializes_builder_response() {
1286 let response: UnsignedRestrictionGovernanceTx = serde_json::from_value(json!({
1287 "method": "buildRestrictAccountTx",
1288 "unsigned": true,
1289 "encoding": "base64",
1290 "wire_format": "lichen_tx_v1",
1291 "tx_type": "native",
1292 "transaction_base64": "AA==",
1293 "transaction": "AA==",
1294 "wire_size": 1,
1295 "message_hash": "00",
1296 "signature_count": 0,
1297 "recent_blockhash": "00",
1298 "slot": null,
1299 "proposer": "",
1300 "governance_authority": "",
1301 "action_label": "restrict",
1302 "action": {},
1303 "instruction": {
1304 "program_id": "",
1305 "accounts": [],
1306 "instruction_type": 34,
1307 "governance_action_type": 10,
1308 "data_hex": ""
1309 }
1310 }))
1311 .unwrap();
1312
1313 assert!(response.unsigned);
1314 assert_eq!(response.method, "buildRestrictAccountTx");
1315 assert_eq!(response.instruction.instruction_type, 34);
1316 }
1317}