github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/libs/ibc-go/modules/apps/29-fee/keeper/escrow_test.go (about)

     1  package keeper_test
     2  
     3  import (
     4  	"fmt"
     5  
     6  	sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types"
     7  	"github.com/fibonacci-chain/fbc/libs/ibc-go/modules/apps/29-fee/types"
     8  	transfertypes "github.com/fibonacci-chain/fbc/libs/ibc-go/modules/apps/transfer/types"
     9  	channeltypes "github.com/fibonacci-chain/fbc/libs/ibc-go/modules/core/04-channel/types"
    10  	"github.com/fibonacci-chain/fbc/libs/ibc-go/testing/mock"
    11  	"github.com/fibonacci-chain/fbc/libs/tendermint/crypto/secp256k1"
    12  )
    13  
    14  func (suite *KeeperTestSuite) TestDistributeFee() {
    15  	var (
    16  		forwardRelayer    string
    17  		forwardRelayerBal sdk.Coin
    18  		reverseRelayer    sdk.AccAddress
    19  		reverseRelayerBal sdk.Coin
    20  		refundAcc         sdk.AccAddress
    21  		refundAccBal      sdk.Coin
    22  		packetFee         types.PacketFee
    23  		packetFees        []types.PacketFee
    24  		fee               types.Fee
    25  	)
    26  	testCases := []struct {
    27  		name      string
    28  		malleate  func()
    29  		expResult func()
    30  	}{
    31  		{
    32  			"success",
    33  			func() {
    34  				packetFee = types.NewPacketFee(fee, refundAcc.String(), []string{})
    35  				packetFees = []types.PacketFee{packetFee, packetFee}
    36  			},
    37  			func() {
    38  				// check if fees has been deleted
    39  				packetID := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, 1)
    40  				suite.Require().False(suite.chainA.GetSimApp().IBCFeeKeeper.HasFeesInEscrow(suite.chainA.GetContext(), packetID))
    41  
    42  				// check if the reverse relayer is paid
    43  				expectedReverseAccBal := reverseRelayerBal.Add(defaultAckFee.ToCoins()[0]).Add(defaultAckFee.ToCoins()[0])
    44  				balance := suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), reverseRelayer, sdk.DefaultBondDenom)
    45  				suite.Require().Equal(expectedReverseAccBal, balance)
    46  
    47  				// check if the forward relayer is paid
    48  				forward, err := sdk.AccAddressFromBech32(forwardRelayer)
    49  				suite.Require().NoError(err)
    50  
    51  				expectedForwardAccBal := forwardRelayerBal.Add(defaultRecvFee.ToCoins()[0]).Add(defaultRecvFee.ToCoins()[0])
    52  				balance = suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), forward, sdk.DefaultBondDenom)
    53  				suite.Require().Equal(expectedForwardAccBal, balance)
    54  
    55  				// check if the refund acc has been refunded the timeoutFee
    56  				expectedRefundAccBal := refundAccBal.Add(defaultTimeoutFee.ToCoins()[0].Add(defaultTimeoutFee.ToCoins()[0]))
    57  				balance = suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), refundAcc, sdk.DefaultBondDenom)
    58  				suite.Require().Equal(expectedRefundAccBal, balance)
    59  
    60  				// check the module acc wallet is now empty
    61  				balance = suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), suite.chainA.GetSimApp().IBCFeeKeeper.GetFeeModuleAddress(), sdk.DefaultBondDenom)
    62  				suite.Require().Equal(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(0)), balance)
    63  			},
    64  		},
    65  		{
    66  			"success: refund account is module account",
    67  			func() {
    68  				refundAcc = suite.chainA.GetSimApp().SupplyKeeper.GetModuleAddress(mock.ModuleName)
    69  
    70  				packetFee = types.NewPacketFee(fee, refundAcc.String(), []string{})
    71  				packetFees = []types.PacketFee{packetFee, packetFee}
    72  
    73  				// fund mock account
    74  				err := suite.chainA.GetSimApp().SupplyKeeper.SendCoinsFromAccountToModule(suite.chainA.GetContext(), suite.chainA.SenderAccount().GetAddress(), mock.ModuleName, packetFee.Fee.Total().Add(packetFee.Fee.Total()...).ToCoins())
    75  				balance := suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), refundAcc, sdk.DefaultBondDenom)
    76  				fmt.Println(balance.String())
    77  				suite.Require().NoError(err)
    78  			},
    79  			func() {
    80  				// check if the refund acc has been refunded the timeoutFee
    81  				expectedRefundAccBal := refundAccBal.Add(defaultTimeoutFee.ToCoins()[0]).Add(defaultTimeoutFee.ToCoins()[0])
    82  				balance := suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), refundAcc, sdk.DefaultBondDenom)
    83  				fmt.Println(expectedRefundAccBal.String())
    84  				fmt.Println(balance.String())
    85  				suite.Require().Equal(expectedRefundAccBal, balance)
    86  			},
    87  		},
    88  		{
    89  			"escrow account out of balance, fee module becomes locked - no distribution", func() {
    90  				packetFee = types.NewPacketFee(fee, refundAcc.String(), []string{})
    91  				packetFees = []types.PacketFee{packetFee, packetFee}
    92  
    93  				// pass in an extra packet fee
    94  				packetFees = append(packetFees, packetFee)
    95  			},
    96  			func() {
    97  				packetID := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, 1)
    98  
    99  				suite.Require().True(suite.chainA.GetSimApp().IBCFeeKeeper.IsLocked(suite.chainA.GetContext()))
   100  				suite.Require().True(suite.chainA.GetSimApp().IBCFeeKeeper.HasFeesInEscrow(suite.chainA.GetContext(), packetID))
   101  
   102  				// check if the module acc contains all the fees
   103  				expectedModuleAccBal := packetFee.Fee.Total().Add(packetFee.Fee.Total()...)
   104  				balance := suite.chainA.GetSimApp().BankKeeper.GetAllBalances(suite.chainA.GetContext(), suite.chainA.GetSimApp().IBCFeeKeeper.GetFeeModuleAddress())
   105  				suite.Require().Equal(expectedModuleAccBal.ToCoins(), balance)
   106  			},
   107  		},
   108  		{
   109  			"invalid forward address",
   110  			func() {
   111  				packetFee = types.NewPacketFee(fee, refundAcc.String(), []string{})
   112  				packetFees = []types.PacketFee{packetFee, packetFee}
   113  
   114  				forwardRelayer = "invalid address"
   115  			},
   116  			func() {
   117  				// check if the refund acc has been refunded the timeoutFee & recvFee
   118  				expectedRefundAccBal := refundAccBal.Add(defaultTimeoutFee.ToCoins()[0]).Add(defaultRecvFee.ToCoins()[0]).Add(defaultTimeoutFee.ToCoins()[0]).Add(defaultRecvFee.ToCoins()[0])
   119  				balance := suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), refundAcc, sdk.DefaultBondDenom)
   120  				suite.Require().Equal(expectedRefundAccBal, balance)
   121  			},
   122  		},
   123  		{
   124  			"invalid forward address: blocked address",
   125  			func() {
   126  				packetFee = types.NewPacketFee(fee, refundAcc.String(), []string{})
   127  				packetFees = []types.PacketFee{packetFee, packetFee}
   128  
   129  				forwardRelayer = suite.chainA.GetSimApp().SupplyKeeper.GetModuleAccount(suite.chainA.GetContext(), transfertypes.ModuleName).GetAddress().String()
   130  			},
   131  			func() {
   132  				// check if the refund acc has been refunded the timeoutFee & recvFee
   133  				expectedRefundAccBal := refundAccBal.Add(defaultTimeoutFee.ToCoins()[0]).Add(defaultRecvFee.ToCoins()[0]).Add(defaultTimeoutFee.ToCoins()[0]).Add(defaultRecvFee.ToCoins()[0])
   134  				balance := suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), refundAcc, sdk.DefaultBondDenom)
   135  				suite.Require().Equal(expectedRefundAccBal, balance)
   136  			},
   137  		},
   138  		{
   139  			"invalid receiver address: ack fee returned to sender",
   140  			func() {
   141  				packetFee = types.NewPacketFee(fee, refundAcc.String(), []string{})
   142  				packetFees = []types.PacketFee{packetFee, packetFee}
   143  
   144  				reverseRelayer = suite.chainA.GetSimApp().SupplyKeeper.GetModuleAccount(suite.chainA.GetContext(), transfertypes.ModuleName).GetAddress()
   145  			},
   146  			func() {
   147  				// check if the refund acc has been refunded the timeoutFee & ackFee
   148  				expectedRefundAccBal := refundAccBal.Add(defaultTimeoutFee.ToCoins()[0]).Add(defaultAckFee.ToCoins()[0]).Add(defaultTimeoutFee.ToCoins()[0]).Add(defaultAckFee.ToCoins()[0])
   149  				balance := suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), refundAcc, sdk.DefaultBondDenom)
   150  				suite.Require().Equal(expectedRefundAccBal, balance)
   151  			},
   152  		},
   153  		{
   154  			"invalid refund address: no-op, timeout fee remains in escrow",
   155  			func() {
   156  				packetFee = types.NewPacketFee(fee, refundAcc.String(), []string{})
   157  				packetFees = []types.PacketFee{packetFee, packetFee}
   158  
   159  				packetFees[0].RefundAddress = suite.chainA.GetSimApp().SupplyKeeper.GetModuleAccount(suite.chainA.GetContext(), transfertypes.ModuleName).GetAddress().String()
   160  				packetFees[1].RefundAddress = suite.chainA.GetSimApp().SupplyKeeper.GetModuleAccount(suite.chainA.GetContext(), transfertypes.ModuleName).GetAddress().String()
   161  			},
   162  			func() {
   163  				// check if the module acc contains the timeoutFee
   164  				expectedModuleAccBal := sdk.NewCoinAdapter(sdk.DefaultIbcWei, defaultTimeoutFee.Add(defaultTimeoutFee...).AmountOf(sdk.DefaultIbcWei))
   165  				balance := suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), suite.chainA.GetSimApp().IBCFeeKeeper.GetFeeModuleAddress(), sdk.DefaultBondDenom)
   166  				fmt.Println(expectedModuleAccBal.String(), balance.String())
   167  				suite.Require().Equal(expectedModuleAccBal.ToCoin(), balance)
   168  			},
   169  		},
   170  	}
   171  
   172  	for _, tc := range testCases {
   173  		tc := tc
   174  
   175  		suite.Run(tc.name, func() {
   176  			suite.SetupTest()                   // reset
   177  			suite.coordinator.Setup(suite.path) // setup channel
   178  
   179  			// setup accounts
   180  			forwardRelayer = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()).String()
   181  			reverseRelayer = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address())
   182  			refundAcc = suite.chainA.SenderAccount().GetAddress()
   183  
   184  			packetID := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, 1)
   185  			fee = types.NewFee(defaultRecvFee, defaultAckFee, defaultTimeoutFee)
   186  
   187  			tc.malleate()
   188  
   189  			// escrow the packet fees & store the fees in state
   190  			suite.chainA.GetSimApp().IBCFeeKeeper.SetFeesInEscrow(suite.chainA.GetContext(), packetID, types.NewPacketFees(packetFees))
   191  			err := suite.chainA.GetSimApp().SupplyKeeper.SendCoinsFromAccountToModule(suite.chainA.GetContext(), refundAcc, types.ModuleName, packetFee.Fee.Total().Add(packetFee.Fee.Total()...).ToCoins())
   192  			suite.Require().NoError(err)
   193  
   194  			// fetch the account balances before fee distribution (forward, reverse, refund)
   195  			forwardAccAddress, _ := sdk.AccAddressFromBech32(forwardRelayer)
   196  			forwardRelayerBal = suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), forwardAccAddress, sdk.DefaultBondDenom)
   197  			reverseRelayerBal = suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), reverseRelayer, sdk.DefaultBondDenom)
   198  			refundAccBal = suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), refundAcc, sdk.DefaultBondDenom)
   199  
   200  			suite.chainA.GetSimApp().IBCFeeKeeper.DistributePacketFeesOnAcknowledgement(suite.chainA.GetContext(), forwardRelayer, reverseRelayer, packetFees, packetID)
   201  			tc.expResult()
   202  		})
   203  	}
   204  }
   205  
   206  func (suite *KeeperTestSuite) TestDistributePacketFeesOnTimeout() {
   207  	var (
   208  		timeoutRelayer    sdk.AccAddress
   209  		timeoutRelayerBal sdk.Coin
   210  		refundAcc         sdk.AccAddress
   211  		refundAccBal      sdk.Coin
   212  		packetFee         types.PacketFee
   213  		packetFees        []types.PacketFee
   214  	)
   215  	testCases := []struct {
   216  		name      string
   217  		malleate  func()
   218  		expResult func()
   219  	}{
   220  		{
   221  			"success",
   222  			func() {},
   223  			func() {
   224  				// check if the timeout relayer is paid
   225  				expectedTimeoutAccBal := timeoutRelayerBal.Add(defaultTimeoutFee.ToCoins()[0]).Add(defaultTimeoutFee.ToCoins()[0])
   226  				balance := suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), timeoutRelayer, sdk.DefaultBondDenom)
   227  				suite.Require().Equal(expectedTimeoutAccBal, balance)
   228  
   229  				// check if the refund acc has been refunded the recv/ack fees
   230  				expectedRefundAccBal := refundAccBal.Add(defaultAckFee.ToCoins()[0]).Add(defaultAckFee.ToCoins()[0]).Add(defaultRecvFee.ToCoins()[0]).Add(defaultRecvFee.ToCoins()[0])
   231  				balance = suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), refundAcc, sdk.DefaultBondDenom)
   232  				suite.Require().Equal(expectedRefundAccBal, balance)
   233  
   234  				// check the module acc wallet is now empty
   235  				balance = suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), suite.chainA.GetSimApp().IBCFeeKeeper.GetFeeModuleAddress(), sdk.DefaultBondDenom)
   236  				suite.Require().Equal(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(0)), balance)
   237  			},
   238  		},
   239  		{
   240  			"escrow account out of balance, fee module becomes locked - no distribution", func() {
   241  				// pass in an extra packet fee
   242  				packetFees = append(packetFees, packetFee)
   243  			},
   244  			func() {
   245  				packetID := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, 1)
   246  
   247  				suite.Require().True(suite.chainA.GetSimApp().IBCFeeKeeper.IsLocked(suite.chainA.GetContext()))
   248  				suite.Require().True(suite.chainA.GetSimApp().IBCFeeKeeper.HasFeesInEscrow(suite.chainA.GetContext(), packetID))
   249  
   250  				// check if the module acc contains all the fees
   251  				expectedModuleAccBal := packetFee.Fee.Total().Add(packetFee.Fee.Total()...)
   252  				balance := suite.chainA.GetSimApp().BankKeeper.GetAllBalances(suite.chainA.GetContext(), suite.chainA.GetSimApp().IBCFeeKeeper.GetFeeModuleAddress())
   253  				fmt.Println(expectedModuleAccBal.String(), balance.String())
   254  				suite.Require().Equal(expectedModuleAccBal.ToCoins(), balance)
   255  			},
   256  		},
   257  		{
   258  			"invalid timeout relayer address: timeout fee returned to sender",
   259  			func() {
   260  				timeoutRelayer = suite.chainA.GetSimApp().SupplyKeeper.GetModuleAccount(suite.chainA.GetContext(), transfertypes.ModuleName).GetAddress()
   261  			},
   262  			func() {
   263  				// check if the refund acc has been refunded the all the fees
   264  				expectedRefundAccBal := sdk.Coins{refundAccBal}.Add(packetFee.Fee.Total().ToCoins()...).Add(packetFee.Fee.Total().ToCoins()...)[0]
   265  				balance := suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), refundAcc, sdk.DefaultBondDenom)
   266  				suite.Require().Equal(expectedRefundAccBal, balance)
   267  			},
   268  		},
   269  		{
   270  			"invalid refund address: no-op, recv and ack fees remain in escrow",
   271  			func() {
   272  				packetFees[0].RefundAddress = suite.chainA.GetSimApp().SupplyKeeper.GetModuleAccount(suite.chainA.GetContext(), transfertypes.ModuleName).GetAddress().String()
   273  				packetFees[1].RefundAddress = suite.chainA.GetSimApp().SupplyKeeper.GetModuleAccount(suite.chainA.GetContext(), transfertypes.ModuleName).GetAddress().String()
   274  			},
   275  			func() {
   276  				// check if the module acc contains the timeoutFee
   277  				expectedModuleAccBal := sdk.NewCoinAdapter(sdk.DefaultIbcWei, defaultRecvFee.Add(defaultRecvFee[0]).Add(defaultAckFee[0]).Add(defaultAckFee[0]).AmountOf(sdk.DefaultIbcWei))
   278  				balance := suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), suite.chainA.GetSimApp().IBCFeeKeeper.GetFeeModuleAddress(), sdk.DefaultBondDenom)
   279  				suite.Require().Equal(expectedModuleAccBal.ToCoin(), balance)
   280  			},
   281  		},
   282  	}
   283  
   284  	for _, tc := range testCases {
   285  		tc := tc
   286  
   287  		suite.Run(tc.name, func() {
   288  			suite.SetupTest()                   // reset
   289  			suite.coordinator.Setup(suite.path) // setup channel
   290  
   291  			// setup accounts
   292  			timeoutRelayer = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address())
   293  			refundAcc = suite.chainA.SenderAccount().GetAddress()
   294  
   295  			packetID := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, 1)
   296  			fee := types.NewFee(defaultRecvFee, defaultAckFee, defaultTimeoutFee)
   297  
   298  			// escrow the packet fees & store the fees in state
   299  			packetFee = types.NewPacketFee(fee, refundAcc.String(), []string{})
   300  			packetFees = []types.PacketFee{packetFee, packetFee}
   301  
   302  			suite.chainA.GetSimApp().IBCFeeKeeper.SetFeesInEscrow(suite.chainA.GetContext(), packetID, types.NewPacketFees(packetFees))
   303  			err := suite.chainA.GetSimApp().SupplyKeeper.SendCoinsFromAccountToModule(suite.chainA.GetContext(), refundAcc, types.ModuleName, packetFee.Fee.Total().Add(packetFee.Fee.Total()...).ToCoins())
   304  			suite.Require().NoError(err)
   305  
   306  			tc.malleate()
   307  
   308  			// fetch the account balances before fee distribution (forward, reverse, refund)
   309  			timeoutRelayerBal = suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), timeoutRelayer, sdk.DefaultBondDenom)
   310  			refundAccBal = suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), refundAcc, sdk.DefaultBondDenom)
   311  
   312  			suite.chainA.GetSimApp().IBCFeeKeeper.DistributePacketFeesOnTimeout(suite.chainA.GetContext(), timeoutRelayer, packetFees, packetID)
   313  
   314  			tc.expResult()
   315  		})
   316  	}
   317  }
   318  
   319  func (suite *KeeperTestSuite) TestRefundFeesOnChannelClosure() {
   320  	var (
   321  		expIdentifiedPacketFees     []types.IdentifiedPacketFees
   322  		expEscrowBal                sdk.Coins
   323  		expRefundBal                sdk.Coins
   324  		refundAcc                   sdk.AccAddress
   325  		fee                         types.Fee
   326  		locked                      bool
   327  		expectEscrowFeesToBeDeleted bool
   328  	)
   329  
   330  	testCases := []struct {
   331  		name     string
   332  		malleate func()
   333  		expPass  bool
   334  	}{
   335  		{
   336  			"success", func() {
   337  				for i := 1; i < 6; i++ {
   338  					// store the fee in state & update escrow account balance
   339  					packetID := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, uint64(i))
   340  					packetFees := types.NewPacketFees([]types.PacketFee{types.NewPacketFee(fee, refundAcc.String(), nil)})
   341  					identifiedPacketFees := types.NewIdentifiedPacketFees(packetID, packetFees.PacketFees)
   342  
   343  					suite.chainA.GetSimApp().IBCFeeKeeper.SetFeesInEscrow(suite.chainA.GetContext(), packetID, packetFees)
   344  
   345  					err := suite.chainA.GetSimApp().SupplyKeeper.SendCoinsFromAccountToModule(suite.chainA.GetContext(), refundAcc, types.ModuleName, fee.Total().ToCoins())
   346  					suite.Require().NoError(err)
   347  
   348  					expIdentifiedPacketFees = append(expIdentifiedPacketFees, identifiedPacketFees)
   349  				}
   350  			}, true,
   351  		},
   352  		{
   353  			"success with undistributed packet fees on a different channel", func() {
   354  				for i := 1; i < 6; i++ {
   355  					// store the fee in state & update escrow account balance
   356  					packetID := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, uint64(i))
   357  					packetFees := types.NewPacketFees([]types.PacketFee{types.NewPacketFee(fee, refundAcc.String(), nil)})
   358  					identifiedPacketFees := types.NewIdentifiedPacketFees(packetID, packetFees.PacketFees)
   359  
   360  					suite.chainA.GetSimApp().IBCFeeKeeper.SetFeesInEscrow(suite.chainA.GetContext(), packetID, packetFees)
   361  
   362  					err := suite.chainA.GetSimApp().SupplyKeeper.SendCoinsFromAccountToModule(suite.chainA.GetContext(), refundAcc, types.ModuleName, fee.Total().ToCoins())
   363  					suite.Require().NoError(err)
   364  
   365  					expIdentifiedPacketFees = append(expIdentifiedPacketFees, identifiedPacketFees)
   366  				}
   367  
   368  				// set packet fee for a different channel
   369  				packetID := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, "channel-1", uint64(1))
   370  				packetFees := types.NewPacketFees([]types.PacketFee{types.NewPacketFee(fee, refundAcc.String(), nil)})
   371  				suite.chainA.GetSimApp().IBCFeeKeeper.SetFeeEnabled(suite.chainA.GetContext(), suite.path.EndpointA.ChannelConfig.PortID, "channel-1")
   372  
   373  				suite.chainA.GetSimApp().IBCFeeKeeper.SetFeesInEscrow(suite.chainA.GetContext(), packetID, packetFees)
   374  				err := suite.chainA.GetSimApp().SupplyKeeper.SendCoinsFromAccountToModule(suite.chainA.GetContext(), refundAcc, types.ModuleName, fee.Total().ToCoins())
   375  				suite.Require().NoError(err)
   376  
   377  				expEscrowBal = fee.Total().ToCoins()
   378  				expRefundBal = expRefundBal.Sub(fee.Total().ToCoins())
   379  			}, true,
   380  		},
   381  		{
   382  			"escrow account empty, module should become locked", func() {
   383  				locked = true
   384  
   385  				// store the fee in state without updating escrow account balance
   386  				packetID := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, uint64(1))
   387  				packetFees := types.NewPacketFees([]types.PacketFee{types.NewPacketFee(fee, refundAcc.String(), nil)})
   388  				identifiedPacketFees := types.NewIdentifiedPacketFees(packetID, packetFees.PacketFees)
   389  
   390  				suite.chainA.GetSimApp().IBCFeeKeeper.SetFeesInEscrow(suite.chainA.GetContext(), packetID, packetFees)
   391  
   392  				expIdentifiedPacketFees = []types.IdentifiedPacketFees{identifiedPacketFees}
   393  			},
   394  			true,
   395  		},
   396  		{
   397  			"escrow account goes negative on second packet, module should become locked", func() {
   398  				locked = true
   399  
   400  				// store 2 fees in state
   401  				packetID1 := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, uint64(1))
   402  				packetID2 := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, uint64(2))
   403  				packetFees := types.NewPacketFees([]types.PacketFee{types.NewPacketFee(fee, refundAcc.String(), nil)})
   404  				identifiedPacketFee1 := types.NewIdentifiedPacketFees(packetID1, packetFees.PacketFees)
   405  				identifiedPacketFee2 := types.NewIdentifiedPacketFees(packetID2, packetFees.PacketFees)
   406  
   407  				suite.chainA.GetSimApp().IBCFeeKeeper.SetFeesInEscrow(suite.chainA.GetContext(), packetID1, packetFees)
   408  				suite.chainA.GetSimApp().IBCFeeKeeper.SetFeesInEscrow(suite.chainA.GetContext(), packetID2, packetFees)
   409  
   410  				// update escrow account balance for 1 fee
   411  				err := suite.chainA.GetSimApp().SupplyKeeper.SendCoinsFromAccountToModule(suite.chainA.GetContext(), refundAcc, types.ModuleName, fee.Total().ToCoins())
   412  				suite.Require().NoError(err)
   413  
   414  				expIdentifiedPacketFees = []types.IdentifiedPacketFees{identifiedPacketFee1, identifiedPacketFee2}
   415  			}, true,
   416  		},
   417  		{
   418  			"invalid refund acc address", func() {
   419  				// store the fee in state & update escrow account balance
   420  				expectEscrowFeesToBeDeleted = false
   421  				packetID := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, uint64(1))
   422  				packetFees := types.NewPacketFees([]types.PacketFee{types.NewPacketFee(fee, "invalid refund address", nil)})
   423  				identifiedPacketFees := types.NewIdentifiedPacketFees(packetID, packetFees.PacketFees)
   424  
   425  				suite.chainA.GetSimApp().IBCFeeKeeper.SetFeesInEscrow(suite.chainA.GetContext(), packetID, packetFees)
   426  
   427  				err := suite.chainA.GetSimApp().SupplyKeeper.SendCoinsFromAccountToModule(suite.chainA.GetContext(), refundAcc, types.ModuleName, fee.Total().ToCoins())
   428  				suite.Require().NoError(err)
   429  
   430  				expIdentifiedPacketFees = []types.IdentifiedPacketFees{identifiedPacketFees}
   431  
   432  				expEscrowBal = fee.Total().ToCoins()
   433  				expRefundBal = expRefundBal.Sub(fee.Total().ToCoins())
   434  			}, true,
   435  		},
   436  		{
   437  			"distributing to blocked address is skipped", func() {
   438  				expectEscrowFeesToBeDeleted = false
   439  				blockedAddr := suite.chainA.GetSimApp().SupplyKeeper.GetModuleAccount(suite.chainA.GetContext(), transfertypes.ModuleName).GetAddress().String()
   440  
   441  				// store the fee in state & update escrow account balance
   442  				packetID := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, uint64(1))
   443  				packetFees := types.NewPacketFees([]types.PacketFee{types.NewPacketFee(fee, blockedAddr, nil)})
   444  				identifiedPacketFees := types.NewIdentifiedPacketFees(packetID, packetFees.PacketFees)
   445  
   446  				suite.chainA.GetSimApp().IBCFeeKeeper.SetFeesInEscrow(suite.chainA.GetContext(), packetID, packetFees)
   447  
   448  				err := suite.chainA.GetSimApp().SupplyKeeper.SendCoinsFromAccountToModule(suite.chainA.GetContext(), refundAcc, types.ModuleName, fee.Total().ToCoins())
   449  				suite.Require().NoError(err)
   450  
   451  				expIdentifiedPacketFees = []types.IdentifiedPacketFees{identifiedPacketFees}
   452  
   453  				expEscrowBal = fee.Total().ToCoins()
   454  				expRefundBal = expRefundBal.Sub(fee.Total().ToCoins())
   455  			}, true,
   456  		},
   457  	}
   458  
   459  	for _, tc := range testCases {
   460  		tc := tc
   461  
   462  		suite.Run(tc.name, func() {
   463  			suite.SetupTest()                   // reset
   464  			suite.coordinator.Setup(suite.path) // setup channel
   465  			expIdentifiedPacketFees = []types.IdentifiedPacketFees{}
   466  			expEscrowBal = sdk.Coins{}
   467  			locked = false
   468  			expectEscrowFeesToBeDeleted = true
   469  
   470  			// setup
   471  			refundAcc = suite.chainA.SenderAccount().GetAddress()
   472  			moduleAcc := suite.chainA.GetSimApp().IBCFeeKeeper.GetFeeModuleAddress()
   473  
   474  			// expected refund balance if the refunds are successful
   475  			// NOTE: tc.malleate() should transfer from refund balance to correctly set the escrow balance
   476  			expRefundBal = suite.chainA.GetSimApp().BankKeeper.GetAllBalances(suite.chainA.GetContext(), refundAcc)
   477  
   478  			fee = types.Fee{
   479  				RecvFee:    defaultRecvFee,
   480  				AckFee:     defaultAckFee,
   481  				TimeoutFee: defaultTimeoutFee,
   482  			}
   483  
   484  			tc.malleate()
   485  
   486  			// refundAcc balance before distribution
   487  			originalRefundBal := suite.chainA.GetSimApp().BankKeeper.GetAllBalances(suite.chainA.GetContext(), refundAcc)
   488  			originalEscrowBal := suite.chainA.GetSimApp().BankKeeper.GetAllBalances(suite.chainA.GetContext(), moduleAcc)
   489  
   490  			err := suite.chainA.GetSimApp().IBCFeeKeeper.RefundFeesOnChannelClosure(suite.chainA.GetContext(), suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID)
   491  
   492  			// refundAcc balance after RefundFeesOnChannelClosure
   493  			refundBal := suite.chainA.GetSimApp().BankKeeper.GetAllBalances(suite.chainA.GetContext(), refundAcc)
   494  			escrowBal := suite.chainA.GetSimApp().BankKeeper.GetAllBalances(suite.chainA.GetContext(), moduleAcc)
   495  
   496  			if tc.expPass {
   497  				suite.Require().NoError(err)
   498  			} else {
   499  				suite.Require().Error(err)
   500  			}
   501  
   502  			suite.Require().Equal(locked, suite.chainA.GetSimApp().IBCFeeKeeper.IsLocked(suite.chainA.GetContext()))
   503  
   504  			if locked || !tc.expPass {
   505  				// refund account and escrow account balances should remain unchanged
   506  				suite.Require().Equal(originalRefundBal, refundBal)
   507  				suite.Require().Equal(originalEscrowBal, escrowBal)
   508  
   509  				// ensure none of the fees were deleted
   510  				suite.Require().Equal(expIdentifiedPacketFees, suite.chainA.GetSimApp().IBCFeeKeeper.GetIdentifiedPacketFeesForChannel(suite.chainA.GetContext(), suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID))
   511  			} else {
   512  				if escrowBal == nil {
   513  					escrowBal = sdk.Coins{}
   514  				}
   515  				suite.Require().Equal(expEscrowBal, escrowBal) // escrow balance should be empty
   516  				suite.Require().Equal(expRefundBal, refundBal) // all packets should have been refunded
   517  
   518  				// all fees in escrow should be deleted if expected for this channel
   519  				suite.Require().Equal(expectEscrowFeesToBeDeleted, len(suite.chainA.GetSimApp().IBCFeeKeeper.GetIdentifiedPacketFeesForChannel(suite.chainA.GetContext(), suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID)) == 0)
   520  			}
   521  		})
   522  	}
   523  }