Skip to content

Commit

Permalink
feat: support bridge FX token by value (#483)
Browse files Browse the repository at this point in the history
  • Loading branch information
zakir-code authored May 15, 2024
1 parent f4de51a commit 5ce799b
Show file tree
Hide file tree
Showing 6 changed files with 116 additions and 8 deletions.
2 changes: 1 addition & 1 deletion x/crosschain/keeper/bridge_call_out.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
"github.com/functionx/fx-core/v7/x/crosschain/types"
)

func (k Keeper) bridgeCallCoinsToERC20Token(ctx sdk.Context, sender sdk.AccAddress, coins sdk.Coins) ([]types.ERC20Token, error) {
func (k Keeper) BridgeCallCoinsToERC20Token(ctx sdk.Context, sender sdk.AccAddress, coins sdk.Coins) ([]types.ERC20Token, error) {
tokens := make([]types.ERC20Token, 0, len(coins))
for _, coin := range coins {
targetCoin, err := k.erc20Keeper.ConvertDenomToTarget(ctx, sender, coin, fxtypes.ParseFxTarget(k.moduleName))
Expand Down
94 changes: 94 additions & 0 deletions x/crosschain/keeper/bridge_call_out_test.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package keeper_test

import (
"reflect"

sdk "github.com/cosmos/cosmos-sdk/types"
"go.uber.org/mock/gomock"

"github.com/functionx/fx-core/v7/testutil/helpers"
fxtypes "github.com/functionx/fx-core/v7/types"
"github.com/functionx/fx-core/v7/x/crosschain/types"
)

Expand Down Expand Up @@ -57,3 +61,93 @@ func (s *KeeperTestSuite) TestKeeper_BridgeCallResultHandler() {
})
}
}

