use cosmwasm_schema::cw_serde;
use cosmwasm_std::{Binary, IbcTimeout};
use crate::{
error::Ics721Error,
token_types::{ClassId, TokenId},
};
#[cw_serde]
#[serde(rename_all = "camelCase")]
pub struct NonFungibleTokenPacketData {
pub class_id: ClassId,
pub class_uri: Option<String>,
pub class_data: Option<Binary>,
pub token_ids: Vec<TokenId>,
pub token_uris: Option<Vec<String>>,
pub token_data: Option<Vec<Binary>>,
pub sender: String,
pub receiver: String,
pub memo: Option<String>,
}
macro_rules! non_empty_optional {
($e:expr) => {
if $e.map_or(false, |data| data.is_empty()) {
return Err(Ics721Error::EmptyOptional {});
}
};
}
impl NonFungibleTokenPacketData {
pub fn validate(&self) -> Result<(), Ics721Error> {
if self.class_id.is_empty() {
return Err(Ics721Error::EmptyClassId {});
}
non_empty_optional!(self.class_uri.as_ref());
non_empty_optional!(self.class_data.as_ref());
let token_count = self.token_ids.len();
if token_count == 0 {
return Err(Ics721Error::NoTokens {});
}
if self
.token_data
.as_ref()
.map_or(false, |data| data.len() != token_count)
|| self
.token_uris
.as_ref()
.map_or(false, |data| data.len() != token_count)
{
return Err(Ics721Error::TokenInfoLenMissmatch {});
}
Ok(())
}
}
#[cw_serde]
pub struct IbcOutgoingMsg {
pub receiver: String,
pub channel_id: String,
pub timeout: IbcTimeout,
pub memo: Option<String>,
}
#[cw_serde]
pub struct IbcOutgoingProxyMsg {
pub collection: String,
pub msg: Binary,
}
#[cfg(test)]
mod tests {
use super::*;
use crate::token_types::{ClassId, TokenId};
#[test]
fn test_packet_validation() {
let default_token = NonFungibleTokenPacketData {
class_id: ClassId::new("id"),
class_uri: None,
class_data: None,
token_ids: vec![TokenId::new("1")],
token_uris: None,
token_data: None,
sender: "violet".to_string(),
receiver: "blue".to_string(),
memo: None,
};
let empty_class_id = NonFungibleTokenPacketData {
class_id: ClassId::new(""),
..default_token.clone()
};
let err = empty_class_id.validate().unwrap_err();
assert_eq!(err, Ics721Error::EmptyClassId {});
let empty_class_uri = NonFungibleTokenPacketData {
class_uri: Some("".to_string()),
..default_token.clone()
};
let err = empty_class_uri.validate().unwrap_err();
assert_eq!(err, Ics721Error::EmptyOptional {});
let empty_class_data = NonFungibleTokenPacketData {
class_data: Some(Binary::default()),
..default_token.clone()
};
let err = empty_class_data.validate().unwrap_err();
assert_eq!(err, Ics721Error::EmptyOptional {});
let no_tokens = NonFungibleTokenPacketData {
token_ids: vec![],
..default_token.clone()
};
let err = no_tokens.validate().unwrap_err();
assert_eq!(err, Ics721Error::NoTokens {});
let uri_imbalance_empty = NonFungibleTokenPacketData {
token_uris: Some(vec![]),
..default_token.clone()
};
let err = uri_imbalance_empty.validate().unwrap_err();
assert_eq!(err, Ics721Error::TokenInfoLenMissmatch {});
let uri_imbalance = NonFungibleTokenPacketData {
token_uris: Some(vec!["a".to_string(), "b".to_string()]),
..default_token.clone()
};
let err = uri_imbalance.validate().unwrap_err();
assert_eq!(err, Ics721Error::TokenInfoLenMissmatch {});
let data_imbalance_empty = NonFungibleTokenPacketData {
token_data: Some(vec![]),
..default_token.clone()
};
let err = data_imbalance_empty.validate().unwrap_err();
assert_eq!(err, Ics721Error::TokenInfoLenMissmatch {});
let data_imbalance = NonFungibleTokenPacketData {
token_data: Some(vec![Binary::default(), Binary::default()]),
..default_token
};
let err = data_imbalance.validate().unwrap_err();
assert_eq!(err, Ics721Error::TokenInfoLenMissmatch {});
}
}