Skip to content

Commit 00e0e8f

Browse files
codchenCysonXingchen Liao
authored
Automatically unregister failed contracts in Dex EndBlock (#739)
* Auto unregister contract if it fails execution * rebase * fix test * Remove refund from failed contract unregisteration --------- Co-authored-by: Cyson <[email protected]> Co-authored-by: Xingchen Liao <[email protected]>
1 parent 2887556 commit 00e0e8f

File tree

4 files changed

+44
-16
lines changed

4 files changed

+44
-16
lines changed

x/dex/keeper/contract.go

+23
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55

66
"github.com/cosmos/cosmos-sdk/store/prefix"
77
sdk "github.com/cosmos/cosmos-sdk/types"
8+
appparams "github.com/sei-protocol/sei-chain/app/params"
89
"github.com/sei-protocol/sei-chain/x/dex/types"
910
)
1011

@@ -113,3 +114,25 @@ func (k Keeper) GetRentsForContracts(ctx sdk.Context, contractAddrs []string) ma
113114
}
114115
return res
115116
}
117+
118+
// Unregistrate and refund the creator
119+
func (k Keeper) DoUnregisterContractWithRefund(ctx sdk.Context, contract types.ContractInfoV2) error {
120+
k.DoUnregisterContract(ctx, contract)
121+
creatorAddr, _ := sdk.AccAddressFromBech32(contract.Creator)
122+
if err := k.BankKeeper.SendCoins(ctx, k.AccountKeeper.GetModuleAddress(types.ModuleName), creatorAddr, sdk.NewCoins(sdk.NewCoin(appparams.BaseCoinUnit, sdk.NewInt(int64(contract.RentBalance))))); err != nil {
123+
return err
124+
}
125+
return nil
126+
}
127+
128+
// Contract unregistration will remove all orderbook data stored for the contract
129+
func (k Keeper) DoUnregisterContract(ctx sdk.Context, contract types.ContractInfoV2) {
130+
k.DeleteContract(ctx, contract.ContractAddr)
131+
k.RemoveAllLongBooksForContract(ctx, contract.ContractAddr)
132+
k.RemoveAllShortBooksForContract(ctx, contract.ContractAddr)
133+
k.RemoveAllPricesForContract(ctx, contract.ContractAddr)
134+
k.DeleteMatchResultState(ctx, contract.ContractAddr)
135+
k.DeleteNextOrderID(ctx, contract.ContractAddr)
136+
k.DeleteAllRegisteredPairsForContract(ctx, contract.ContractAddr)
137+
k.RemoveAllTriggeredOrders(ctx, contract.ContractAddr)
138+
}

x/dex/keeper/msgserver/msg_server_unregister_contract.go

+1-12
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import (
66

77
sdk "github.com/cosmos/cosmos-sdk/types"
88
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
9-
appparams "github.com/sei-protocol/sei-chain/app/params"
109
"github.com/sei-protocol/sei-chain/x/dex/types"
1110
)
1211

