Some checks failed
CodeQL / Analyze (push) Waiting to run
golangci-lint / lint (push) Waiting to run
Tests / Code Coverage / build (amd64) (push) Waiting to run
Tests / Code Coverage / build (arm64) (push) Waiting to run
Tests / Code Coverage / unit-tests (map[additional-args:-tags="test_e2e" name:e2e path:./e2e]) (push) Waiting to run
Tests / Code Coverage / unit-tests (map[name:08-wasm path:./modules/light-clients/08-wasm]) (push) Waiting to run
Tests / Code Coverage / unit-tests (map[name:ibc-go path:.]) (push) Waiting to run
Docker Build & Push Simapp (main) / docker-build (push) Has been cancelled
151 lines
5.3 KiB
Go
151 lines
5.3 KiB
Go
package keeper
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/cosmos/gogoproto/proto"
|
|
|
|
errorsmod "cosmossdk.io/errors"
|
|
|
|
sdk "git.cw.tr/mukan-network/mukan-sdk/types"
|
|
|
|
"git.cw.tr/mukan-network/mukan-ibc/modules/apps/transfer/internal/events"
|
|
"git.cw.tr/mukan-network/mukan-ibc/modules/apps/transfer/internal/telemetry"
|
|
"git.cw.tr/mukan-network/mukan-ibc/modules/apps/transfer/types"
|
|
clienttypes "git.cw.tr/mukan-network/mukan-ibc/modules/core/02-client/types"
|
|
channeltypesv2 "git.cw.tr/mukan-network/mukan-ibc/modules/core/04-channel/v2/types"
|
|
ibcerrors "git.cw.tr/mukan-network/mukan-ibc/modules/core/errors"
|
|
)
|
|
|
|
var _ types.MsgServer = (*Keeper)(nil)
|
|
|
|
// Transfer defines an rpc handler method for MsgTransfer.
|
|
func (k Keeper) Transfer(goCtx context.Context, msg *types.MsgTransfer) (*types.MsgTransferResponse, error) {
|
|
ctx := sdk.UnwrapSDKContext(goCtx)
|
|
|
|
if !k.GetParams(ctx).SendEnabled {
|
|
return nil, types.ErrSendDisabled
|
|
}
|
|
|
|
sender, err := k.addressCodec.StringToBytes(msg.Sender)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
coin := msg.Token
|
|
|
|
// Using types.UnboundedSpendLimit allows us to send the entire balance of a given denom.
|
|
if coin.Amount.Equal(types.UnboundedSpendLimit()) {
|
|
coin.Amount = k.BankKeeper.SpendableCoin(ctx, sender, coin.Denom).Amount
|
|
if coin.Amount.IsZero() {
|
|
return nil, errorsmod.Wrapf(types.ErrInvalidAmount, "empty spendable balance for %s", coin.Denom)
|
|
}
|
|
}
|
|
|
|
token, err := k.TokenFromCoin(ctx, coin)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
packetData := types.NewFungibleTokenPacketData(token.Denom.Path(), token.Amount, msg.Sender, msg.Receiver, msg.Memo)
|
|
|
|
if err := packetData.ValidateBasic(); err != nil {
|
|
return nil, errorsmod.Wrapf(err, "failed to validate %s packet data", types.V1)
|
|
}
|
|
|
|
// if a channel exists with source channel, then use IBC V1 protocol
|
|
// otherwise use IBC V2 protocol
|
|
channel, isIBCV1 := k.channelKeeper.GetChannel(ctx, msg.SourcePort, msg.SourceChannel)
|
|
|
|
var sequence uint64
|
|
if isIBCV1 {
|
|
// if a V1 channel exists for the source channel, then use IBC V1 protocol
|
|
sequence, err = k.transferV1Packet(ctx, msg.SourceChannel, token, msg.TimeoutHeight, msg.TimeoutTimestamp, sender, packetData)
|
|
// telemetry for transfer occurs here, in IBC V2 this is done in the onSendPacket callback
|
|
telemetry.ReportTransfer(msg.SourcePort, msg.SourceChannel, channel.Counterparty.PortId, channel.Counterparty.ChannelId, token)
|
|
} else {
|
|
// otherwise try to send an IBC V2 packet, if the sourceChannel is not a IBC V2 client
|
|
// then core IBC will return a CounterpartyNotFound error
|
|
sequence, err = k.transferV2Packet(ctx, msg.Encoding, msg.SourceChannel, msg.TimeoutTimestamp, packetData)
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
k.Logger(ctx).Info("IBC fungible token transfer", "token", coin, "sender", msg.Sender, "receiver", msg.Receiver)
|
|
|
|
return &types.MsgTransferResponse{Sequence: sequence}, nil
|
|
}
|
|
|
|
func (k *Keeper) transferV1Packet(ctx sdk.Context, sourceChannel string, token types.Token, timeoutHeight clienttypes.Height, timeoutTimestamp uint64, sender sdk.AccAddress, packetData types.FungibleTokenPacketData) (uint64, error) {
|
|
if err := k.SendTransfer(ctx, types.PortID, sourceChannel, token, sender); err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
packetDataBytes := packetData.GetBytes()
|
|
sequence, err := k.ics4Wrapper.SendPacket(ctx, types.PortID, sourceChannel, timeoutHeight, timeoutTimestamp, packetDataBytes)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
events.EmitTransferEvent(ctx, packetData.Sender, packetData.Receiver, token, packetData.Memo)
|
|
|
|
return sequence, nil
|
|
}
|
|
|
|
func (k Keeper) transferV2Packet(ctx sdk.Context, encoding, sourceChannel string, timeoutTimestamp uint64, packetData types.FungibleTokenPacketData) (uint64, error) {
|
|
if encoding == "" {
|
|
encoding = types.EncodingJSON
|
|
}
|
|
|
|
data, err := types.MarshalPacketData(packetData, types.V1, encoding)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
payload := channeltypesv2.NewPayload(
|
|
types.PortID, types.PortID,
|
|
types.V1, encoding, data,
|
|
)
|
|
msg := channeltypesv2.NewMsgSendPacket(
|
|
sourceChannel, timeoutTimestamp,
|
|
packetData.Sender, payload,
|
|
)
|
|
|
|
handler := k.msgRouter.Handler(msg)
|
|
if handler == nil {
|
|
return 0, errorsmod.Wrapf(ibcerrors.ErrInvalidRequest, "unrecognized packet type: %T", msg)
|
|
}
|
|
res, err := handler(ctx, msg)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
// NOTE: The sdk msg handler creates a new EventManager, so events must be correctly propagated back to the current context
|
|
ctx.EventManager().EmitEvents(res.GetEvents())
|
|
|
|
// Each individual sdk.Result has exactly one Msg response. We aggregate here.
|
|
msgResponse := res.MsgResponses[0]
|
|
if msgResponse == nil {
|
|
return 0, errorsmod.Wrapf(ibcerrors.ErrLogic, "got nil Msg response for msg %s", sdk.MsgTypeURL(msg))
|
|
}
|
|
var sendResponse channeltypesv2.MsgSendPacketResponse
|
|
err = proto.Unmarshal(msgResponse.Value, &sendResponse)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
return sendResponse.Sequence, nil
|
|
}
|
|
|
|
// UpdateParams defines an rpc handler method for MsgUpdateParams. Updates the ibc-transfer module's parameters.
|
|
func (k Keeper) UpdateParams(goCtx context.Context, msg *types.MsgUpdateParams) (*types.MsgUpdateParamsResponse, error) {
|
|
if k.GetAuthority() != msg.Signer {
|
|
return nil, errorsmod.Wrapf(ibcerrors.ErrUnauthorized, "expected %s, got %s", k.GetAuthority(), msg.Signer)
|
|
}
|
|
|
|
ctx := sdk.UnwrapSDKContext(goCtx)
|
|
k.SetParams(ctx, msg.Params)
|
|
|
|
return &types.MsgUpdateParamsResponse{}, nil
|
|
}
|