Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: bridge call transfer coin #243

Merged
merged 1 commit into from
Mar 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 139 additions & 0 deletions x/crosschain/keeper/bridge_call_erc20.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
package keeper

import (
"math/big"
"strconv"

errorsmod "cosmossdk.io/errors"
sdkmath "cosmossdk.io/math"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/ethereum/go-ethereum/common"

fxtypes "github.com/functionx/fx-core/v7/types"
"github.com/functionx/fx-core/v7/x/crosschain/types"
erc20types "github.com/functionx/fx-core/v7/x/erc20/types"
)

func (k Keeper) bridgeCallERC20Handler(
ctx sdk.Context,
asset, sender, to, receiver []byte,
dstChainID, message string,
value sdkmath.Int,
gasLimit, eventNonce uint64,
) error {
tokens, amounts, err := types.UnpackERC20Asset(asset)
if err != nil {
return errorsmod.Wrap(types.ErrInvalid, "asset erc20")
}
targetCoins, err := k.bridgeCallTransferToSender(ctx, sender, tokens, amounts)
if err != nil {
return err
}

switch dstChainID {
case types.FxcoreChainID:
if err = k.bridgeCallTransferToReceiver(ctx, sender, receiver, targetCoins); err != nil {
return err
}
var toAddrPtr *common.Address
if len(to) > 0 {
toAddr := common.BytesToAddress(to)
toAddrPtr = &toAddr
}
if len(message) > 0 || toAddrPtr != nil {
senderAddr := common.BytesToAddress(sender)
k.bridgeCallEvmHandler(ctx, senderAddr, toAddrPtr, message, value, gasLimit, eventNonce)
}
default:
// not support chain, refund
}
// todo refund asset

return nil
}

func (k Keeper) bridgeCallTransferToReceiver(ctx sdk.Context, sender sdk.AccAddress, receiver []byte, targetCoins sdk.Coins) error {
for _, coin := range targetCoins {
if coin.Denom == fxtypes.DefaultDenom {
continue
}
if _, err := k.erc20Keeper.ConvertCoin(sdk.WrapSDKContext(ctx), &erc20types.MsgConvertCoin{
Coin: coin,
Receiver: common.BytesToAddress(receiver).String(),
Sender: sender.String(),
}); err != nil {
return err
}
}
return nil
}

func (k Keeper) bridgeCallTransferToSender(ctx sdk.Context, receiver sdk.AccAddress, tokens [][]byte, amounts []*big.Int) (sdk.Coins, error) {
tokens, amounts = types.MergeDuplicationERC20(tokens, amounts)

mintCoins := sdk.NewCoins()
unlockCoins := sdk.NewCoins()
for i := 0; i < len(tokens); i++ {
bridgeToken := k.GetBridgeTokenDenom(ctx, fxtypes.AddressToStr(tokens[i], k.moduleName))
if bridgeToken == nil {
return nil, errorsmod.Wrap(types.ErrInvalid, "bridge token is not exist")
}
amount := sdkmath.NewIntFromBigInt(amounts[i])
if !amount.IsPositive() {
continue
}
coin := sdk.NewCoin(bridgeToken.Denom, amount)
isOriginOrConverted := k.erc20Keeper.IsOriginOrConvertedDenom(ctx, bridgeToken.Denom)
if !isOriginOrConverted {
mintCoins = mintCoins.Add(coin)
}
unlockCoins = unlockCoins.Add(coin)
}
if mintCoins.IsAllPositive() {
if err := k.bankKeeper.MintCoins(ctx, k.moduleName, mintCoins); err != nil {
return nil, errorsmod.Wrapf(err, "mint vouchers coins")
}
}
if unlockCoins.IsAllPositive() {
if err := k.bankKeeper.SendCoinsFromModuleToAccount(ctx, k.moduleName, receiver, unlockCoins); err != nil {
return nil, errorsmod.Wrap(err, "transfer vouchers")
}
}

targetCoins := sdk.NewCoins()
for _, coin := range unlockCoins {
targetCoin, err := k.erc20Keeper.ConvertDenomToTarget(ctx, receiver, coin, fxtypes.ParseFxTarget(fxtypes.ERC20Target))
if err != nil {
return nil, errorsmod.Wrap(err, "convert to target coin")
}
targetCoins = targetCoins.Add(targetCoin)
}
return targetCoins, nil
}

