package query import ( "context" "errors" "sort" "github.com/cosmos/interchaintest/v10/ibc" "cosmossdk.io/math" "git.cw.tr/mukan-network/mukan-sdk/client/grpc/cmtservice" sdk "git.cw.tr/mukan-network/mukan-sdk/types" authtypes "git.cw.tr/mukan-network/mukan-sdk/x/auth/types" banktypes "git.cw.tr/mukan-network/mukan-sdk/x/bank/types" controllertypes "git.cw.tr/mukan-network/mukan-ibc/modules/apps/27-interchain-accounts/controller/types" transfertypes "git.cw.tr/mukan-network/mukan-ibc/modules/apps/transfer/types" clienttypes "git.cw.tr/mukan-network/mukan-ibc/modules/core/02-client/types" channeltypes "git.cw.tr/mukan-network/mukan-ibc/modules/core/04-channel/types" ibcexported "git.cw.tr/mukan-network/mukan-ibc/modules/core/exported" ) const queryPathTransferDenoms = "/ibc.applications.transfer.v1.Query/Denoms" // ModuleAccountAddress returns the address of the given module on the given chain. // Added because interchaintest's method doesn't work. func ModuleAccountAddress(ctx context.Context, moduleName string, chain ibc.Chain) (sdk.AccAddress, error) { modAccResp, err := GRPCQuery[authtypes.QueryModuleAccountByNameResponse]( ctx, chain, &authtypes.QueryModuleAccountByNameRequest{Name: moduleName}, ) if err != nil { return nil, err } cfg := chain.Config().EncodingConfig var account sdk.AccountI err = cfg.InterfaceRegistry.UnpackAny(modAccResp.Account, &account) if err != nil { return nil, err } govAccount, ok := account.(sdk.ModuleAccountI) if !ok { return nil, errors.New("account is not a module account") } if govAccount.GetAddress().String() == "" { return nil, errors.New("module account address is empty") } return govAccount.GetAddress(), nil } // ClientState queries the client state on the given chain for the provided clientID. func ClientState(ctx context.Context, chain ibc.Chain, clientID string) (ibcexported.ClientState, error) { clientStateResp, err := GRPCQuery[clienttypes.QueryClientStateResponse](ctx, chain, &clienttypes.QueryClientStateRequest{ ClientId: clientID, }) if err != nil { return nil, err } clientStateAny := clientStateResp.ClientState cfg := chain.Config().EncodingConfig var clientState ibcexported.ClientState err = cfg.InterfaceRegistry.UnpackAny(clientStateAny, &clientState) if err != nil { return nil, err } return clientState, nil } // ClientStatus queries the status of the client by clientID func ClientStatus(ctx context.Context, chain ibc.Chain, clientID string) (string, error) { clientStatusResp, err := GRPCQuery[clienttypes.QueryClientStatusResponse](ctx, chain, &clienttypes.QueryClientStatusRequest{ ClientId: clientID, }) if err != nil { return "", err } return clientStatusResp.Status, nil } // GetValidatorSetByHeight returns the validators of the given chain at the specified height. The returned validators // are sorted by address. func GetValidatorSetByHeight(ctx context.Context, chain ibc.Chain, height uint64) ([]*cmtservice.Validator, error) { res, err := GRPCQuery[cmtservice.GetValidatorSetByHeightResponse](ctx, chain, &cmtservice.GetValidatorSetByHeightRequest{ Height: int64(height), }) if err != nil { return nil, err } sort.SliceStable(res.Validators, func(i, j int) bool { return res.Validators[i].Address < res.Validators[j].Address }) return res.Validators, nil } // Balance returns the balance of a specific denomination for a given account by address. func Balance(ctx context.Context, chain ibc.Chain, address string, denom string) (math.Int, error) { res, err := GRPCQuery[banktypes.QueryBalanceResponse](ctx, chain, &banktypes.QueryBalanceRequest{ Address: address, Denom: denom, }) if err != nil { return math.Int{}, err } return res.Balance.Amount, nil } // Channel queries the channel on a given chain for the provided portID and channelID func Channel(ctx context.Context, chain ibc.Chain, portID, channelID string) (channeltypes.Channel, error) { res, err := GRPCQuery[channeltypes.QueryChannelResponse](ctx, chain, &channeltypes.QueryChannelRequest{ PortId: portID, ChannelId: channelID, }) if err != nil { return channeltypes.Channel{}, err } return *res.Channel, nil } // TotalEscrowForDenom queries the total amount of tokens in escrow for a denom func TotalEscrowForDenom(ctx context.Context, chain ibc.Chain, denom string) (sdk.Coin, error) { res, err := GRPCQuery[transfertypes.QueryTotalEscrowForDenomResponse](ctx, chain, &transfertypes.QueryTotalEscrowForDenomRequest{ Denom: denom, }) if err != nil { return sdk.Coin{}, err } return res.Amount, nil } // PacketAcknowledgements queries the packet acknowledgements on the given chain for the provided channel (optional) list of packet commitment sequences. func PacketAcknowledgements(ctx context.Context, chain ibc.Chain, portID, channelID string, packetCommitmentSequences []uint64) ([]*channeltypes.PacketState, error) { res, err := GRPCQuery[channeltypes.QueryPacketAcknowledgementsResponse](ctx, chain, &channeltypes.QueryPacketAcknowledgementsRequest{ PortId: portID, ChannelId: channelID, PacketCommitmentSequences: packetCommitmentSequences, }) if err != nil { return nil, err } return res.Acknowledgements, nil } // InterchainAccount queries the interchain account for the given owner and connectionID. func InterchainAccount(ctx context.Context, chain ibc.Chain, address, connectionID string) (string, error) { res, err := GRPCQuery[controllertypes.QueryInterchainAccountResponse](ctx, chain, &controllertypes.QueryInterchainAccountRequest{ Owner: address, ConnectionId: connectionID, }) if err != nil { return "", err } return res.Address, nil } func TransferDenoms(ctx context.Context, chain ibc.Chain) (*transfertypes.QueryDenomsResponse, error) { return grpcQueryWithMethod[transfertypes.QueryDenomsResponse](ctx, chain, &transfertypes.QueryDenomsRequest{}, queryPathTransferDenoms) }