@@ -25,19 +24,9 @@ func (k msgServer) UnregisterContract(goCtx context.Context, msg *types.MsgUnreg
2524
if contract.Creator != msg.Creator {
2625
return nil, sdkerrors.ErrUnauthorized
2726
}
28-
// refund remaining rent to the creator
29-
creatorAddr, _ := sdk.AccAddressFromBech32(contract.Creator)
30-
if err := k.BankKeeper.SendCoins(ctx, k.AccountKeeper.GetModuleAddress(types.ModuleName), creatorAddr, sdk.NewCoins(sdk.NewCoin(appparams.BaseCoinUnit, sdk.NewInt(int64(contract.RentBalance))))); err != nil {
27+
if err := k.DoUnregisterContractWithRefund(ctx, contract); err != nil {
3128
return nil, err
3229
}
33-
k.DeleteContract(ctx, msg.ContractAddr)
34-
k.RemoveAllLongBooksForContract(ctx, msg.ContractAddr)
35-
k.RemoveAllShortBooksForContract(ctx, msg.ContractAddr)
36-
k.RemoveAllPricesForContract(ctx, msg.ContractAddr)
37-
k.DeleteMatchResultState(ctx, msg.ContractAddr)
38-
k.DeleteNextOrderID(ctx, msg.ContractAddr)
39-
k.DeleteAllRegisteredPairsForContract(ctx, msg.ContractAddr)
40-
k.RemoveAllTriggeredOrders(ctx, msg.ContractAddr)
4130

4231
ctx.EventManager().EmitEvent(sdk.NewEvent(
4332
types.EventTypeUnregisterContract,

x/dex/module.go

+10
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,7 @@ func (am AppModule) EndBlock(ctx sdk.Context, _ abci.RequestEndBlock) (ret []abc
273273
defer dexutils.GetMemState(ctx.Context()).Clear(ctx)
274274

275275
validContractsInfo := am.getAllContractInfo(ctx)
276+
validContractsInfoAtBeginning := validContractsInfo
276277
// Each iteration is atomic. If an iteration finishes without any error, it will return,
277278
// otherwise it will rollback any state change, filter out contracts that cause the error,
278279
// and proceed to the next iteration. The loop is guaranteed to finish since
@@ -294,6 +295,15 @@ func (am AppModule) EndBlock(ctx sdk.Context, _ abci.RequestEndBlock) (ret []abc
294295
break
295296
}
296297
}
298+
validContractAddrs := map[string]struct{}{}
299+
for _, c := range validContractsInfo {
300+
validContractAddrs[c.ContractAddr] = struct{}{}
301+
}
302+
for _, c := range validContractsInfoAtBeginning {
303+
if _, ok := validContractAddrs[c.ContractAddr]; !ok {
304+
am.keeper.DoUnregisterContract(ctx, c)
305+
}
306+
}
297307

298308
return []abci.ValidatorUpdate{}
299309
}

x/dex/module_test.go

+10-4
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,9 @@ func TestEndBlockPartialRollback(t *testing.T) {
458458
// No state change should've been persisted for bad contract
459459
matchResult, _ := dexkeeper.GetMatchResultState(ctx, keepertest.TestContract)
460460
require.Equal(t, &types.MatchResult{}, matchResult)
461+
// bad contract should be unregistered
462+
_, err = dexkeeper.GetContract(ctx, keepertest.TestContract)
463+
require.Equal(t, types.ErrContractNotExists, err)
461464
// state change should've been persisted for good contract
462465
matchResult, _ = dexkeeper.GetMatchResultState(ctx, contractAddr.String())
463466
require.Equal(t, 1, len(matchResult.Orders))
@@ -615,14 +618,17 @@ func TestEndBlockRollbackWithRentCharge(t *testing.T) {
615618
)
616619

617620
ctx = ctx.WithBlockHeight(1)
621+
creatorBalanceBefore := bankkeeper.GetBalance(ctx, testAccount, "usei")
618622
testApp.EndBlocker(ctx, abci.RequestEndBlock{})
619623
// no state change should've been persisted for good contract because it should've run out of gas
620624
matchResult, _ := dexkeeper.GetMatchResultState(ctx, contractAddr.String())
621625
require.Equal(t, 0, len(matchResult.Orders))
622-
// rent should still be charged even if the contract failed
623-
contract, err := dexkeeper.GetContract(ctx, contractAddr.String())
624-
require.Nil(t, err)
625-
require.Zero(t, contract.RentBalance)
626+
// rent should still be charged even if the contract failed, so no rent should be sent to the creator after
627+
// auto unregister
628+
_, err = dexkeeper.GetContract(ctx, contractAddr.String())
629+
require.NotNil(t, err) // auto-unregistered
630+
creatorBalanceAfter := bankkeeper.GetBalance(ctx, testAccount, "usei")
631+
require.Equal(t, creatorBalanceBefore, creatorBalanceAfter)
626632
}
627633

628634
func TestEndBlockContractWithoutPair(t *testing.T) {

0 commit comments

Comments
 (0)