func (k Keeper) bridgeCallEvmHandler(
ctx sdk.Context,
sender common.Address,
to *common.Address,
message string, value sdkmath.Int,
gasLimit, eventNonce uint64,
) {
callErr, callResult := "", false
defer func() {
attrs := []sdk.Attribute{sdk.NewAttribute(types.AttributeKeyEvmCallResult, strconv.FormatBool(callResult))}
if len(callErr) > 0 {
attrs = append(attrs, sdk.NewAttribute(types.AttributeKeyEvmCallError, callErr))
}
ctx.EventManager().EmitEvents(sdk.Events{sdk.NewEvent(types.EventTypeBridgeCallEvm, attrs...)})
}()

txResp, err := k.evmKeeper.CallEVM(ctx, sender, to, value.BigInt(), gasLimit, types.MustDecodeMessage(message), true)
if err != nil {
k.Logger(ctx).Error("bridge call evm error", "nonce", eventNonce, "error", err.Error())
callErr = err.Error()
return
}

callResult = !txResp.Failed()
callErr = txResp.VmError
}
137 changes: 4 additions & 133 deletions x/crosschain/keeper/relay_transfer.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,9 @@ package keeper
import (
"encoding/hex"
"fmt"
"math/big"
"strconv"
"strings"
"time"

errorsmod "cosmossdk.io/errors"
sdkmath "cosmossdk.io/math"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/bech32"
Expand Down Expand Up @@ -43,25 +40,16 @@ func (k Keeper) RelayTransferHandler(ctx sdk.Context, eventNonce uint64, targetH
// transfer to evm
cacheCtx, commit := ctx.CacheContext()
receiverHex := common.BytesToAddress(receiver.Bytes())
if err := k.transferErc20Handler(cacheCtx, eventNonce, receiver, receiverHex, coin, true); err != nil {
if err := k.erc20Keeper.TransferAfter(cacheCtx, receiver, receiverHex.String(), coin, sdk.NewCoin(coin.Denom, sdkmath.ZeroInt()), false); err != nil {
k.Logger(cacheCtx).Error("transfer convert denom failed", "error", err.Error())
return err
}
commit()
}
return nil
}

func (k Keeper) transferErc20Handler(ctx sdk.Context, eventNonce uint64, sender sdk.AccAddress, receiver common.Address, coin sdk.Coin, isEmitEvent bool) error {
if err := k.erc20Keeper.TransferAfter(ctx, sender, receiver.String(), coin, sdk.NewCoin(coin.Denom, sdkmath.ZeroInt()), false); err != nil {
k.Logger(ctx).Error("transfer convert denom failed", "error", err.Error())
return err
}
if isEmitEvent {
ctx.EventManager().EmitEvent(sdk.NewEvent(
cacheCtx.EventManager().EmitEvent(sdk.NewEvent(
types.EventTypeEvmTransfer,
sdk.NewAttribute(sdk.AttributeKeyModule, k.moduleName),
sdk.NewAttribute(types.AttributeKeyEventNonce, fmt.Sprint(eventNonce)),
))
commit()
}
return nil
}
Expand Down Expand Up @@ -108,120 +96,3 @@ func (k Keeper) transferIBCHandler(ctx sdk.Context, eventNonce uint64, receive s
))
return err
}

func (k Keeper) bridgeCallERC20Handler(
ctx sdk.Context,
asset, sender, to, receiver []byte,
dstChainID, message string,
value sdkmath.Int,
gasLimit, eventNonce uint64,
) error {
tokens, amounts, err := types.UnpackERC20Asset(asset)
if err != nil {
return errorsmod.Wrap(types.ErrInvalid, "asset erc20")
}
senderAddr := common.BytesToAddress(sender)
targetCoins, err := k.bridgeCallTargetCoinsHandler(ctx, senderAddr, tokens, amounts)
if err != nil {
return err
}

switch dstChainID {
case types.FxcoreChainID:
// convert coin to erc20
isEmitEvent := true
for _, coin := range targetCoins {
// not convert FX
if coin.Denom == fxtypes.DefaultDenom {
continue
}
if err = k.transferErc20Handler(ctx, eventNonce, senderAddr.Bytes(), common.BytesToAddress(receiver), coin, isEmitEvent); err != nil {
return err
}
isEmitEvent = false
}
var toAddrPtr *common.Address
if len(to) > 0 {
toAddr := common.BytesToAddress(to)
toAddrPtr = &toAddr
}
if len(message) > 0 || toAddrPtr != nil {
k.bridgeCallEvmHandler(ctx, senderAddr, toAddrPtr, message, value, gasLimit, eventNonce)
}
default:
// not support chain, refund
}
// todo refund asset

return nil
}

func (k Keeper) bridgeCallTargetCoinsHandler(ctx sdk.Context, receiver common.Address, tokens [][]byte, amounts []*big.Int) (sdk.Coins, error) {
tokens, amounts = types.MergeDuplicationERC20(tokens, amounts)

mintCoins := sdk.NewCoins()
unlockCoins := sdk.NewCoins()
for i := 0; i < len(tokens); i++ {
bridgeToken := k.GetBridgeTokenDenom(ctx, fxtypes.AddressToStr(tokens[i], k.moduleName))
if bridgeToken == nil {
return nil, errorsmod.Wrap(types.ErrInvalid, "bridge token is not exist")
}
amount := sdkmath.NewIntFromBigInt(amounts[i])
if !amount.IsPositive() {
continue
}
coin := sdk.NewCoin(bridgeToken.Denom, amount)
isOriginOrConverted := k.erc20Keeper.IsOriginOrConvertedDenom(ctx, bridgeToken.Denom)
if !isOriginOrConverted {
mintCoins = mintCoins.Add(coin)
}
unlockCoins = unlockCoins.Add(coin)
}
if mintCoins.IsAllPositive() {
if err := k.bankKeeper.MintCoins(ctx, k.moduleName, mintCoins); err != nil {
return nil, errorsmod.Wrapf(err, "mint vouchers coins")
}
}
if unlockCoins.IsAllPositive() {
if err := k.bankKeeper.SendCoinsFromModuleToAccount(ctx, k.moduleName, receiver.Bytes(), unlockCoins); err != nil {
return nil, errorsmod.Wrap(err, "transfer vouchers")
}
}

targetCoins := sdk.NewCoins()
for _, coin := range unlockCoins {
targetCoin, err := k.erc20Keeper.ConvertDenomToTarget(ctx, receiver.Bytes(), coin, fxtypes.ParseFxTarget(fxtypes.ERC20Target))
if err != nil {
return nil, errorsmod.Wrap(err, "convert to target coin")
}
targetCoins = targetCoins.Add(targetCoin)
}
return targetCoins, nil
}

func (k Keeper) bridgeCallEvmHandler(
ctx sdk.Context,
sender common.Address,
to *common.Address,
message string, value sdkmath.Int,
gasLimit, eventNonce uint64,
) {
callErr, callResult := "", false
defer func() {
attrs := []sdk.Attribute{sdk.NewAttribute(types.AttributeKeyEvmCallResult, strconv.FormatBool(callResult))}
if len(callErr) > 0 {
attrs = append(attrs, sdk.NewAttribute(types.AttributeKeyEvmCallError, callErr))
}
ctx.EventManager().EmitEvents(sdk.Events{sdk.NewEvent(types.EventTypeBridgeCallEvm, attrs...)})
}()

txResp, err := k.evmKeeper.CallEVM(ctx, sender, to, value.BigInt(), gasLimit, types.MustDecodeMessage(message), true)
if err != nil {
k.Logger(ctx).Error("bridge call evm error", "nonce", eventNonce, "error", err.Error())
callErr = err.Error()
return
}

callResult = !txResp.Failed()
callErr = txResp.VmError
}
2 changes: 2 additions & 0 deletions x/crosschain/types/expected_keepers.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/evmos/ethermint/x/evm/types"

fxtypes "github.com/functionx/fx-core/v7/types"
erc20types "github.com/functionx/fx-core/v7/x/erc20/types"
)

type StakingKeeper interface {
Expand Down Expand Up @@ -57,6 +58,7 @@ type BankKeeper interface {

type Erc20Keeper interface {
TransferAfter(ctx sdk.Context, sender sdk.AccAddress, receive string, coin, fee sdk.Coin, _ bool) error
ConvertCoin(goCtx context.Context, msg *erc20types.MsgConvertCoin) (*erc20types.MsgConvertCoinResponse, error)
ConvertDenomToTarget(ctx sdk.Context, from sdk.AccAddress, coin sdk.Coin, fxTarget fxtypes.FxTarget) (sdk.Coin, error)
HookOutgoingRefund(ctx sdk.Context, moduleName string, txID uint64, sender sdk.AccAddress, totalCoin sdk.Coin) error
SetOutgoingTransferRelation(ctx sdk.Context, moduleName string, txID uint64)
Expand Down
Loading