func (s *KeeperTestSuite) TestKeeper_BridgeCallCoinsToERC20Token() {
type Data struct {
sender sdk.AccAddress
coin sdk.Coin
}
tests := []struct {
name string
data Data
mock func(data Data) (want types.ERC20Token)
wantErr bool
}{
{
name: "success - FX",
data: Data{
sender: helpers.GenAccAddress(),
coin: sdk.NewCoin(fxtypes.DefaultDenom, sdk.NewInt(1)),
},
mock: func(data Data) (want types.ERC20Token) {
s.erc20Keeper.EXPECT().ConvertDenomToTarget(gomock.Any(), data.sender, data.coin, fxtypes.ParseFxTarget(s.moduleName)).Return(data.coin, nil).Times(1)
s.erc20Keeper.EXPECT().IsOriginOrConvertedDenom(gomock.Any(), data.coin.Denom).Return(true).Times(1)
s.bankKeeper.EXPECT().SendCoinsFromAccountToModule(gomock.Any(), data.sender, s.moduleName, sdk.NewCoins(data.coin)).Return(nil).Times(1)

return types.ERC20Token{
Contract: s.wfxTokenAddr,
Amount: data.coin.Amount,
}
},
wantErr: false,
},
{
name: "success - bridge denom",
data: Data{
sender: helpers.GenAccAddress(),
coin: sdk.NewCoin(types.NewBridgeDenom(s.moduleName, helpers.GenExternalAddr(s.moduleName)), sdk.NewInt(1)),
},
mock: func(data Data) (want types.ERC20Token) {
contract := data.coin.Denom[len(s.moduleName):]
s.AddBridgeToken(contract)

s.erc20Keeper.EXPECT().ConvertDenomToTarget(gomock.Any(), data.sender, data.coin, fxtypes.ParseFxTarget(s.moduleName)).Return(data.coin, nil).Times(1)
s.erc20Keeper.EXPECT().IsOriginOrConvertedDenom(gomock.Any(), data.coin.Denom).Return(false).Times(1)
s.bankKeeper.EXPECT().SendCoinsFromAccountToModule(gomock.Any(), data.sender, s.moduleName, sdk.NewCoins(data.coin)).Return(nil).Times(1)
s.bankKeeper.EXPECT().BurnCoins(gomock.Any(), s.moduleName, sdk.NewCoins(data.coin)).Return(nil).Times(1)

return types.ERC20Token{
Contract: contract,
Amount: data.coin.Amount,
}
},
wantErr: false,
},
{
name: "success - base denom",
data: Data{
sender: helpers.GenAccAddress(),
coin: sdk.NewCoin("usdt", sdk.NewInt(1)),
},
mock: func(data Data) (want types.ERC20Token) {
contract := helpers.GenHexAddress().String()
bridgeToken := s.AddBridgeToken(contract)

targetCoin := sdk.NewCoin(bridgeToken.Denom, data.coin.Amount)
s.erc20Keeper.EXPECT().ConvertDenomToTarget(gomock.Any(), data.sender, data.coin, fxtypes.ParseFxTarget(s.moduleName)).Return(targetCoin, nil).Times(1)
s.erc20Keeper.EXPECT().IsOriginOrConvertedDenom(gomock.Any(), targetCoin.Denom).Return(false).Times(1)
s.bankKeeper.EXPECT().SendCoinsFromAccountToModule(gomock.Any(), data.sender, s.moduleName, sdk.NewCoins(targetCoin)).Return(nil).Times(1)
s.bankKeeper.EXPECT().BurnCoins(gomock.Any(), s.moduleName, sdk.NewCoins(targetCoin)).Return(nil).Times(1)

return types.ERC20Token{
Contract: contract,
Amount: data.coin.Amount,
}
},
wantErr: false,
},
}
for _, tt := range tests {
s.Run(tt.name, func() {
want := tt.mock(tt.data)
got, err := s.crosschainKeeper.BridgeCallCoinsToERC20Token(s.ctx, tt.data.sender, sdk.NewCoins(tt.data.coin))
if (err != nil) != tt.wantErr {
s.T().Errorf("BridgeCallCoinsToERC20Token() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, []types.ERC20Token{want}) {
s.T().Errorf("BridgeCallCoinsToERC20Token() got = %v, want %v", got, want)
}
})
}
}
2 changes: 1 addition & 1 deletion x/crosschain/keeper/hook.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func (k Keeper) PrecompileBridgeCall(
data []byte,
memo []byte,
) (eventNonce uint64, err error) {
tokens, err := k.bridgeCallCoinsToERC20Token(ctx, sender.Bytes(), coins)
tokens, err := k.BridgeCallCoinsToERC20Token(ctx, sender.Bytes(), coins)
if err != nil {
return 0, err
}
Expand Down
14 changes: 10 additions & 4 deletions x/crosschain/keeper/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (

"github.com/functionx/fx-core/v7/testutil"
"github.com/functionx/fx-core/v7/testutil/helpers"
fxtypes "github.com/functionx/fx-core/v7/types"
arbitrumtypes "github.com/functionx/fx-core/v7/x/arbitrum/types"
avalanchetypes "github.com/functionx/fx-core/v7/x/avalanche/types"
bsctypes "github.com/functionx/fx-core/v7/x/bsc/types"
Expand All @@ -32,8 +33,9 @@ import (
type KeeperTestSuite struct {
suite.Suite

ctx sdk.Context
moduleName string
ctx sdk.Context
moduleName string
wfxTokenAddr string

queryClient types.QueryClient
msgServer types.MsgServer
Expand Down Expand Up @@ -66,7 +68,10 @@ func TestKeeperTestSuite(t *testing.T) {
}...)
}
for _, moduleName := range subModules {
suite.Run(t, &KeeperTestSuite{moduleName: moduleName})
suite.Run(t, &KeeperTestSuite{
moduleName: moduleName,
wfxTokenAddr: helpers.GenHexAddress().String(),
})
}
}

Expand Down Expand Up @@ -128,9 +133,10 @@ func (s *KeeperTestSuite) SetupTest() {
s.queryClient = types.NewQueryClient(queryHelper)
s.msgServer = crosschainkeeper.NewMsgServerRouterImpl(crosschainRouterKeeper)

// set params
params := s.CrossChainParams()
s.NoError(s.crosschainKeeper.SetParams(s.ctx, &params))

s.crosschainKeeper.AddBridgeToken(s.ctx, s.wfxTokenAddr, fxtypes.DefaultDenom)
}

func (s *KeeperTestSuite) CrossChainParams() types.Params {
Expand Down
2 changes: 1 addition & 1 deletion x/crosschain/keeper/msg_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -593,7 +593,7 @@ func (s MsgServer) BridgeCall(c context.Context, msg *types.MsgBridgeCall) (*typ
}

ctx := sdk.UnwrapSDKContext(c)
tokens, err := s.bridgeCallCoinsToERC20Token(ctx, sender, msg.Coins)
tokens, err := s.BridgeCallCoinsToERC20Token(ctx, sender, msg.Coins)
if err != nil {
return nil, err
}
Expand Down
10 changes: 9 additions & 1 deletion x/evm/precompiles/crosschain/bridge_call.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package crosschain

import (
"errors"
"math/big"

sdkmath "cosmossdk.io/math"
sdk "github.com/cosmos/cosmos-sdk/types"
Expand Down Expand Up @@ -29,7 +30,6 @@ func (c *Contract) BridgeCall(ctx sdk.Context, evm *vm.EVM, contract *vm.Contrac
}
sender := contract.Caller()

// NOTE: current only support erc20 token
coins := sdk.NewCoins()
for i, token := range args.Tokens {
coin, err := c.handlerERC20Token(ctx, evm, sender, token, args.Amounts[i])
Expand All @@ -38,6 +38,14 @@ func (c *Contract) BridgeCall(ctx sdk.Context, evm *vm.EVM, contract *vm.Contrac
}
coins = coins.Add(coin)
}
value := contract.Value()
if value.Cmp(big.NewInt(0)) == 1 {
totalCoin, err := c.handlerOriginToken(ctx, evm, sender, value)
if err != nil {
return nil, err
}
coins = coins.Add(totalCoin)
}

eventNonce, err := route.PrecompileBridgeCall(
ctx,
Expand Down

0 comments on commit 5ce799b

Please sign in to comment.