hermit_toolkit_permit/
structs.rs

1#![allow(clippy::field_reassign_with_default)] // This is triggered in `#[derive(JsonSchema)]`
2
3use schemars::JsonSchema;
4use serde::{Deserialize, Serialize};
5
6use crate::pubkey_to_account;
7use cosmwasm_std::{Binary, CanonicalAddr, HumanAddr, Uint128};
8
9#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
10#[serde(rename_all = "snake_case")]
11pub struct Permit<Permission: Permissions = TokenPermissions> {
12    #[serde(bound = "")]
13    pub params: PermitParams<Permission>,
14    pub signature: PermitSignature,
15}
16
17impl<Permission: Permissions> Permit<Permission> {
18    pub fn check_token(&self, token: &HumanAddr) -> bool {
19        self.params.allowed_tokens.contains(token)
20    }
21
22    pub fn check_permission(&self, permission: &Permission) -> bool {
23        self.params.permissions.contains(permission)
24    }
25}
26
27#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
28#[serde(rename_all = "snake_case")]
29pub struct PermitParams<Permission: Permissions = TokenPermissions> {
30    pub allowed_tokens: Vec<HumanAddr>,
31    pub permit_name: String,
32    pub chain_id: String,
33    #[serde(bound = "")]
34    pub permissions: Vec<Permission>,
35}
36
37#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
38#[serde(rename_all = "snake_case")]
39pub struct PermitSignature {
40    pub pub_key: PubKey,
41    pub signature: Binary,
42}
43
44#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
45#[serde(rename_all = "snake_case")]
46pub struct PubKey {
47    /// ignored, but must be "tendermint/PubKeySecp256k1" otherwise the verification will fail
48    pub r#type: String,
49    /// Secp256k1 PubKey
50    pub value: Binary,
51}
52
53impl PubKey {
54    pub fn canonical_address(&self) -> CanonicalAddr {
55        pubkey_to_account(&self.value)
56    }
57}
58
59// Note: The order of fields in this struct is important for the permit signature verification!
60#[remain::sorted]
61#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
62#[serde(rename_all = "snake_case")]
63pub struct SignedPermit<Permission: Permissions = TokenPermissions> {
64    /// ignored
65    pub account_number: Uint128,
66    /// ignored, no Env in query
67    pub chain_id: String,
68    /// ignored
69    pub fee: Fee,
70    /// ignored
71    pub memo: String,
72    /// the signed message
73    #[serde(bound = "")]
74    pub msgs: Vec<PermitMsg<Permission>>,
75    /// ignored
76    pub sequence: Uint128,
77}
78
79impl<Permission: Permissions> SignedPermit<Permission> {
80    pub fn from_params(params: &PermitParams<Permission>) -> Self {
81        Self {
82            account_number: Uint128::zero(),
83            chain_id: params.chain_id.clone(),
84            fee: Fee::new(),
85            memo: String::new(),
86            msgs: vec![PermitMsg::from_content(PermitContent::from_params(params))],
87            sequence: Uint128::zero(),
88        }
89    }
90}
91
92// Note: The order of fields in this struct is important for the permit signature verification!
93#[remain::sorted]
94#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
95#[serde(rename_all = "snake_case")]
96pub struct Fee {
97    pub amount: Vec<Coin>,
98    pub gas: Uint128,
99}
100
101impl Fee {
102    pub fn new() -> Self {
103        Self {
104            amount: vec![Coin::new()],
105            gas: Uint128(1),
106        }
107    }
108}
109
110impl Default for Fee {
111    fn default() -> Self {
112        Self::new()
113    }
114}
115
116// Note: The order of fields in this struct is important for the permit signature verification!
117#[remain::sorted]
118#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
119#[serde(rename_all = "snake_case")]
120pub struct Coin {
121    pub amount: Uint128,
122    pub denom: String,
123}
124
125impl Coin {
126    pub fn new() -> Self {
127        Self {
128            amount: Uint128::zero(),
129            denom: "uscrt".to_string(),
130        }
131    }
132}
133
134impl Default for Coin {
135    fn default() -> Self {
136        Self::new()
137    }
138}
139
140// Note: The order of fields in this struct is important for the permit signature verification!
141#[remain::sorted]
142#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
143#[serde(rename_all = "snake_case")]
144pub struct PermitMsg<Permission: Permissions = TokenPermissions> {
145    pub r#type: String,
146    #[serde(bound = "")]
147    pub value: PermitContent<Permission>,
148}
149
150impl<Permission: Permissions> PermitMsg<Permission> {
151    pub fn from_content(content: PermitContent<Permission>) -> Self {
152        Self {
153            r#type: "query_permit".to_string(),
154            value: content,
155        }
156    }
157}
158
159// Note: The order of fields in this struct is important for the permit signature verification!
160#[remain::sorted]
161#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
162#[serde(rename_all = "snake_case")]
163pub struct PermitContent<Permission: Permissions = TokenPermissions> {
164    pub allowed_tokens: Vec<HumanAddr>,
165    #[serde(bound = "")]
166    pub permissions: Vec<Permission>,
167    pub permit_name: String,
168}
169
170impl<Permission: Permissions> PermitContent<Permission> {
171    pub fn from_params(params: &PermitParams<Permission>) -> Self {
172        Self {
173            allowed_tokens: params.allowed_tokens.clone(),
174            permit_name: params.permit_name.clone(),
175            permissions: params.permissions.clone(),
176        }
177    }
178}
179
180/// This trait is an alias for all the other traits it inherits from.
181/// It does this by providing a blanket implementation for all types that
182/// implement the same set of traits
183pub trait Permissions:
184Clone + PartialEq + Serialize + for<'d> Deserialize<'d> + JsonSchema
185{
186}
187
188impl<T> Permissions for T where
189    T: Clone + PartialEq + Serialize + for<'d> Deserialize<'d> + JsonSchema
190{
191}
192
193#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
194#[serde(rename_all = "snake_case")]
195pub enum TokenPermissions {
196    /// Allowance for SNIP-20 - Permission to query allowance of the owner & spender
197    Allowance,
198    /// Balance for SNIP-20 - Permission to query balance
199    Balance,
200    /// History for SNIP-20 - Permission to query transfer_history & transaction_hisotry
201    History,
202    /// Owner permission indicates that the bearer of this permit should be granted all
203    /// the access of the creator/signer of the permit.  SNIP-721 uses this to grant
204    /// viewing access to all data that the permit creator owns and is whitelisted for.
205    /// For SNIP-721 use, a permit with Owner permission should NEVER be given to
206    /// anyone else.  If someone wants to share private data, they should whitelist
207    /// the address they want to share with via a SetWhitelistedApproval tx, and that
208    /// address will view the data by creating their own permit with Owner permission
209    Owner,
210}