@@ -107,6 +107,11 @@ const MIN_TIME_LOCK: i64 = 50;
107
107
108
108
const ACCOUNT_SEQUENCE_ERR : & str = "incorrect account sequence" ;
109
109
110
+ pub struct SerializedUnsignedTx {
111
+ tx_json : Json ,
112
+ body_bytes : Vec < u8 > ,
113
+ }
114
+
110
115
type TendermintPrivKeyPolicy = PrivKeyPolicy < TendermintKeyPair > ;
111
116
112
117
pub struct TendermintKeyPair {
@@ -346,6 +351,7 @@ pub struct TendermintCoinImpl {
346
351
client : TendermintRpcClient ,
347
352
pub ( crate ) chain_registry_name : Option < String > ,
348
353
pub ( crate ) ctx : MmWeak ,
354
+ pub ( crate ) is_keplr_from_ledger : bool ,
349
355
}
350
356
351
357
#[ derive( Clone ) ]
@@ -599,6 +605,7 @@ impl TendermintCommons for TendermintCoin {
599
605
}
600
606
601
607
impl TendermintCoin {
608
+ #[ allow( clippy:: too_many_arguments) ]
602
609
pub async fn init (
603
610
ctx : & MmArc ,
604
611
ticker : String ,
@@ -607,6 +614,7 @@ impl TendermintCoin {
607
614
rpc_urls : Vec < String > ,
608
615
tx_history : bool ,
609
616
activation_policy : TendermintActivationPolicy ,
617
+ is_keplr_from_ledger : bool ,
610
618
) -> MmResult < Self , TendermintInitError > {
611
619
if rpc_urls. is_empty ( ) {
612
620
return MmError :: err ( TendermintInitError {
@@ -671,6 +679,7 @@ impl TendermintCoin {
671
679
client : TendermintRpcClient ( AsyncMutex :: new ( client_impl) ) ,
672
680
chain_registry_name : protocol_info. chain_registry_name ,
673
681
ctx : ctx. weak ( ) ,
682
+ is_keplr_from_ledger,
674
683
} ) ) )
675
684
}
676
685
@@ -868,19 +877,14 @@ impl TendermintCoin {
868
877
let ctx = try_tx_s ! ( MmArc :: from_weak( & self . ctx) . ok_or( ERRL !( "ctx must be initialized already" ) ) ) ;
869
878
870
879
let account_info = try_tx_s ! ( self . account_info( & self . account_id) . await ) ;
871
- let sign_doc = try_tx_s ! ( self . any_to_sign_doc( account_info, tx_payload, fee, timeout_height, memo) ) ;
872
-
873
- let unsigned_tx = json ! ( {
874
- "sign_doc" : {
875
- "body_bytes" : sign_doc. body_bytes,
876
- "auth_info_bytes" : sign_doc. auth_info_bytes,
877
- "chain_id" : sign_doc. chain_id,
878
- "account_number" : sign_doc. account_number,
879
- }
880
- } ) ;
880
+ let SerializedUnsignedTx { tx_json, body_bytes } = if self . is_keplr_from_ledger {
881
+ try_tx_s ! ( self . any_to_legacy_amino_json( account_info, tx_payload, fee, timeout_height, memo) )
882
+ } else {
883
+ try_tx_s ! ( self . any_to_serialized_sign_doc( account_info, tx_payload, fee, timeout_height, memo) )
884
+ } ;
881
885
882
886
let data: TxHashData = try_tx_s ! ( ctx
883
- . ask_for_data( & format!( "TX_HASH:{}" , self . ticker( ) ) , unsigned_tx , timeout)
887
+ . ask_for_data( & format!( "TX_HASH:{}" , self . ticker( ) ) , tx_json , timeout)
884
888
. await
885
889
. map_err( |e| ERRL !( "{}" , e) ) ) ;
886
890
@@ -892,7 +896,7 @@ impl TendermintCoin {
892
896
signatures : tx. signatures ,
893
897
} ;
894
898
895
- if sign_doc . body_bytes != tx_raw_inner. body_bytes {
899
+ if body_bytes != tx_raw_inner. body_bytes {
896
900
return Err ( crate :: TransactionErr :: Plain ( ERRL ! (
897
901
"Unsigned transaction don't match with the externally provided transaction."
898
902
) ) ) ;
@@ -1166,18 +1170,13 @@ impl TendermintCoin {
1166
1170
hex:: encode_upper ( hash. as_slice ( ) ) ,
1167
1171
) )
1168
1172
} else {
1169
- let sign_doc = self . any_to_sign_doc ( account_info, message, fee, timeout_height, memo) ?;
1170
-
1171
- let tx = json ! ( {
1172
- "sign_doc" : {
1173
- "body_bytes" : sign_doc. body_bytes,
1174
- "auth_info_bytes" : sign_doc. auth_info_bytes,
1175
- "chain_id" : sign_doc. chain_id,
1176
- "account_number" : sign_doc. account_number,
1177
- }
1178
- } ) ;
1173
+ let SerializedUnsignedTx { tx_json, .. } = if self . is_keplr_from_ledger {
1174
+ self . any_to_legacy_amino_json ( account_info, message, fee, timeout_height, memo)
1175
+ } else {
1176
+ self . any_to_serialized_sign_doc ( account_info, message, fee, timeout_height, memo)
1177
+ } ?;
1179
1178
1180
- Ok ( TransactionData :: Unsigned ( tx ) )
1179
+ Ok ( TransactionData :: Unsigned ( tx_json ) )
1181
1180
}
1182
1181
}
1183
1182
@@ -1253,18 +1252,116 @@ impl TendermintCoin {
1253
1252
sign_doc. sign ( & signkey)
1254
1253
}
1255
1254
1256
- pub ( super ) fn any_to_sign_doc (
1255
+ pub ( super ) fn any_to_serialized_sign_doc (
1257
1256
& self ,
1258
1257
account_info : BaseAccount ,
1259
1258
tx_payload : Any ,
1260
1259
fee : Fee ,
1261
1260
timeout_height : u64 ,
1262
1261
memo : String ,
1263
- ) -> cosmrs:: Result < SignDoc > {
1262
+ ) -> cosmrs:: Result < SerializedUnsignedTx > {
1264
1263
let tx_body = tx:: Body :: new ( vec ! [ tx_payload] , memo, timeout_height as u32 ) ;
1265
1264
let pubkey = self . activation_policy . public_key ( ) ?. into ( ) ;
1266
1265
let auth_info = SignerInfo :: single_direct ( Some ( pubkey) , account_info. sequence ) . auth_info ( fee) ;
1267
- SignDoc :: new ( & tx_body, & auth_info, & self . chain_id , account_info. account_number )
1266
+ let sign_doc = SignDoc :: new ( & tx_body, & auth_info, & self . chain_id , account_info. account_number ) ?;
1267
+
1268
+ let tx_json = json ! ( {
1269
+ "sign_doc" : {
1270
+ "body_bytes" : sign_doc. body_bytes,
1271
+ "auth_info_bytes" : sign_doc. auth_info_bytes,
1272
+ "chain_id" : sign_doc. chain_id,
1273
+ "account_number" : sign_doc. account_number,
1274
+ }
1275
+ } ) ;
1276
+
1277
+ Ok ( SerializedUnsignedTx {
1278
+ tx_json,
1279
+ body_bytes : sign_doc. body_bytes ,
1280
+ } )
1281
+ }
1282
+
1283
+ /// This should only be used for Keplr from Ledger!
1284
+ /// When using Keplr from Ledger, they don't accept `SING_MODE_DIRECT` transactions.
1285
+ ///
1286
+ /// Visit https://docs.cosmos.network/main/build/architecture/adr-050-sign-mode-textual#context for more context.
1287
+ pub ( super ) fn any_to_legacy_amino_json (
1288
+ & self ,
1289
+ account_info : BaseAccount ,
1290
+ tx_payload : Any ,
1291
+ fee : Fee ,
1292
+ timeout_height : u64 ,
1293
+ memo : String ,
1294
+ ) -> cosmrs:: Result < SerializedUnsignedTx > {
1295
+ const MSG_SEND_TYPE_URL : & str = "/cosmos.bank.v1beta1.MsgSend" ;
1296
+ const LEDGER_MSG_SEND_TYPE_URL : & str = "cosmos-sdk/MsgSend" ;
1297
+
1298
+ // Ledger's keplr works as wallet-only, so `MsgSend` support is enough for now.
1299
+ if tx_payload. type_url != MSG_SEND_TYPE_URL {
1300
+ return Err ( ErrorReport :: new ( io:: Error :: new (
1301
+ io:: ErrorKind :: Unsupported ,
1302
+ format ! (
1303
+ "Signing mode `SIGN_MODE_LEGACY_AMINO_JSON` is not supported for '{}' transaction type." ,
1304
+ tx_payload. type_url
1305
+ ) ,
1306
+ ) ) ) ;
1307
+ }
1308
+
1309
+ let msg_send = MsgSend :: from_any ( & tx_payload) ?;
1310
+ let timeout_height = u32:: try_from ( timeout_height) ?;
1311
+ let original_tx_type_url = tx_payload. type_url . clone ( ) ;
1312
+ let body_bytes = tx:: Body :: new ( vec ! [ tx_payload] , & memo, timeout_height) . into_bytes ( ) ?;
1313
+
1314
+ let amount: Vec < Json > = msg_send
1315
+ . amount
1316
+ . into_iter ( )
1317
+ . map ( |t| {
1318
+ json ! ( {
1319
+ "denom" : t. denom,
1320
+ // Numbers needs to be converted into string type.
1321
+ // Ref: https://github.com/cosmos/ledger-cosmos/blob/c707129e59f6e0f07ad67161a6b75e8951af063c/docs/TXSPEC.md#json-format
1322
+ "amount" : t. amount. to_string( ) ,
1323
+ } )
1324
+ } )
1325
+ . collect ( ) ;
1326
+
1327
+ let msg = json ! ( {
1328
+ "type" : LEDGER_MSG_SEND_TYPE_URL ,
1329
+ "value" : json!( {
1330
+ "from_address" : msg_send. from_address. to_string( ) ,
1331
+ "to_address" : msg_send. to_address. to_string( ) ,
1332
+ "amount" : amount,
1333
+ } )
1334
+ } ) ;
1335
+
1336
+ let fee_amount: Vec < Json > = fee
1337
+ . amount
1338
+ . into_iter ( )
1339
+ . map ( |t| {
1340
+ json ! ( {
1341
+ "denom" : t. denom,
1342
+ // Numbers needs to be converted into string type.
1343
+ // Ref: https://github.com/cosmos/ledger-cosmos/blob/c707129e59f6e0f07ad67161a6b75e8951af063c/docs/TXSPEC.md#json-format
1344
+ "amount" : t. amount. to_string( ) ,
1345
+ } )
1346
+ } )
1347
+ . collect ( ) ;
1348
+
1349
+ let tx_json = serde_json:: json!( {
1350
+ "legacy_amino_json" : {
1351
+ "account_number" : account_info. account_number. to_string( ) ,
1352
+ "chain_id" : self . chain_id. to_string( ) ,
1353
+ "fee" : {
1354
+ "amount" : fee_amount,
1355
+ "gas" : fee. gas_limit. to_string( )
1356
+ } ,
1357
+ "memo" : memo,
1358
+ "msgs" : [ msg] ,
1359
+ "sequence" : account_info. sequence. to_string( ) ,
1360
+ } ,
1361
+ "original_tx_type_url" : original_tx_type_url,
1362
+ } ) ;
1363
+
1364
+ Ok ( SerializedUnsignedTx { tx_json, body_bytes } )
1268
1365
}
1269
1366
1270
1367
pub fn add_activated_token_info ( & self , ticker : String , decimals : u8 , denom : Denom ) {
@@ -2024,6 +2121,13 @@ pub async fn get_ibc_chain_list() -> IBCChainRegistriesResult {
2024
2121
impl MmCoin for TendermintCoin {
2025
2122
fn is_asset_chain ( & self ) -> bool { false }
2026
2123
2124
+ fn wallet_only ( & self , ctx : & MmArc ) -> bool {
2125
+ let coin_conf = crate :: coin_conf ( ctx, self . ticker ( ) ) ;
2126
+ let wallet_only_conf = coin_conf[ "wallet_only" ] . as_bool ( ) . unwrap_or ( false ) ;
2127
+
2128
+ wallet_only_conf || self . is_keplr_from_ledger
2129
+ }
2130
+
2027
2131
fn spawner ( & self ) -> CoinFutSpawner { CoinFutSpawner :: new ( & self . abortable_system ) }
2028
2132
2029
2133
fn withdraw ( & self , req : WithdrawRequest ) -> WithdrawFut {
@@ -3214,6 +3318,7 @@ pub mod tendermint_coin_tests {
3214
3318
rpc_urls,
3215
3319
false ,
3216
3320
activation_policy,
3321
+ false ,
3217
3322
) )
3218
3323
. unwrap ( ) ;
3219
3324
@@ -3339,6 +3444,7 @@ pub mod tendermint_coin_tests {
3339
3444
rpc_urls,
3340
3445
false ,
3341
3446
activation_policy,
3447
+ false ,
3342
3448
) )
3343
3449
. unwrap ( ) ;
3344
3450
@@ -3401,6 +3507,7 @@ pub mod tendermint_coin_tests {
3401
3507
rpc_urls,
3402
3508
false ,
3403
3509
activation_policy,
3510
+ false ,
3404
3511
) )
3405
3512
. unwrap ( ) ;
3406
3513
@@ -3474,6 +3581,7 @@ pub mod tendermint_coin_tests {
3474
3581
rpc_urls,
3475
3582
false ,
3476
3583
activation_policy,
3584
+ false ,
3477
3585
) )
3478
3586
. unwrap ( ) ;
3479
3587
@@ -3670,6 +3778,7 @@ pub mod tendermint_coin_tests {
3670
3778
rpc_urls,
3671
3779
false ,
3672
3780
activation_policy,
3781
+ false ,
3673
3782
) )
3674
3783
. unwrap ( ) ;
3675
3784
@@ -3752,6 +3861,7 @@ pub mod tendermint_coin_tests {
3752
3861
rpc_urls,
3753
3862
false ,
3754
3863
activation_policy,
3864
+ false ,
3755
3865
) )
3756
3866
. unwrap ( ) ;
3757
3867
@@ -3827,6 +3937,7 @@ pub mod tendermint_coin_tests {
3827
3937
rpc_urls,
3828
3938
false ,
3829
3939
activation_policy,
3940
+ false ,
3830
3941
) )
3831
3942
. unwrap ( ) ;
3832
3943
@@ -3898,6 +4009,7 @@ pub mod tendermint_coin_tests {
3898
4009
rpc_urls,
3899
4010
false ,
3900
4011
activation_policy,
4012
+ false ,
3901
4013
) )
3902
4014
. unwrap ( ) ;
3903
4015
@@ -3952,6 +4064,7 @@ pub mod tendermint_coin_tests {
3952
4064
rpc_urls,
3953
4065
false ,
3954
4066
activation_policy,
4067
+ false ,
3955
4068
) )
3956
4069
. unwrap ( ) ;
3957
4070
0 commit comments