diff --git a/precompiles/crosschain/contract_test.go b/precompiles/crosschain/contract_test.go index 11b4c8ef..276c90f6 100644 --- a/precompiles/crosschain/contract_test.go +++ b/precompiles/crosschain/contract_test.go @@ -116,20 +116,24 @@ func (suite *CrosschainPrecompileTestSuite) GetBridgeToken(baseDenom string) erc func (suite *CrosschainPrecompileTestSuite) AddBridgeToken(symbolOrAddr string, isNativeCoin bool, isIBC ...bool) { keeper := suite.App.Erc20Keeper - var erc20Token erc20types.ERC20Token - var err error + var baseDenom string isNative := false - if isNativeCoin || symbolOrAddr == fxtypes.DefaultSymbol { - erc20Token, err = keeper.RegisterNativeCoin(suite.Ctx, symbolOrAddr, symbolOrAddr, 18) + if symbolOrAddr == fxtypes.LegacyFXDenom { + baseDenom = fxtypes.FXDenom + } else if isNativeCoin || symbolOrAddr == fxtypes.DefaultSymbol { + erc20Token, err := keeper.RegisterNativeCoin(suite.Ctx, symbolOrAddr, symbolOrAddr, 18) + suite.Require().NoError(err) + baseDenom = erc20Token.Denom } else { isNative = true - erc20Token, err = keeper.RegisterNativeERC20(suite.Ctx, common.HexToAddress(symbolOrAddr)) + erc20Token, err := keeper.RegisterNativeERC20(suite.Ctx, common.HexToAddress(symbolOrAddr)) + suite.Require().NoError(err) + baseDenom = erc20Token.Denom } if len(isIBC) > 0 && isIBC[0] { isNative = true } - suite.Require().NoError(err) - err = keeper.AddBridgeToken(suite.Ctx, erc20Token.Denom, suite.chainName, helpers.GenExternalAddr(suite.chainName), isNative) + err := keeper.AddBridgeToken(suite.Ctx, baseDenom, suite.chainName, helpers.GenExternalAddr(suite.chainName), isNative) suite.Require().NoError(err) } diff --git a/precompiles/crosschain/execute_claim.go b/precompiles/crosschain/execute_claim.go index 395e4037..ddbc225a 100644 --- a/precompiles/crosschain/execute_claim.go +++ b/precompiles/crosschain/execute_claim.go @@ -7,6 +7,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" + ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" fxcontract "github.com/pundiai/fx-core/v8/contract" @@ -93,6 +94,17 @@ func (m ExecuteClaimABI) NewExecuteClaimEvent(sender common.Address, eventNonce return evmtypes.PackTopicData(m.Event, []common.Hash{sender.Hash()}, eventNonce, dstChain, errReason) } +func (m ExecuteClaimABI) UnpackEvent(log *ethtypes.Log) (*fxcontract.ICrosschainExecuteClaimEvent, error) { + if log == nil { + return nil, errors.New("log is nil") + } + filterer, err := fxcontract.NewICrosschainFilterer(common.Address{}, nil) + if err != nil { + return nil, err + } + return filterer.ParseExecuteClaimEvent(*log) +} + func (m ExecuteClaimABI) PackInput(args fxcontract.ExecuteClaimArgs) ([]byte, error) { arguments, err := m.Method.Inputs.Pack(args.Chain, args.EventNonce) if err != nil { diff --git a/precompiles/crosschain/execute_claim_test.go b/precompiles/crosschain/execute_claim_test.go index dbdc11d9..733e67f6 100644 --- a/precompiles/crosschain/execute_claim_test.go +++ b/precompiles/crosschain/execute_claim_test.go @@ -2,14 +2,24 @@ package crosschain_test import ( "encoding/hex" + "fmt" "math/big" + "strings" "testing" + sdkmath "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/pundiai/fx-core/v8/contract" "github.com/pundiai/fx-core/v8/precompiles/crosschain" + "github.com/pundiai/fx-core/v8/testutil/helpers" + fxtypes "github.com/pundiai/fx-core/v8/types" + crosschaintypes "github.com/pundiai/fx-core/v8/x/crosschain/types" + erc20types "github.com/pundiai/fx-core/v8/x/erc20/types" ethtypes "github.com/pundiai/fx-core/v8/x/eth/types" ) @@ -33,3 +43,150 @@ func TestExecuteClaimMethod_PackInput(t *testing.T) { expected := "4ac3bdc30000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000036574680000000000000000000000000000000000000000000000000000000000" assert.Equal(t, expected, hex.EncodeToString(input)) } + +func (suite *CrosschainPrecompileTestSuite) TestContract_ExecuteClaim_SendToFx() { + testCases := []struct { + name string + malleate func() erc20types.BridgeToken + amount sdkmath.Int + erc20Balance *big.Int + moduleAmount sdkmath.Int + baseDenomAmount sdkmath.Int + logsLen int + }{ + { + name: "native coin", + malleate: func() erc20types.BridgeToken { + symbol := helpers.NewRandSymbol() + suite.AddBridgeToken(symbol, true) + + bridgeToken := suite.GetBridgeToken(strings.ToLower(symbol)) + return bridgeToken + }, + amount: sdkmath.NewInt(100), + erc20Balance: big.NewInt(100), + moduleAmount: sdkmath.NewInt(100), + baseDenomAmount: sdkmath.NewInt(0), + logsLen: 2, + }, + { + name: "native erc20", + malleate: func() erc20types.BridgeToken { + symbol := helpers.NewRandSymbol() + + erc20TokenAddr := suite.erc20TokenSuite.DeployERC20Token(suite.Ctx, symbol) + suite.AddBridgeToken(erc20TokenAddr.String(), false) + + // eth module lock some tokens + bridgeToken := suite.GetBridgeToken(strings.ToLower(symbol)) + suite.MintTokenToModule(suite.chainName, sdk.NewCoin(bridgeToken.BridgeDenom(), sdkmath.NewInt(100))) + + // erc20 module lock some tokens + suite.MintTokenToModule(crosschaintypes.ModuleName, sdk.NewCoin(bridgeToken.Denom, sdkmath.NewInt(100))) + + erc20ModuelAddr := common.BytesToAddress(authtypes.NewModuleAddress(erc20types.ModuleName)) + suite.erc20TokenSuite.Mint(suite.Ctx, suite.signer.Address(), erc20ModuelAddr, big.NewInt(100)) + return bridgeToken + }, + amount: sdkmath.NewInt(100), + erc20Balance: big.NewInt(100), + moduleAmount: sdkmath.NewInt(0), + baseDenomAmount: sdkmath.NewInt(0), + logsLen: 2, + }, + { + name: "original token", + malleate: func() erc20types.BridgeToken { + suite.AddBridgeToken(fxtypes.DefaultSymbol, false) + + // eth module lock some tokens + bridgeToken := suite.GetBridgeToken(fxtypes.DefaultDenom) + suite.MintTokenToModule(ethtypes.ModuleName, sdk.NewCoin(bridgeToken.BridgeDenom(), sdkmath.NewInt(100))) + + return bridgeToken + }, + amount: sdkmath.NewInt(100), + erc20Balance: big.NewInt(0), + moduleAmount: sdkmath.NewInt(0), + baseDenomAmount: sdkmath.NewInt(100), + logsLen: 1, + }, + { + name: "legacy FX token", + malleate: func() erc20types.BridgeToken { + suite.AddBridgeToken(fxtypes.LegacyFXDenom, false) + suite.AddBridgeToken(fxtypes.DefaultSymbol, false) + + // eth module lock some tokens + bridgeToken := suite.GetBridgeToken(fxtypes.DefaultDenom) + suite.MintTokenToModule(ethtypes.ModuleName, sdk.NewCoin(bridgeToken.BridgeDenom(), sdkmath.NewInt(1))) + + return suite.GetBridgeToken(fxtypes.FXDenom) + }, + amount: sdkmath.NewInt(100), + erc20Balance: big.NewInt(0), + moduleAmount: sdkmath.NewInt(0), + baseDenomAmount: sdkmath.NewInt(1), + logsLen: 1, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.name), func() { + bridgeToken := tc.malleate() + + receiver := helpers.GenAccAddress() + claim := &crosschaintypes.MsgSendToFxClaim{ + EventNonce: 1, + BlockHeight: 100, + TokenContract: bridgeToken.Contract, + Amount: tc.amount, + Sender: helpers.GenExternalAddr(suite.chainName), + Receiver: receiver.String(), + BridgerAddress: helpers.GenAccAddress().String(), + ChainName: suite.chainName, + } + keeper := suite.App.CrosschainKeepers.GetKeeper(suite.chainName) + err := keeper.SavePendingExecuteClaim(suite.Ctx, claim) + suite.Require().NoError(err) + + txResponse := suite.ExecuteClaim(suite.Ctx, suite.signer.Address(), + contract.ExecuteClaimArgs{Chain: suite.chainName, EventNonce: big.NewInt(int64(claim.EventNonce))}, + ) + suite.NotNil(txResponse) + suite.Len(txResponse.Logs, tc.logsLen) + event, err := crosschain.NewExecuteClaimABI(). + UnpackEvent(txResponse.Logs[len(txResponse.Logs)-1].ToEthereum()) + suite.Require().NoError(err) + suite.Equal(suite.GetSender(), event.Sender) + suite.Equal(claim.EventNonce, event.EventNonce.Uint64()) + suite.Equal(claim.ChainName, event.Chain) + suite.Empty(event.ErrReason) + + // crosschain module balance + bridgeTokenBalance := sdk.NewCoin(bridgeToken.BridgeDenom(), tc.moduleAmount) + suite.AssertBalance(authtypes.NewModuleAddress(crosschaintypes.ModuleName), bridgeTokenBalance) + + // erc20 module balance + baseDenomBalance := sdk.NewCoin(bridgeToken.Denom, tc.moduleAmount) + suite.AssertBalance(authtypes.NewModuleAddress(erc20types.ModuleName), baseDenomBalance) + + baseDenom := bridgeToken.Denom + if baseDenom == fxtypes.FXDenom { // swap base denom to apundiai denom + bridgeTokenBalance = sdk.NewCoin(bridgeToken.BridgeDenom(), tc.amount) + suite.AssertBalance(authtypes.NewModuleAddress(suite.chainName), bridgeTokenBalance) + baseDenom = fxtypes.DefaultDenom + } + + // receiver balance + baseDenomBalance = sdk.NewCoin(baseDenom, tc.baseDenomAmount) + suite.AssertBalance(receiver, baseDenomBalance) + + erc20Contract := suite.GetERC20Token(baseDenom).GetERC20Contract() + suite.erc20TokenSuite.WithContract(erc20Contract) + + balance := suite.erc20TokenSuite.BalanceOf(suite.Ctx, common.BytesToAddress(receiver)) + suite.Equal(tc.erc20Balance.String(), balance.String()) + }) + } +} diff --git a/x/crosschain/keeper/keeper_test.go b/x/crosschain/keeper/keeper_test.go index caec5714..973a3575 100644 --- a/x/crosschain/keeper/keeper_test.go +++ b/x/crosschain/keeper/keeper_test.go @@ -31,7 +31,6 @@ type KeeperMockSuite struct { chainName string queryClient types.QueryClient - // msgServer types.MsgServer crosschainKeeper crosschainkeeper.Keeper stakingKeeper *mock.MockStakingKeeper @@ -106,7 +105,6 @@ func (s *KeeperMockSuite) SetupTest() { queryHelper := baseapp.NewQueryServerTestHelper(s.ctx, myApp.InterfaceRegistry()) types.RegisterQueryServer(queryHelper, crosschainRouterKeeper) s.queryClient = types.NewQueryClient(queryHelper) - // s.msgServer = crosschainkeeper.NewMsgServerRouterImpl(crosschainRouterKeeper) } func (s *KeeperMockSuite) SetupSubTest() { diff --git a/x/crosschain/keeper/many_to_one.go b/x/crosschain/keeper/many_to_one.go index 170459b0..226c18ee 100644 --- a/x/crosschain/keeper/many_to_one.go +++ b/x/crosschain/keeper/many_to_one.go @@ -34,7 +34,7 @@ func (k Keeper) SwapBridgeToken(ctx context.Context, holder sdk.AccAddress, brid return defBridgeToken, swapAmount, nil } // transfer defaultDenom from eth module to holder - err = k.bankKeeper.SendCoinsFromModuleToAccount(ctx, ethtypes.ModuleName, holder, sdk.NewCoins(sdk.NewCoin(defBridgeToken.Denom, swapAmount))) + err = k.bankKeeper.SendCoinsFromModuleToAccount(ctx, ethtypes.ModuleName, holder, sdk.NewCoins(sdk.NewCoin(defBridgeToken.BridgeDenom(), swapAmount))) return defBridgeToken, swapAmount, err }