@@ -14,14 +14,18 @@ use cosmos_client::{
1414use cosmos_sdk_event:: CosmosSdkEvent ;
1515use ibc_union_spec:: { ChannelId , ClientId , ConnectionId , Timestamp } ;
1616use protos:: {
17- cosmos:: base:: v1beta1:: Coin ,
17+ cosmos:: {
18+ bank:: v1beta1:: { QueryBalanceRequest , QueryBalanceResponse } ,
19+ base:: v1beta1:: Coin ,
20+ } ,
1821 cosmwasm:: wasm:: v1:: { QuerySmartContractStateRequest , QuerySmartContractStateResponse } ,
1922} ;
2023use serde:: { Deserialize , Serialize } ;
2124use ucs03_zkgm:: msg:: { PredictWrappedTokenResponse , QueryMsg } ;
2225use unionlabs:: {
2326 self ,
2427 google:: protobuf:: any:: mk_any,
28+ ibc:: core:: client:: height:: Height ,
2529 primitives:: { encoding:: HexUnprefixed , Bech32 , Bytes , H160 , H256 } ,
2630} ;
2731use voyager_sdk:: {
@@ -38,6 +42,7 @@ pub struct Config {
3842 pub chain_id : ChainId ,
3943 pub ibc_host_contract_address : Bech32 < H256 > ,
4044 pub keyring : KeyringConfig ,
45+ pub privileged_acc_keyring : KeyringConfig ,
4146 pub rpc_url : String ,
4247 pub gas_config : GasFillerConfig ,
4348 #[ serde( default ) ]
@@ -64,6 +69,7 @@ pub struct Module {
6469 pub chain_id : ChainId ,
6570 pub ibc_host_contract_address : Bech32 < H256 > ,
6671 pub keyring : ConcurrentKeyring < Bech32 < H160 > , LocalSigner > ,
72+ pub privileged_acc_keyring : ConcurrentKeyring < Bech32 < H160 > , LocalSigner > ,
6773 pub rpc : Rpc ,
6874 pub gas_config : any:: GasFiller ,
6975 pub bech32_prefix : String ,
@@ -128,6 +134,18 @@ impl Module {
128134 }
129135 } ) ,
130136 ) ,
137+ privileged_acc_keyring : ConcurrentKeyring :: new (
138+ config. privileged_acc_keyring . name ,
139+ config. privileged_acc_keyring . keys . into_iter ( ) . map ( |entry| {
140+ let signer =
141+ LocalSigner :: new ( entry. value ( ) . try_into ( ) . unwrap ( ) , bech32_prefix. clone ( ) ) ;
142+
143+ KeyringEntry {
144+ address : signer. address ( ) ,
145+ signer,
146+ }
147+ } ) ,
148+ ) ,
131149 rpc,
132150 chain_id : ChainId :: new ( chain_id) ,
133151 gas_config : config
@@ -345,6 +363,65 @@ impl Module {
345363 . unwrap ( ) )
346364 }
347365
366+ pub async fn wait_for_update_client (
367+ & self ,
368+ expected_revision_height : u64 ,
369+ max_wait : Duration ,
370+ ) -> anyhow:: Result < helpers:: UpdateClient > {
371+ Ok ( self
372+ . wait_for_event (
373+ move |evt| {
374+ if let ModuleEvent :: UpdateClient {
375+ consensus_heights, ..
376+ } = evt
377+ {
378+ if let Some ( first) = consensus_heights. first ( ) {
379+ if first. height ( ) >= expected_revision_height {
380+ return Some ( helpers:: UpdateClient {
381+ height : first. height ( ) ,
382+ } ) ;
383+ }
384+ }
385+ }
386+ None
387+ } ,
388+ max_wait,
389+ 1 ,
390+ )
391+ . await ?
392+ . pop ( )
393+ . unwrap ( ) )
394+ }
395+
396+ pub async fn wait_for_packet_timeout (
397+ & self ,
398+ packet_hash_param : H256 ,
399+ max_wait : Duration ,
400+ ) -> anyhow:: Result < helpers:: PacketTimeout > {
401+ println ! ( "Waiting for packet timeout event with hash: {packet_hash_param:?}" ) ;
402+ Ok ( self
403+ . wait_for_event (
404+ move |evt| {
405+ if let ModuleEvent :: WasmPacketTimeout { packet_hash, .. } = evt {
406+ println ! ( "Packet timeout event came with hash: {packet_hash:?}" ) ;
407+ if packet_hash. as_ref ( ) == packet_hash_param. as_ref ( ) {
408+ return Some ( helpers:: PacketTimeout {
409+ packet_hash : * packet_hash,
410+ } ) ;
411+ }
412+ None
413+ } else {
414+ None
415+ }
416+ } ,
417+ max_wait,
418+ 1 ,
419+ )
420+ . await ?
421+ . pop ( )
422+ . unwrap ( ) )
423+ }
424+
348425 pub async fn wait_for_packet_ack (
349426 & self ,
350427 packet_hash_param : H256 ,
@@ -354,10 +431,22 @@ impl Module {
354431 Ok ( self
355432 . wait_for_event (
356433 move |evt| {
357- if let ModuleEvent :: WasmPacketAck { packet_hash, .. } = evt {
434+ if let ModuleEvent :: WasmPacketAck {
435+ packet_hash,
436+ acknowledgement,
437+ ..
438+ } = evt
439+ {
358440 if packet_hash. as_ref ( ) == packet_hash_param. as_ref ( ) {
441+ let ack_bytes: & [ u8 ] = acknowledgement. as_ref ( ) ;
442+
443+ // Grab the first 32 bytes — this is the uint256 in ABI encoding
444+ let mut tag_be = [ 0u8 ; 32 ] ;
445+ tag_be. copy_from_slice ( & ack_bytes[ ..32 ] ) ;
446+ let tag_u128 = u128:: from_be_bytes ( tag_be[ 16 ..] . try_into ( ) . ok ( ) ?) ;
359447 return Some ( helpers:: PacketAck {
360448 packet_hash : * packet_hash,
449+ tag : tag_u128,
361450 } ) ;
362451 }
363452 None
@@ -480,6 +569,52 @@ impl Module {
480569 }
481570 }
482571
572+ pub async fn get_minter ( & self , contract : Bech32 < H256 > ) -> anyhow:: Result < String > {
573+ let req = QuerySmartContractStateRequest {
574+ address : contract. to_string ( ) ,
575+ query_data : serde_json:: to_vec ( & QueryMsg :: GetMinter { } ) ?,
576+ } ;
577+
578+ let raw = self
579+ . rpc
580+ . client ( )
581+ . grpc_abci_query :: < _ , QuerySmartContractStateResponse > (
582+ "/cosmwasm.wasm.v1.Query/SmartContractState" ,
583+ & req,
584+ None ,
585+ false ,
586+ )
587+ . await ?
588+ . into_result ( ) ?
589+ . unwrap ( )
590+ . data ;
591+
592+ // 3) Deserialize the JSON `{ "minter": "union1..." }` into our struct
593+ let resp = serde_json:: from_slice ( & raw ) . context ( "deserializing GetMinterResponse" ) ?;
594+
595+ Ok ( resp)
596+ }
597+
598+ pub async fn get_balance (
599+ & self ,
600+ address : impl Into < String > ,
601+ denom : & str ,
602+ ) -> anyhow:: Result < protos:: cosmos:: base:: v1beta1:: Coin > {
603+ let req = QueryBalanceRequest {
604+ address : address. into ( ) ,
605+ denom : denom. to_string ( ) ,
606+ } ;
607+ let resp: QueryBalanceResponse = self
608+ . rpc
609+ . client ( )
610+ . grpc_abci_query ( "/cosmos.bank.v1beta1.Query/Balance" , & req, None , false )
611+ . await ?
612+ . into_result ( ) ?
613+ . unwrap ( ) ;
614+ resp. balance
615+ . ok_or_else ( || anyhow:: anyhow!( "no balance for denom {}" , denom) )
616+ }
617+
483618 pub async fn send_transaction_with_retry (
484619 & self ,
485620 contract : Bech32 < H256 > ,
@@ -561,10 +696,12 @@ impl Module {
561696 contract : Bech32 < H256 > ,
562697 msg : ( Vec < u8 > , Vec < Coin > ) ,
563698 signer : & LocalSigner ,
564- ) -> anyhow:: Result < H256 > {
699+ ) -> anyhow:: Result < ( H256 , u64 ) > {
565700 let result = self . send_transaction ( contract, msg, signer) . await ;
566-
567701 let tx_result = result. ok_or_else ( || anyhow ! ( "failed to send transaction" ) ) ??;
702+ let height = tx_result
703+ . height
704+ . ok_or_else ( || anyhow ! ( "transaction height not found" ) ) ?;
568705
569706 let send_event = tx_result
570707 . tx_result
@@ -580,7 +717,7 @@ impl Module {
580717 . ok_or_else ( || anyhow ! ( "wasm-packet_send event not found" ) ) ?;
581718
582719 Ok ( match send_event {
583- ModuleEvent :: WasmPacketSend { packet_hash, .. } => packet_hash,
720+ ModuleEvent :: WasmPacketSend { packet_hash, .. } => ( packet_hash, height . get ( ) ) ,
584721 _ => bail ! ( "unexpected event variant" ) ,
585722 } )
586723 }
@@ -610,6 +747,12 @@ pub enum ModuleEvent {
610747 channel_id : ChannelId ,
611748 packet_hash : H256 ,
612749 } ,
750+ #[ serde( rename = "wasm-packet_timeout" ) ]
751+ WasmPacketTimeout {
752+ #[ serde( with = "serde_utils::string" ) ]
753+ channel_id : ChannelId ,
754+ packet_hash : H256 ,
755+ } ,
613756
614757 #[ serde( rename = "wasm-create_client" ) ]
615758 WasmCreateClient {
@@ -658,4 +801,46 @@ pub enum ModuleEvent {
658801 packet_hash : H256 ,
659802 acknowledgement : Bytes < HexUnprefixed > ,
660803 } ,
804+
805+ #[ serde( rename = "update_client" ) ]
806+ UpdateClient {
807+ client_id : unionlabs:: id:: ClientId ,
808+ client_type : String ,
809+ #[ serde( with = "height_list_comma_separated" ) ]
810+ consensus_heights : Vec < Height > ,
811+ } ,
812+ }
813+
814+ // TODO: Check if human readable
815+ pub mod height_list_comma_separated {
816+ use std:: string:: String ;
817+
818+ use serde:: {
819+ de:: { self , Deserialize } ,
820+ Deserializer , Serialize , Serializer ,
821+ } ;
822+ use unionlabs:: ibc:: core:: client:: height:: Height ;
823+
824+ #[ allow( clippy:: ptr_arg) ] // required by serde
825+ pub fn serialize < S > ( data : & Vec < Height > , serializer : S ) -> Result < S :: Ok , S :: Error >
826+ where
827+ S : Serializer ,
828+ {
829+ data. iter ( )
830+ . map ( |height| format ! ( "{height:#}" ) )
831+ . collect :: < Vec < _ > > ( )
832+ . join ( "," )
833+ . serialize ( serializer)
834+ }
835+
836+ pub fn deserialize < ' de , D > ( deserializer : D ) -> Result < Vec < Height > , D :: Error >
837+ where
838+ D : Deserializer < ' de > ,
839+ {
840+ String :: deserialize ( deserializer) ?
841+ . split ( ',' )
842+ . map ( Height :: from_str_allow_zero_revision)
843+ . collect :: < Result < _ , _ > > ( )
844+ . map_err ( de:: Error :: custom)
845+ }
661846}
0 commit comments