feat: Add support for flow from Celestia (#4668)
### Description Add support for flow from Celestia ### Related issues - Fixes https://github.com/hyperlane-xyz/hyperlane-monorepo/issues/4646 ### Backward compatibility Yes ### Testing Local run of Scraper Check that transaction and messages can be parsed --------- Co-authored-by: Danil Nemirovsky <4614623+ameten@users.noreply.github.com>pull/4676/head
parent
a4d5d692f3
commit
470e53bb76
@ -0,0 +1,163 @@ |
||||
use cosmrs::proto::ibc::core::channel::v1::MsgRecvPacket; |
||||
use cosmrs::proto::prost::Message; |
||||
use cosmrs::Any; |
||||
use serde::{Deserialize, Serialize}; |
||||
|
||||
use crate::HyperlaneCosmosError; |
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Default)] |
||||
pub struct PacketData { |
||||
pub amount: String, |
||||
pub denom: String, |
||||
pub memo: String, |
||||
pub receiver: String, |
||||
pub sender: String, |
||||
} |
||||
|
||||
impl TryFrom<&Any> for PacketData { |
||||
type Error = HyperlaneCosmosError; |
||||
|
||||
fn try_from(any: &Any) -> Result<Self, Self::Error> { |
||||
let vec = any.value.as_slice(); |
||||
let msg = MsgRecvPacket::decode(vec).map_err(Into::<HyperlaneCosmosError>::into)?; |
||||
let packet = msg |
||||
.packet |
||||
.ok_or(HyperlaneCosmosError::UnparsableEmptyField( |
||||
"MsgRecvPacket packet is empty".to_owned(), |
||||
))?; |
||||
let data = serde_json::from_slice::<PacketData>(&packet.data)?; |
||||
Ok(data) |
||||
} |
||||
} |
||||
|
||||
impl TryFrom<Any> for PacketData { |
||||
type Error = HyperlaneCosmosError; |
||||
|
||||
fn try_from(any: Any) -> Result<Self, Self::Error> { |
||||
Self::try_from(&any) |
||||
} |
||||
} |
||||
|
||||
#[cfg(test)] |
||||
mod tests { |
||||
use cosmrs::proto::ibc::core::channel::v1::MsgRecvPacket; |
||||
use cosmrs::proto::ibc::core::channel::v1::Packet; |
||||
use cosmrs::proto::prost::Message; |
||||
use cosmrs::Any; |
||||
|
||||
use crate::providers::cosmos::provider::parse::PacketData; |
||||
use crate::HyperlaneCosmosError; |
||||
|
||||
#[test] |
||||
fn success() { |
||||
// given
|
||||
let json = r#"{"amount":"59743800","denom":"utia","memo":"{\"wasm\":{\"contract\":\"neutron1jyyjd3x0jhgswgm6nnctxvzla8ypx50tew3ayxxwkrjfxhvje6kqzvzudq\",\"msg\":{\"transfer_remote\":{\"dest_domain\":42161,\"recipient\":\"0000000000000000000000008784aca75a95696fec93184b1c7b2d3bf5838df9\",\"amount\":\"59473800\"}},\"funds\":[{\"amount\":\"59743800\",\"denom\":\"ibc/773B4D0A3CD667B2275D5A4A7A2F0909C0BA0F4059C0B9181E680DDF4965DCC7\"}]}}","receiver":"neutron1jyyjd3x0jhgswgm6nnctxvzla8ypx50tew3ayxxwkrjfxhvje6kqzvzudq","sender":"celestia19ns7dd07g5vvrueyqlkvn4dmxt957zcdzemvj6"}"#; |
||||
let any = any(json); |
||||
|
||||
// when
|
||||
let data = PacketData::try_from(&any); |
||||
|
||||
// then
|
||||
assert!(data.is_ok()); |
||||
} |
||||
|
||||
#[test] |
||||
fn fail_json() { |
||||
// given
|
||||
let json = r#"{"amount":"27000000","denom":"utia","receiver":"neutron13uuq6vgenxan43ngscjlew8lc2z32znx9qfk0n","sender":"celestia1rh4gplea4gzvaaejew8jfvp9r0qkdmfgkf55qy"}"#; |
||||
let any = any(json); |
||||
|
||||
// when
|
||||
let data = PacketData::try_from(&any); |
||||
|
||||
// then
|
||||
assert!(data.is_err()); |
||||
assert!(matches!( |
||||
data.err().unwrap(), |
||||
HyperlaneCosmosError::SerdeError(_), |
||||
)); |
||||
} |
||||
|
||||
#[test] |
||||
fn fail_empty() { |
||||
// given
|
||||
let any = empty(); |
||||
|
||||
// when
|
||||
let data = PacketData::try_from(&any); |
||||
|
||||
// then
|
||||
assert!(data.is_err()); |
||||
assert!(matches!( |
||||
data.err().unwrap(), |
||||
HyperlaneCosmosError::UnparsableEmptyField(_), |
||||
)); |
||||
} |
||||
|
||||
#[test] |
||||
fn fail_decode() { |
||||
// given
|
||||
let any = wrong_encoding(); |
||||
|
||||
// when
|
||||
let data = PacketData::try_from(&any); |
||||
|
||||
// then
|
||||
assert!(data.is_err()); |
||||
assert!(matches!( |
||||
data.err().unwrap(), |
||||
HyperlaneCosmosError::Prost(_), |
||||
)); |
||||
} |
||||
|
||||
fn any(json: &str) -> Any { |
||||
let packet = Packet { |
||||
sequence: 0, |
||||
source_port: "".to_string(), |
||||
source_channel: "".to_string(), |
||||
destination_port: "".to_string(), |
||||
destination_channel: "".to_string(), |
||||
data: json.as_bytes().to_vec(), |
||||
timeout_height: None, |
||||
timeout_timestamp: 0, |
||||
}; |
||||
|
||||
let msg = MsgRecvPacket { |
||||
packet: Option::from(packet), |
||||
proof_commitment: vec![], |
||||
proof_height: None, |
||||
signer: "".to_string(), |
||||
}; |
||||
|
||||
encode_proto(&msg) |
||||
} |
||||
|
||||
fn empty() -> Any { |
||||
let msg = MsgRecvPacket { |
||||
packet: None, |
||||
proof_commitment: vec![], |
||||
proof_height: None, |
||||
signer: "".to_string(), |
||||
}; |
||||
|
||||
encode_proto(&msg) |
||||
} |
||||
|
||||
fn wrong_encoding() -> Any { |
||||
let buf = vec![1, 2, 3]; |
||||
Any { |
||||
type_url: "".to_string(), |
||||
value: buf, |
||||
} |
||||
} |
||||
|
||||
fn encode_proto(msg: &MsgRecvPacket) -> Any { |
||||
let mut buf = Vec::with_capacity(msg.encoded_len()); |
||||
MsgRecvPacket::encode(&msg, &mut buf).unwrap(); |
||||
|
||||
Any { |
||||
type_url: "".to_string(), |
||||
value: buf, |
||||
} |
||||
} |
||||
} |
Loading…
Reference in new issue