func (k Keeper) SetHTLC(ctx sdk.Context, htlc types.HTLC, id tmbytes.HexBytes) {
	store := ctx.KVStore(k.storeKey)
	bz := k.cdc.MustMarshal(&htlc)
	store.Set(types.GetHTLCKey(id), bz)
}
SetHTLC sets the given HTLC
package keeper
import (
	"bytes"
	"encoding/hex"
	"fmt"
	"time"
	tmbytes "github.com/tendermint/tendermint/libs/bytes"
	sdk "github.com/cosmos/cosmos-sdk/types"
	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
	"github.com/irisnet/irismod/modules/htlc/types"
)
func (k Keeper) CreateHTLC(
	ctx sdk.Context,
	sender sdk.AccAddress,
	to sdk.AccAddress,
	receiverOnOtherChain string,
	senderOnOtherChain string,
	amount sdk.Coins,
	hashLock tmbytes.HexBytes,
	timestamp uint64,
	timeLock uint64,
	transfer bool,
) (
	id tmbytes.HexBytes,
	err error,
) {
	id = types.GetID(sender, to, amount, hashLock)
	if k.HasHTLC(ctx, id) {
		return id, sdkerrors.Wrap(types.ErrHTLCExists, id.String())
	}
	expirationHeight := uint64(ctx.BlockHeight()) + timeLock
	var direction types.SwapDirection
	if transfer {
		if direction, err = k.createHTLT(
			ctx, sender, to, receiverOnOtherChain, senderOnOtherChain,
			amount, hashLock, timestamp, timeLock,
		); err != nil {
			return id, err
		}
	} else {
		if err = k.createHTLC(ctx, sender, amount); err != nil {
			return id, err
		}
	}
	htlc := types.NewHTLC(
		id, sender, to, receiverOnOtherChain,
		senderOnOtherChain, amount, hashLock,
		nil, timestamp, expirationHeight,
		types.Open, 0, transfer, direction,
	)
	k.SetHTLC(ctx, htlc, id)
	k.AddHTLCToExpiredQueue(ctx, htlc.ExpirationHeight, id)
	return id, nil
}
func (k Keeper) createHTLC(
	ctx sdk.Context,
	sender sdk.AccAddress,
	amount sdk.Coins,
) error {
	return k.bankKeeper.SendCoinsFromAccountToModule(ctx, sender, types.ModuleName, amount)
}
func (k Keeper) createHTLT(
	ctx sdk.Context,
	sender sdk.AccAddress,
	to sdk.AccAddress,
	receiverOnOtherChain string,
	senderOnOtherChain string,
	amount sdk.Coins,
	hashLock tmbytes.HexBytes,
	timestamp uint64,
	timeLock uint64,
) (
	types.SwapDirection,
	error,
) {
	var direction types.SwapDirection
	if len(amount) != 1 {
		return direction, sdkerrors.Wrapf(types.ErrInvalidAmount, "amount %s must contain exactly one coin", amount.String())
	}
	asset, err := k.GetAsset(ctx, amount[0].Denom)
	if err != nil {
		return direction, err
	}
	if err = k.ValidateLiveAsset(ctx, amount[0]); err != nil {
		return direction, err
	}
	if amount[0].Amount.LT(asset.MinSwapAmount) || amount[0].Amount.GT(asset.MaxSwapAmount) {
		return direction, sdkerrors.Wrapf(types.ErrInvalidAmount, "amount %s outside range [%s, %s]", amount[0].Amount, asset.MinSwapAmount.String(), asset.MaxSwapAmount)
	}
	pastTimestampLimit := ctx.BlockTime().Add(-15 * time.Minute).Unix()
	futureTimestampLimit := ctx.BlockTime().Add(30 * time.Minute).Unix()
	if timestamp < uint64(pastTimestampLimit) || timestamp >= uint64(futureTimestampLimit) {
		return direction, sdkerrors.Wrap(
			types.ErrInvalidTimestamp,
			fmt.Sprintf(
				"timestamp can neither be 15 minutes ahead of the current time, nor 30 minutes later. block time: %s, timestamp: %s",
				ctx.BlockTime().String(), time.Unix(int64(timestamp), 0).UTC().String(),
			),
		)
	}
	deputyAddress, _ := sdk.AccAddressFromBech32(asset.DeputyAddress)
	if sender.Equals(deputyAddress) {
		if to.Equals(deputyAddress) {
			return direction, sdkerrors.Wrapf(types.ErrInvalidAccount, "deputy cannot be both sender and receiver: %s", asset.DeputyAddress)
		}
		direction = types.Incoming
	} else {
		if !to.Equals(deputyAddress) {
			return direction, sdkerrors.Wrapf(types.ErrInvalidAccount, "deputy must be recipient for outgoing account: %s", to)
		}
		direction = types.Outgoing
	}
	switch direction {
	case types.Incoming:
		recipientAcc := k.accountKeeper.GetAccount(ctx, deputyAddress)
		if recipientAcc == nil {
			acc := k.accountKeeper.NewAccountWithAddress(ctx, deputyAddress)
			k.accountKeeper.SetAccount(ctx, acc)
		}
		if err := k.IncrementIncomingAssetSupply(ctx, amount[0]); err != nil {
			return direction, err
		}
	case types.Outgoing:
		if timeLock < asset.MinBlockLock || timeLock > asset.MaxBlockLock {
			return direction, sdkerrors.Wrapf(types.ErrInvalidTimeLock, "time lock %d outside range [%d, %d]", timeLock, asset.MinBlockLock, asset.MaxBlockLock)
		}
		if amount[0].Amount.LT(asset.FixedFee.Add(asset.MinSwapAmount)) {
			return direction, sdkerrors.Wrapf(
				types.ErrInsufficientAmount,
				"amount %s is less than fixed fee %s add min swap amount %s",
				amount[0].String(), asset.FixedFee.String(), asset.MinSwapAmount.String(),
			)
		}
		if err := k.IncrementOutgoingAssetSupply(ctx, amount[0]); err != nil {
			return direction, err
		}
		if err := k.bankKeeper.SendCoinsFromAccountToModule(ctx, sender, types.ModuleName, amount); err != nil {
			return direction, err
		}
	default:
		return direction, sdkerrors.Wrapf(types.ErrInvalidDirection, direction.String())
	}
	return direction, nil
}
func (k Keeper) ClaimHTLC(
	ctx sdk.Context,
	id tmbytes.HexBytes,
	secret tmbytes.HexBytes,
) (
	string,
	bool,
	types.SwapDirection,
	error,
) {
	htlc, found := k.GetHTLC(ctx, id)
	if !found {
		return "", false, types.None, sdkerrors.Wrap(types.ErrUnknownHTLC, id.String())
	}
	if htlc.State != types.Open {
		return "", false, types.None, sdkerrors.Wrap(types.ErrHTLCNotOpen, id.String())
	}
	hashLock, _ := hex.DecodeString(htlc.HashLock)
	if !bytes.Equal(types.GetHashLock(secret, htlc.Timestamp), hashLock) {
		return "", false, types.None, sdkerrors.Wrap(types.ErrInvalidSecret, secret.String())
	}
	to, err := sdk.AccAddressFromBech32(htlc.To)
	if err != nil {
		return "", false, types.None, err
	}
	if htlc.Transfer {
		if err := k.claimHTLT(ctx, htlc); err != nil {
			return "", false, types.None, err
		}
	} else {
		if err := k.claimHTLC(ctx, htlc.Amount, to); err != nil {
			return "", false, types.None, err
		}
	}
	htlc.Secret = secret.String()
	htlc.State = types.Completed
	htlc.ClosedBlock = uint64(ctx.BlockHeight())
	k.SetHTLC(ctx, htlc, id)
	k.DeleteHTLCFromExpiredQueue(ctx, htlc.ExpirationHeight, id)
	return htlc.HashLock, htlc.Transfer, htlc.Direction, nil
}
func (k Keeper) claimHTLC(ctx sdk.Context, amount sdk.Coins, to sdk.AccAddress) error {
	return k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, to, amount)
}
func (k Keeper) claimHTLT(ctx sdk.Context, htlc types.HTLC) error {
	switch htlc.Direction {
	case types.Incoming:
		if err := k.DecrementIncomingAssetSupply(ctx, htlc.Amount[0]); err != nil {
			return err
		}
		if err := k.IncrementCurrentAssetSupply(ctx, htlc.Amount[0]); err != nil {
			return err
		}
		if err := k.bankKeeper.MintCoins(ctx, types.ModuleName, htlc.Amount); err != nil {
			return err
		}
		toAddr, _ := sdk.AccAddressFromBech32(htlc.To)
		if err := k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, toAddr, htlc.Amount); err != nil {
			return err
		}
	case types.Outgoing:
		if err := k.DecrementOutgoingAssetSupply(ctx, htlc.Amount[0]); err != nil {
			return err
		}
		if err := k.DecrementCurrentAssetSupply(ctx, htlc.Amount[0]); err != nil {
			return err
		}
		if err := k.bankKeeper.BurnCoins(ctx, types.ModuleName, htlc.Amount); err != nil {
			return err
		}
	default:
		return sdkerrors.Wrapf(types.ErrInvalidDirection, htlc.Direction.String())
	}
	return nil
}
func (k Keeper) RefundHTLC(ctx sdk.Context, h types.HTLC, id tmbytes.HexBytes) error {
	sender, err := sdk.AccAddressFromBech32(h.Sender)
	if err != nil {
		return err
	}
	if h.Transfer {
		if err := k.refundHTLT(ctx, h.Direction, sender, h.Amount); err != nil {
			return err
		}
	} else {
		if err := k.refundHTLC(ctx, sender, h.Amount); err != nil {
			return err
		}
	}
	h.State = types.Refunded
	h.ClosedBlock = uint64(ctx.BlockHeight())
	k.SetHTLC(ctx, h, id)
	return nil
}
func (k Keeper) refundHTLC(ctx sdk.Context, sender sdk.AccAddress, amount sdk.Coins) error {
	return k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, sender, amount)
}
func (k Keeper) refundHTLT(ctx sdk.Context, direction types.SwapDirection, sender sdk.AccAddress, amount sdk.Coins) error {
	switch direction {
	case types.Incoming:
		if err := k.DecrementIncomingAssetSupply(ctx, amount[0]); err != nil {
			return err
		}
	case types.Outgoing:
		if err := k.DecrementOutgoingAssetSupply(ctx, amount[0]); err != nil {
			return err
		}
		if err := k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, sender, amount); err != nil {
			return err
		}
	default:
		return fmt.Errorf("invalid direction")
	}
	return nil
}
func (k Keeper) HasHTLC(ctx sdk.Context, id tmbytes.HexBytes) bool {
	store := ctx.KVStore(k.storeKey)
	return store.Has(types.GetHTLCKey(id))
}