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
//! Types related to the pre-propose module. Motivation:
//! <https://github.com/DA0-DA0/dao-contracts/discussions/462>.
use cosmwasm_schema::cw_serde;
use cosmwasm_std::{Addr, Empty, StdResult, SubMsg};
use dao_interface::state::ModuleInstantiateInfo;
use crate::reply::pre_propose_module_instantiation_id;
#[cw_serde]
pub enum PreProposeInfo {
/// Anyone may create a proposal free of charge.
AnyoneMayPropose {},
/// The module specified in INFO has exclusive rights to proposal
/// creation.
ModuleMayPropose { info: ModuleInstantiateInfo },
}
#[cw_serde]
pub enum ProposalCreationPolicy {
/// Anyone may create a proposal, free of charge.
Anyone {},
/// Only ADDR may create proposals. It is expected that ADDR is a
/// pre-propose module, though we only require that it is a valid
/// address.
Module { addr: Addr },
}
impl ProposalCreationPolicy {
/// Determines if CREATOR is permitted to create a
/// proposal. Returns true if so and false otherwise.
pub fn is_permitted(&self, creator: &Addr) -> bool {
match self {
Self::Anyone {} => true,
Self::Module { addr } => creator == addr,
}
}
}
impl PreProposeInfo {
pub fn into_initial_policy_and_messages(
self,
dao: Addr,
) -> StdResult<(ProposalCreationPolicy, Vec<SubMsg<Empty>>)> {
Ok(match self {
Self::AnyoneMayPropose {} => (ProposalCreationPolicy::Anyone {}, vec![]),
Self::ModuleMayPropose { info } => (
// Anyone can propose will be set until instantiation succeeds, then
// `ModuleMayPropose` will be set. This ensures that we fail open
// upon instantiation failure.
ProposalCreationPolicy::Anyone {},
vec![SubMsg::reply_on_success(
info.into_wasm_msg(dao),
pre_propose_module_instantiation_id(),
)],
),
})
}
}
#[cfg(test)]
mod tests {
use cosmwasm_std::{to_binary, WasmMsg};
use super::*;
#[test]
fn test_anyone_is_permitted() {
let policy = ProposalCreationPolicy::Anyone {};
// I'll actually stand by this as a legit testing strategy
// when looking at string inputs. If anything is going to
// screw things up, its weird unicode characters.
//
// For example, my langauge server explodes for me if I use
// the granddaddy of weird unicode characters, the large
// family: 👩👩👧👦.
//
// The family emoji you see is actually a combination of
// individual person emojis. You can browse the whole
// collection of combo emojis here:
// <https://unicode.org/emoji/charts/emoji-zwj-sequences.html>.
//
// You may also enjoy this PDF wherein there is a discussion
// about the feesability of supporting all 7230 possible
// combos of family emojis:
// <https://www.unicode.org/L2/L2020/20114-family-emoji-explor.pdf>.
for c in '😀'..'🤣' {
assert!(policy.is_permitted(&Addr::unchecked(c.to_string())))
}
}
#[test]
fn test_module_is_permitted() {
let policy = ProposalCreationPolicy::Module {
addr: Addr::unchecked("deposit_module"),
};
assert!(!policy.is_permitted(&Addr::unchecked("👩👩👧👦")));
assert!(policy.is_permitted(&Addr::unchecked("deposit_module")));
}
#[test]
fn test_pre_any_conversion() {
let info = PreProposeInfo::AnyoneMayPropose {};
let (policy, messages) = info
.into_initial_policy_and_messages(Addr::unchecked("😃"))
.unwrap();
assert_eq!(policy, ProposalCreationPolicy::Anyone {});
assert!(messages.is_empty())
}
#[test]
fn test_pre_module_conversion() {
let info = PreProposeInfo::ModuleMayPropose {
info: ModuleInstantiateInfo {
code_id: 42,
msg: to_binary("foo").unwrap(),
admin: None,
label: "pre-propose-9000".to_string(),
},
};
let (policy, messages) = info
.into_initial_policy_and_messages(Addr::unchecked("🥵"))
.unwrap();
// In this case the package is expected to allow anyone to
// create a proposal (fail-open), and provide some messages
// that, when handled in a `reply` handler will set the
// creation policy to a specific module.
assert_eq!(policy, ProposalCreationPolicy::Anyone {});
assert_eq!(messages.len(), 1);
assert_eq!(
messages[0],
SubMsg::reply_on_success(
WasmMsg::Instantiate {
admin: None,
code_id: 42,
msg: to_binary("foo").unwrap(),
funds: vec![],
label: "pre-propose-9000".to_string()
},
crate::reply::pre_propose_module_instantiation_id()
)
)
}
}