1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
use schemars::JsonSchema;

use std::fmt;

use cosmwasm_schema::{cw_serde, QueryResponses};
use cosmwasm_std::{Coin, CosmosMsg, Empty};
use cw_utils::{Expiration, NativeBalance};

use crate::state::Permissions;

#[cw_serde]
#[derive(cw_orch::ExecuteFns)]
pub enum ExecuteMsg<T = Empty>
where
    T: Clone + fmt::Debug + PartialEq + JsonSchema,
{
    /// Execute requests the contract to re-dispatch all these messages with the
    /// contract's address as sender. Every implementation has it's own logic to
    /// determine in
    Execute { msgs: Vec<CosmosMsg<T>> },
    /// Freeze will make a mutable contract immutable, must be called by an admin
    Freeze {},
    /// UpdateAdmins will change the admin set of the contract, must be called by an existing admin,
    /// and only works if the contract is mutable
    UpdateAdmins { admins: Vec<String> },

    /// Add an allowance to a given subkey (subkey must not be admin)
    IncreaseAllowance {
        spender: String,
        amount: Coin,
        expires: Option<Expiration>,
    },
    /// Decreases an allowance for a given subkey (subkey must not be admin)
    DecreaseAllowance {
        spender: String,
        amount: Coin,
        expires: Option<Expiration>,
    },

    // Setups up permissions for a given subkey.
    SetPermissions {
        spender: String,
        permissions: Permissions,
    },
}

#[cw_serde]
#[derive(QueryResponses, cw_orch::QueryFns)]
pub enum QueryMsg<T = Empty>
where
    T: Clone + fmt::Debug + PartialEq + JsonSchema,
{
    /// Shows all admins and whether or not it is mutable
    #[returns(abstract_cw1_whitelist::msg::AdminListResponse)]
    AdminList {},
    /// Get the current allowance for the given subkey (how much it can spend)
    #[returns(crate::state::Allowance)]
    Allowance { spender: String },
    /// Get the current permissions for the given subkey (how much it can spend)
    #[returns(PermissionsInfo)]
    Permissions { spender: String },
    /// Checks permissions of the caller on this proxy.
    /// If CanExecute returns true then a call to `Execute` with the same message,
    /// before any further state changes, should also succeed.
    #[returns(abstract_cw1::CanExecuteResponse)]
    CanExecute { sender: String, msg: CosmosMsg<T> },
    /// Gets all Allowances for this contract
    #[returns(AllAllowancesResponse)]
    AllAllowances {
        start_after: Option<String>,
        limit: Option<u32>,
    },
    /// Gets all Permissions for this contract
    #[returns(AllPermissionsResponse)]
    AllPermissions {
        start_after: Option<String>,
        limit: Option<u32>,
    },
}

#[cw_serde]
pub struct AllAllowancesResponse {
    pub allowances: Vec<AllowanceInfo>,
}

#[cfg(test)]
impl AllAllowancesResponse {
    pub fn canonical(mut self) -> Self {
        self.allowances = self
            .allowances
            .into_iter()
            .map(AllowanceInfo::canonical)
            .collect();
        self.allowances.sort_by(AllowanceInfo::cmp_by_spender);
        self
    }
}

#[cw_serde]
pub struct AllowanceInfo {
    pub spender: String,
    pub balance: NativeBalance,
    pub expires: Expiration,
}

#[cfg(test)]
impl AllowanceInfo {
    /// Utility function providing some ordering to be used with `slice::sort_by`.
    ///
    /// Note, that this doesn't implement full ordering - items with same spender but differing on
    /// permissions, would be considered equal, however as spender is a unique key in any valid
    /// state this is enough for testing purposes.
    ///
    /// Example:
    ///
    /// ```
    /// # use cw_utils::{Expiration, NativeBalance};
    /// # use abstract_cw1_subkeys::msg::AllowanceInfo;
    /// # use cosmwasm_schema::{cw_serde, QueryResponses};use cosmwasm_std::coin;
    ///
    /// let mut allows = vec![AllowanceInfo {
    ///   spender: "spender2".to_owned(),
    ///   balance: NativeBalance(vec![coin(1, "token1")]),
    ///   expires: Expiration::Never {},
    /// }, AllowanceInfo {
    ///   spender: "spender1".to_owned(),
    ///   balance: NativeBalance(vec![coin(2, "token2")]),
    ///   expires: Expiration::Never {},
    /// }];
    ///
    /// allows.sort_by(AllowanceInfo::cmp_by_spender);
    ///
    /// assert_eq!(
    ///   allows.into_iter().map(|allow| allow.spender).collect::<Vec<_>>(),
    ///   vec!["spender1".to_owned(), "spender2".to_owned()]
    /// );
    /// ```
    pub fn cmp_by_spender(left: &Self, right: &Self) -> std::cmp::Ordering {
        left.spender.cmp(&right.spender)
    }

    pub fn canonical(mut self) -> Self {
        self.balance.normalize();
        self
    }
}

#[cw_serde]
pub struct PermissionsInfo {
    pub spender: String,
    pub permissions: Permissions,
}

#[cfg(any(test, feature = "test-utils"))]
impl PermissionsInfo {
    /// Utility function providing some ordering to be used with `slice::sort_by`.
    ///
    /// Note, that this doesn't implement full ordering - items with same spender but differing on
    /// permissions, would be considered equal, however as spender is a unique key in any valid
    /// state this is enough for testing purposes.
    ///
    /// Example:
    ///
    /// ```
    /// # use abstract_cw1_subkeys::msg::PermissionsInfo;
    /// # use abstract_cw1_subkeys::state::Permissions;
    ///
    /// let mut perms = vec![PermissionsInfo {
    ///   spender: "spender2".to_owned(),
    ///   permissions: Permissions::default(),
    /// }, PermissionsInfo {
    ///   spender: "spender1".to_owned(),
    ///   permissions: Permissions::default(),
    /// }];
    ///
    /// perms.sort_by(PermissionsInfo::cmp_by_spender);
    ///
    /// assert_eq!(
    ///   perms.into_iter().map(|perm| perm.spender).collect::<Vec<_>>(),
    ///   vec!["spender1".to_owned(), "spender2".to_owned()]
    /// );
    /// ```
    pub fn cmp_by_spender(left: &Self, right: &Self) -> std::cmp::Ordering {
        left.spender.cmp(&right.spender)
    }
}

#[cw_serde]
pub struct AllPermissionsResponse {
    pub permissions: Vec<PermissionsInfo>,
}