Skip to content

Commit

Permalink
Merge commit from fork
Browse files Browse the repository at this point in the history
fix!: handling ConvertCoin failure using cached context
  • Loading branch information
tkkwon1998 authored Aug 7, 2024
2 parents 01587ca + 7276d65 commit 12a18d7
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 4 deletions.
9 changes: 7 additions & 2 deletions x/onboarding/keeper/ibc_callbacks.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,9 +134,14 @@ func (k Keeper) OnRecvPacket(
// the ICS20 packet data

// Use MsgConvertCoin to convert the Cosmos Coin to an ERC20
if _, err = k.erc20Keeper.ConvertCoin(ctx, convertMsg); err != nil {
// Use cached context to revert the state if the conversion fails

cacheCtx, writeCache := ctx.CacheContext()
if _, err = k.erc20Keeper.ConvertCoin(cacheCtx, convertMsg); err != nil {
logger.Error("failed to convert coins", "error", err)
return ack
convertCoin = sdk.NewCoin(transferredCoin.Denom, sdkmath.ZeroInt())
} else {
writeCache()
}

logger.Info(
Expand Down
23 changes: 21 additions & 2 deletions x/onboarding/keeper/ibc_callbacks_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,25 @@ func (suite *KeeperTestSuite) TestOnRecvPacket() {
sdk.NewCoin(uusdcIbcdenom, sdkmath.NewInt(20998399)),
sdkmath.NewInt(0),
},
{
"convert fail due to evm call failure - cached context is discarded (escrow occured during convert coin is reverted)",
func() {
cantoChannel = "channel-0"
transferAmount = sdkmath.NewIntWithDecimal(25, 6)
transfer := transfertypes.NewFungibleTokenPacketData(denom, transferAmount.String(), secpAddrCosmos, ethsecpAddrcanto, "")
bz := transfertypes.ModuleCdc.MustMarshalJSON(&transfer)
packet = channeltypes.NewPacket(bz, 100, transfertypes.PortID, sourceChannel, transfertypes.PortID, cantoChannel, timeoutHeight, 0)
mockErc20Keeper := NewMockErc20Keeper(suite.app.Erc20Keeper, suite.app.BankKeeper)
suite.app.OnboardingKeeper.SetErc20Keeper(mockErc20Keeper)
mockErc20Keeper.On("ConvertCoin", mock.Anything, mock.Anything).Return(nil, fmt.Errorf("Call EVM Failed"))

},
true,
sdk.NewCoins(sdk.NewCoin("acanto", sdkmath.ZeroInt())),
sdk.NewCoin("acanto", sdkmath.NewIntWithDecimal(4, 18)),
sdk.NewCoin(uusdcIbcdenom, sdkmath.NewInt(20998399)),
sdkmath.ZeroInt(),
},
}
for _, tc := range testCases {
suite.Run(fmt.Sprintf("Case %s", tc.name), func() {
Expand Down Expand Up @@ -381,8 +400,6 @@ func (suite *KeeperTestSuite) TestOnRecvPacket() {
suite.Require().NotNil(usdtPair)
suite.app.Erc20Keeper.SetTokenPair(suite.ctx, *usdtPair)

tc.malleate()

// Set Denom Trace
denomTrace := transfertypes.DenomTrace{
Path: path,
Expand Down Expand Up @@ -416,6 +433,8 @@ func (suite *KeeperTestSuite) TestOnRecvPacket() {
suite.Require().True(found)
suite.app.OnboardingKeeper = keeper.NewKeeper(sp, suite.app.AccountKeeper, suite.app.BankKeeper, suite.app.IBCKeeper.ChannelKeeper, mockTransferKeeper, suite.app.CoinswapKeeper, suite.app.Erc20Keeper, authtypes.NewModuleAddress(govtypes.ModuleName).String())

tc.malleate()

// Fund receiver account with canto, IBC vouchers
testutil.FundAccount(suite.app.BankKeeper, suite.ctx, ethsecpAddr, tc.receiverBalance)
// Fund receiver account with the transferred amount
Expand Down
4 changes: 4 additions & 0 deletions x/onboarding/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,7 @@ func (k Keeper) WriteAcknowledgement(ctx sdk.Context, channelCap *capabilitytype
func (k Keeper) GetAppVersion(ctx sdk.Context, portID, channelID string) (string, bool) {
return k.ics4Wrapper.GetAppVersion(ctx, portID, channelID)
}

func (k *Keeper) SetErc20Keeper(ek types.Erc20Keeper) {
k.erc20Keeper = ek
}
58 changes: 58 additions & 0 deletions x/onboarding/keeper/utils_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
package keeper_test

import (
"context"
"math/big"

errorsmod "cosmossdk.io/errors"
erc20keeper "github.com/Canto-Network/Canto/v8/x/erc20/keeper"
erc20types "github.com/Canto-Network/Canto/v8/x/erc20/types"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
evmtypes "github.com/evmos/ethermint/x/evm/types"
"github.com/stretchr/testify/mock"

tmbytes "github.com/cometbft/cometbft/libs/bytes"
Expand Down Expand Up @@ -49,3 +58,52 @@ func (m *MockTransferKeeper) SendTransfer(

return args.Error(0)
}

type MockErc20Keeper struct {
mock.Mock
erc20keeper erc20keeper.Keeper
bankKeeper bankkeeper.Keeper
}

func NewMockErc20Keeper(ek erc20keeper.Keeper, bk bankkeeper.Keeper) *MockErc20Keeper {
return &MockErc20Keeper{erc20keeper: ek, bankKeeper: bk}
}

func (m *MockErc20Keeper) ConvertCoin(
goCtx context.Context,
msg *erc20types.MsgConvertCoin,
) (*erc20types.MsgConvertCoinResponse, error) {
sender, _ := sdk.AccAddressFromBech32(msg.Sender)

coins := sdk.Coins{msg.Coin}
err := m.bankKeeper.SendCoinsFromAccountToModule(goCtx, sender, types.ModuleName, coins)
if err != nil {
return nil, errorsmod.Wrap(err, "failed to escrow coins")
}
argsMock := m.Called(goCtx, msg)
return nil, argsMock.Error(1)
}

func (m *MockErc20Keeper) GetTokenPairID(ctx sdk.Context, token string) []byte {
return m.erc20keeper.GetTokenPairID(ctx, token)
}

func (m *MockErc20Keeper) GetTokenPair(ctx sdk.Context, id []byte) (erc20types.TokenPair, bool) {
return m.erc20keeper.GetTokenPair(ctx, id)
}

func (m *MockErc20Keeper) BalanceOf(ctx sdk.Context, abi abi.ABI, contract, account common.Address) *big.Int {
return m.erc20keeper.BalanceOf(ctx, abi, contract, account)
}

func (m *MockErc20Keeper) CallEVM(
ctx sdk.Context,
abi abi.ABI,
from, contract common.Address,
commit bool,
method string,
args ...interface{},
) (*evmtypes.MsgEthereumTxResponse, error) {
argsMock := m.Called(ctx, abi, from, contract, commit, method, args)
return nil, argsMock.Error(1)
}

0 comments on commit 12a18d7

Please sign in to comment.