diff --git a/rust/tw_cosmos_sdk/src/modules/serializer/json_serializer.rs b/rust/tw_cosmos_sdk/src/modules/serializer/json_serializer.rs index 0b2c81195ad..1bf3dff3649 100644 --- a/rust/tw_cosmos_sdk/src/modules/serializer/json_serializer.rs +++ b/rust/tw_cosmos_sdk/src/modules/serializer/json_serializer.rs @@ -18,6 +18,9 @@ pub struct SignedTxJson { pub memo: String, pub msg: Vec>, pub signatures: Vec, + #[serde(skip_serializing_if = "Option::is_none")] + #[serde(default)] + pub timeout_height: Option, } #[derive(Serialize)] @@ -28,6 +31,9 @@ pub struct UnsignedTxJson { pub memo: String, pub msgs: Vec>, pub sequence: String, + #[serde(skip_serializing_if = "Option::is_none")] + #[serde(default)] + pub timeout_height: Option, } #[derive(Serialize)] @@ -69,11 +75,19 @@ where let signature = Self::serialize_signature(&signed.signer.public_key, signed.signature.clone()); + let convert = |value: u64| { + if value == 0 { + None + } else { + Some(value.to_string()) + } + }; Ok(SignedTxJson { fee: Self::build_fee(&signed.fee), memo: signed.tx_body.memo.clone(), msg, signatures: vec![signature], + timeout_height: convert(signed.tx_body.timeout_height), }) } @@ -87,6 +101,13 @@ where .map(|msg| msg.to_json()) .collect::>()?; + let convert = |value: u64| { + if value == 0 { + None + } else { + Some(value.to_string()) + } + }; Ok(UnsignedTxJson { account_number: unsigned.account_number.to_string(), chain_id: unsigned.chain_id.clone(), @@ -94,6 +115,7 @@ where memo: unsigned.tx_body.memo.clone(), msgs, sequence: unsigned.signer.sequence.to_string(), + timeout_height: convert(unsigned.tx_body.timeout_height), }) } diff --git a/rust/tw_cosmos_sdk/src/modules/tx_builder.rs b/rust/tw_cosmos_sdk/src/modules/tx_builder.rs index c39d13f8c70..8bd97cafbb1 100644 --- a/rust/tw_cosmos_sdk/src/modules/tx_builder.rs +++ b/rust/tw_cosmos_sdk/src/modules/tx_builder.rs @@ -20,8 +20,6 @@ use tw_number::U256; use tw_proto::Cosmos::Proto; use tw_proto::{google, serialize}; -const DEFAULT_TIMEOUT_HEIGHT: u64 = 0; - pub struct TxBuilder { _phantom: PhantomData, } @@ -126,7 +124,7 @@ where Ok(TxBody { messages, memo: input.memo.to_string(), - timeout_height: DEFAULT_TIMEOUT_HEIGHT, + timeout_height: input.timeout_height, }) } diff --git a/rust/tw_cosmos_sdk/tests/sign.rs b/rust/tw_cosmos_sdk/tests/sign.rs index 37436b4947f..8b703393ff3 100644 --- a/rust/tw_cosmos_sdk/tests/sign.rs +++ b/rust/tw_cosmos_sdk/tests/sign.rs @@ -135,6 +135,70 @@ fn test_sign_raw_json() { }); } +#[test] +fn test_sign_raw_json_with_timeout() { + let coin = TestCoinContext::default() + .with_public_key_type(PublicKeyType::Secp256k1) + .with_hrp("cosmos"); + + let raw_json_msg = Proto::mod_Message::RawJSON { + type_pb: "osmosis/poolmanager/split-amount-in".into(), + value: r#"{ + "routes": [ + { + "pools": [ + { + "pool_id": "463", + "token_out_denom": "ibc/1DC495FCEFDA068A3820F903EDBD78B942FBD204D7E93D3BA2B432E9669D1A59" + }, + { + "pool_id": "916", + "token_out_denom": "ibc/573FCD90FACEE750F55A8864EF7D38265F07E5A9273FA0E8DAFD39951332B580" + } + ], + "token_in_amount": "70000" + }, + { + "pools": [ + { + "pool_id": "907", + "token_out_denom": "ibc/573FCD90FACEE750F55A8864EF7D38265F07E5A9273FA0E8DAFD39951332B580" + } + ], + "token_in_amount": "30000" + } + ], + "sender": "osmo1qr7dhmvcqm4fnleaqel3gel4u20nk5rp9rwsae", + "token_in_denom": "uosmo", + "token_out_min_amount": "885297" + }"#.into(), + }; + let input = Proto::SigningInput { + account_number: 24139, + chain_id: "osmosis-1".into(), + sequence: 191, + fee: Some(make_fee(617438, make_amount("uosmo", "1853"))), + private_key: account_1037_private_key(), + messages: vec![make_message(MessageEnum::raw_json_message(raw_json_msg))], + timeout_height: 13692007, + ..Proto::SigningInput::default() + }; + + // `RawJSON` doesn't support Protobuf serialization and signing. + test_sign_protobuf_error::(TestErrorInput { + coin: &coin, + input: input.clone(), + error: SigningError::Error_not_supported, + }); + test_sign_json::(TestInput { + coin: &coin, + input, + tx: r#"{"mode":"block","tx":{"fee":{"amount":[{"amount":"1853","denom":"uosmo"}],"gas":"617438"},"memo":"","msg":[{"type":"osmosis/poolmanager/split-amount-in","value":{"routes":[{"pools":[{"pool_id":"463","token_out_denom":"ibc/1DC495FCEFDA068A3820F903EDBD78B942FBD204D7E93D3BA2B432E9669D1A59"},{"pool_id":"916","token_out_denom":"ibc/573FCD90FACEE750F55A8864EF7D38265F07E5A9273FA0E8DAFD39951332B580"}],"token_in_amount":"70000"},{"pools":[{"pool_id":"907","token_out_denom":"ibc/573FCD90FACEE750F55A8864EF7D38265F07E5A9273FA0E8DAFD39951332B580"}],"token_in_amount":"30000"}],"sender":"osmo1qr7dhmvcqm4fnleaqel3gel4u20nk5rp9rwsae","token_in_denom":"uosmo","token_out_min_amount":"885297"}}],"signatures":[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"AlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3F"},"signature":"7gMxXwqzZDe5+h1i16q7A7CgGUtLl2+Q8/YaUZCeYvp8kISbBwD2SlNTpJtz1RLskzF2uNcDebo61HbcVn9dAw=="}],"timeout_height":"13692007"}}"#, + signature: "ee03315f0ab36437b9fa1d62d7aabb03b0a0194b4b976f90f3f61a51909e62fa7c90849b0700f64a5353a49b73d512ec933176b8d70379ba3ad476dc567f5d03", + signature_json: r#"[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"AlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3F"},"signature":"7gMxXwqzZDe5+h1i16q7A7CgGUtLl2+Q8/YaUZCeYvp8kISbBwD2SlNTpJtz1RLskzF2uNcDebo61HbcVn9dAw=="}]"#, + }); +} + #[test] fn test_sign_ibc_transfer() { let coin = TestCoinContext::default() diff --git a/src/proto/Cosmos.proto b/src/proto/Cosmos.proto index 6ac55b56570..04384442e93 100644 --- a/src/proto/Cosmos.proto +++ b/src/proto/Cosmos.proto @@ -425,6 +425,9 @@ message SigningInput { // Optional. If set, use a different Signer info when signing the transaction. SignerInfo signer_info = 12; + + // Optional timeout_height + uint64 timeout_height = 13; } // Result containing the signed and encoded transaction.