Skip to content

Commit

Permalink
Merge pull request #22 from FigureTechnologies/tsilva/sc-256087/multi…
Browse files Browse the repository at this point in the history
…-like-capital-denom

Tsilva/sc 256087/multi like capital denom
  • Loading branch information
tsilva-figure authored Sep 18, 2023
2 parents b86ab4f + eb0807d commit f3da8e1
Show file tree
Hide file tree
Showing 9 changed files with 124 additions and 61 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "marketpalace-subscription-contract"
version = "2.2.0"
version = "2.3.1"
authors = ["Thomas Silva <[email protected]>"]
edition = "2018"

Expand Down
28 changes: 22 additions & 6 deletions schema/instantiate_msg.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
"commitment_denom",
"investment_denom",
"like_capital_denoms",
"lp"
"lp",
"required_capital_attributes"
],
"properties": {
"admin": {
Expand Down Expand Up @@ -42,17 +43,32 @@
"lp": {
"$ref": "#/definitions/Addr"
},
"required_capital_attribute": {
"type": [
"string",
"null"
]
"required_capital_attributes": {
"type": "array",
"items": {
"$ref": "#/definitions/CapitalDenomRequirement"
}
}
},
"definitions": {
"Addr": {
"description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.",
"type": "string"
},
"CapitalDenomRequirement": {
"type": "object",
"required": [
"capital_denom",
"required_attribute"
],
"properties": {
"capital_denom": {
"type": "string"
},
"required_attribute": {
"type": "string"
}
}
}
}
}
28 changes: 22 additions & 6 deletions schema/state.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
"investment_denom",
"like_capital_denoms",
"lp",
"raise"
"raise",
"required_capital_attributes"
],
"properties": {
"admin": {
Expand Down Expand Up @@ -38,17 +39,32 @@
"raise": {
"$ref": "#/definitions/Addr"
},
"required_capital_attribute": {
"type": [
"string",
"null"
]
"required_capital_attributes": {
"type": "array",
"items": {
"$ref": "#/definitions/CapitalDenomRequirement"
}
}
},
"definitions": {
"Addr": {
"description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.",
"type": "string"
},
"CapitalDenomRequirement": {
"type": "object",
"required": [
"capital_denom",
"required_attribute"
],
"properties": {
"capital_denom": {
"type": "string"
},
"required_attribute": {
"type": "string"
}
}
}
}
}
20 changes: 14 additions & 6 deletions src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,12 +160,16 @@ pub fn execute(
response,
|response, (capital_denom, capital_sum)| -> Result<_, StdError> {
Ok(if capital_sum < 0 {
match &state.required_capital_attribute {
match &state
.required_capital_attributes
.iter()
.find(|requirement| requirement.capital_denom == capital_denom)
{
None => {
funds.push(coin(capital_sum.unsigned_abs().into(), capital_denom));
response
}
Some(_required_capital_attribute) => {
Some(_requirement) => {
let marker_transfer = transfer_marker_coins(
capital_sum.unsigned_abs().into(),
&capital_denom,
Expand Down Expand Up @@ -216,22 +220,26 @@ pub fn execute(
return contract_error("no capital denom");
};

let response = match state.required_capital_attribute {
let response = match state
.required_capital_attributes
.iter()
.find(|requirement| requirement.capital_denom == capital_denom)
{
None => {
let send_capital = BankMsg::Send {
to_address: to.to_string(),
amount: coins(amount.into(), capital_denom),
};
Response::new().add_message(send_capital)
}
Some(required_capital_attribute) => {
Some(requirement) => {
if !query_attributes(deps, &to)
.any(|attr| attr.name == required_capital_attribute)
.any(|attr| attr.name == requirement.required_attribute)
{
return contract_error(
format!(
"{} does not have required attribute of {}",
&to, &required_capital_attribute
&to, &requirement.required_attribute
)
.as_str(),
);
Expand Down
4 changes: 2 additions & 2 deletions src/instantiate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ pub fn instantiate(
investment_denom: msg.investment_denom,
like_capital_denoms: msg.like_capital_denoms,
capital_per_share: msg.capital_per_share,
required_capital_attribute: msg.required_capital_attribute,
required_capital_attributes: msg.required_capital_attributes,
};

state_storage(deps.storage).save(&state)?;
Expand Down Expand Up @@ -87,7 +87,7 @@ mod tests {
like_capital_denoms: vec![String::from("stable_coin")],
capital_per_share: 100,
initial_commitment: Some(100),
required_capital_attribute: None,
required_capital_attributes: vec![],
},
)
.unwrap();
Expand Down
78 changes: 46 additions & 32 deletions src/migrate.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::hash::Hash;

use crate::error::contract_error;
use crate::error::ContractError;
use crate::msg::MigrateMsg;
use crate::state::state_storage;
Expand All @@ -13,6 +14,7 @@ use cosmwasm_std::DepsMut;
use cosmwasm_std::Env;
use cosmwasm_std::Response;
use cosmwasm_storage::singleton_read;
use cw2::get_contract_version;
use cw2::set_contract_version;
use provwasm_std::ProvenanceMsg;
use provwasm_std::ProvenanceQuery;
Expand All @@ -25,35 +27,37 @@ pub fn migrate(
_: Env,
migrate_msg: MigrateMsg,
) -> Result<Response<ProvenanceMsg>, ContractError> {
set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;
let contract_info = get_contract_version(deps.storage)?;

let old_state: StateV2_0_0 = singleton_read(deps.storage, CONFIG_KEY).load()?;
match contract_info.version.as_str() {
"2.3.0" => {}
"2.2.0" => {
let old_state: StateV2_2_0 = singleton_read(deps.storage, CONFIG_KEY).load()?;

let new_state = State {
admin: old_state.admin,
lp: old_state.lp,
raise: old_state.raise.clone(),
commitment_denom: old_state.commitment_denom,
investment_denom: old_state.investment_denom,
like_capital_denoms: migrate_msg.like_capital_denoms,
capital_per_share: old_state.capital_per_share,
required_capital_attribute: migrate_msg.required_capital_attribute,
};
let new_state = State {
admin: old_state.admin,
lp: old_state.lp,
raise: old_state.raise.clone(),
commitment_denom: old_state.commitment_denom,
investment_denom: old_state.investment_denom,
like_capital_denoms: migrate_msg.like_capital_denoms,
capital_per_share: old_state.capital_per_share,
required_capital_attributes: migrate_msg.required_capital_attributes,
};

state_storage(deps.storage).save(&new_state)?;
state_storage(deps.storage).save(&new_state)?;
}
_ => {
return contract_error(&format!(
"existing contract version not supported for migration to {}",
contract_info.version
));
}
}

Ok(Response::default())
}
set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct StateV2_0_0 {
pub admin: Addr,
pub lp: Addr,
pub raise: Addr,
pub commitment_denom: String,
pub investment_denom: String,
pub capital_denom: String,
pub capital_per_share: u64,
Ok(Response::default())
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
Expand Down Expand Up @@ -163,25 +167,27 @@ impl Hash for Withdrawal {

#[cfg(test)]
mod tests {
use crate::msg::CapitalDenomRequirement;

use super::*;
use cosmwasm_std::testing::mock_env;
use cosmwasm_storage::singleton;
use provwasm_mocks::mock_dependencies;

use super::StateV2_0_0;

#[test]
fn migration() {
let mut deps = mock_dependencies(&[]);
set_contract_version(&mut deps.storage, "TEST", "2.2.0").unwrap();
singleton(&mut deps.storage, CONFIG_KEY)
.save(&StateV2_0_0 {
.save(&StateV2_2_0 {
admin: Addr::unchecked("marketpalace"),
lp: Addr::unchecked("lp"),
raise: Addr::unchecked("raise_1"),
commitment_denom: "commitment".to_string(),
investment_denom: "investment".to_string(),
capital_denom: String::from("stable_coin"),
capital_per_share: 100,
required_capital_attribute: None,
})
.unwrap();

Expand All @@ -190,7 +196,7 @@ mod tests {
mock_env(),
MigrateMsg {
like_capital_denoms: vec![String::from("stable_coin")],
required_capital_attribute: None,
required_capital_attributes: vec![],
},
)
.unwrap();
Expand All @@ -204,7 +210,7 @@ mod tests {
investment_denom: String::from("investment"),
like_capital_denoms: vec![String::from("stable_coin")],
capital_per_share: 100,
required_capital_attribute: None,
required_capital_attributes: vec![],
},
singleton_read(&deps.storage, CONFIG_KEY).load().unwrap()
);
Expand All @@ -213,21 +219,26 @@ mod tests {
#[test]
fn migration_with_capital_denom_and_attribute() {
let mut deps = mock_dependencies(&[]);
set_contract_version(&mut deps.storage, "TEST", "2.2.0").unwrap();
singleton(&mut deps.storage, CONFIG_KEY)
.save(&StateV2_0_0 {
.save(&StateV2_2_0 {
admin: Addr::unchecked("marketpalace"),
lp: Addr::unchecked("lp"),
raise: Addr::unchecked("raise_1"),
commitment_denom: "commitment".to_string(),
investment_denom: "investment".to_string(),
capital_denom: String::from("stable_coin"),
capital_per_share: 100,
required_capital_attribute: Some(String::from("attr")),
})
.unwrap();

let migration_msg = MigrateMsg {
like_capital_denoms: vec![String::from("new_denom")],
required_capital_attribute: Some(String::from("attr")),
required_capital_attributes: vec![CapitalDenomRequirement {
capital_denom: String::from("new_denom"),
required_attribute: String::from("attr"),
}],
};
migrate(deps.as_mut(), mock_env(), migration_msg).unwrap();

Expand All @@ -240,7 +251,10 @@ mod tests {
investment_denom: String::from("investment"),
like_capital_denoms: vec![String::from("new_denom")],
capital_per_share: 100,
required_capital_attribute: Some(String::from("attr")),
required_capital_attributes: vec![CapitalDenomRequirement {
capital_denom: String::from("new_denom"),
required_attribute: String::from("attr"),
}],
},
singleton_read(&deps.storage, CONFIG_KEY).load().unwrap()
);
Expand Down
10 changes: 8 additions & 2 deletions src/msg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@ pub struct InstantiateMsg {
pub like_capital_denoms: Vec<String>,
pub capital_per_share: u64,
pub initial_commitment: Option<u64>,
pub required_capital_attribute: Option<String>,
pub required_capital_attributes: Vec<CapitalDenomRequirement>,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub struct MigrateMsg {
pub like_capital_denoms: Vec<String>,
pub required_capital_attribute: Option<String>,
pub required_capital_attributes: Vec<CapitalDenomRequirement>,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
Expand Down Expand Up @@ -50,6 +50,12 @@ pub enum HandleMsg {
},
}

#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
pub struct CapitalDenomRequirement {
pub capital_denom: String,
pub required_attribute: String,
}

#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
pub struct AssetExchange {
#[serde(rename = "inv")]
Expand Down
Loading

0 comments on commit f3da8e1

Please sign in to comment.