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
use thiserror::Error;
use tracing::error;
use ibc::application::ics20_fungible_token_transfer::msgs::transfer::MsgTransfer;
use ibc::events::IbcEvent;
use ibc::ics24_host::identifier::{ChainId, ChannelId, PortId};
use ibc::tx_msg::Msg;
use crate::chain::{Chain, CosmosSdkChain};
use crate::config::ChainConfig;
use crate::error::Error;
use ibc::timestamp::Timestamp;
use ibc::Height;
use std::time::Duration;
#[derive(Debug, Error)]
pub enum PacketError {
#[error("failed with underlying cause: {0}")]
Failed(String),
#[error("key error with underlying cause: {0}")]
Key(Error),
#[error(
"failed during a transaction submission step to chain id {0} with underlying error: {1}"
)]
Submit(ChainId, Error),
#[error("timestamp overflow")]
TimestampOverflow,
}
#[derive(Clone, Debug)]
pub struct TransferOptions {
pub packet_src_chain_config: ChainConfig,
pub packet_dst_chain_config: ChainConfig,
pub packet_src_port_id: PortId,
pub packet_src_channel_id: ChannelId,
pub amount: u64,
pub denom: String,
pub receiver: Option<String>,
pub timeout_height_offset: u64,
pub timeout_seconds: Duration,
pub number_msgs: usize,
}
pub fn build_and_send_transfer_messages(
mut packet_src_chain: CosmosSdkChain,
mut packet_dst_chain: CosmosSdkChain,
opts: TransferOptions,
) -> Result<Vec<IbcEvent>, PacketError> {
let receiver = match &opts.receiver {
None => packet_dst_chain.get_signer(),
Some(r) => Ok(r.clone().into()),
}
.map_err(PacketError::Key)?;
let sender = packet_src_chain.get_signer().map_err(PacketError::Key)?;
let timeout_timestamp = if opts.timeout_seconds == Duration::from_secs(0) {
Timestamp::none()
} else {
(Timestamp::now() + opts.timeout_seconds).map_err(|_| PacketError::TimestampOverflow)?
};
let timeout_height = if opts.timeout_height_offset == 0 {
Height::zero()
} else {
packet_dst_chain
.query_latest_height()
.map_err(|_| PacketError::Failed("Height error".to_string()))?
.add(opts.timeout_height_offset)
};
let msg = MsgTransfer {
source_port: opts.packet_src_port_id.clone(),
source_channel: opts.packet_src_channel_id.clone(),
token: Some(ibc_proto::cosmos::base::v1beta1::Coin {
denom: opts.denom.clone(),
amount: opts.amount.to_string(),
}),
sender,
receiver,
timeout_height,
timeout_timestamp,
};
let raw_msg = msg.to_any();
let msgs = vec![raw_msg; opts.number_msgs];
let events = packet_src_chain
.send_msgs(msgs)
.map_err(|e| PacketError::Submit(packet_src_chain.id().clone(), e))?;
let result = events
.iter()
.find(|event| matches!(event, IbcEvent::ChainError(_)));
match result {
None => Ok(events),
Some(err) => {
if let IbcEvent::ChainError(err) = err {
Err(PacketError::Failed(err.to_string()))
} else {
panic!(
"internal error, expected IBCEvent::ChainError, got {:?}",
err
)
}
}
}
}