Skip to content

Commit 8eda438

Browse files
author
beer-1
committed
fix(move): route dex swaps through stdaddr
1 parent b5de972 commit 8eda438

4 files changed

Lines changed: 111 additions & 31 deletions

File tree

x/move/keeper/clamm_test.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"cosmossdk.io/math"
88

99
sdk "github.com/cosmos/cosmos-sdk/types"
10+
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
1011

1112
vmtypes "github.com/initia-labs/movevm/types"
1213

@@ -263,6 +264,31 @@ func Test_CLAMM_SwapToBase(t *testing.T) {
263264
require.True(t, before.AmountOf(baseDenom).Add(math.NewInt(1_000)).Equal(after.AmountOf(baseDenom)))
264265
}
265266

267+
func Test_CLAMM_SwapToBase_BlockedRecipient(t *testing.T) {
268+
ctx, input := createDefaultTestInput(t)
269+
270+
baseDenom := bondDenom
271+
quoteDenom := "uusdc"
272+
metadataQuote, err := types.MetadataAddressFromDenom(quoteDenom)
273+
require.NoError(t, err)
274+
metadataLP := createCLAMMPool(t, ctx, input, baseDenom, quoteDenom, 1, 0)
275+
276+
clammKeeper := keeper.NewCLAMMKeeper(&input.MoveKeeper, cafeAddr)
277+
278+
quoteOfferCoin := sdk.NewInt64Coin(quoteDenom, 1_000)
279+
feeCollectorAddr := authtypes.NewModuleAddress(authtypes.FeeCollectorName)
280+
input.Faucet.Fund(ctx, feeCollectorAddr, quoteOfferCoin)
281+
282+
err = clammKeeper.SwapToBase(
283+
ctx,
284+
types.ConvertSDKAddressToVMAddress(feeCollectorAddr),
285+
metadataLP,
286+
metadataQuote,
287+
quoteOfferCoin.Amount,
288+
)
289+
require.Error(t, err)
290+
}
291+
266292
func Test_CLAMM_SwapToBase_InvalidQuote(t *testing.T) {
267293
ctx, input := createDefaultTestInput(t)
268294

x/move/keeper/dex.go

Lines changed: 42 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,9 @@ func (k DexKeeper) SwapToBase(
276276
if ok, err := k.StableSwapKeeper().HasPool(ctx, metadataLP); err != nil {
277277
return err
278278
} else if ok {
279-
return k.StableSwapKeeper().SwapToBase(ctx, vmAddr, metadataLP, metadataQuote, quoteCoin.Amount)
279+
return k.swapToBaseViaStdAddr(ctx, addr, quoteCoin, func() error {
280+
return k.StableSwapKeeper().SwapToBase(ctx, vmtypes.StdAddress, metadataLP, metadataQuote, quoteCoin.Amount)
281+
})
280282
}
281283

282284
params, err := k.GetParams(ctx)
@@ -293,37 +295,50 @@ func (k DexKeeper) SwapToBase(
293295
if ok, err := clammKeeper.HasPool(ctx, metadataLP); err != nil {
294296
return err
295297
} else if ok {
296-
baseDenom := params.BaseDenom
297-
prevBaseBalance, err := k.moveBankKeeper.GetBalance(ctx, types.StdAddr, baseDenom)
298-
if err != nil {
299-
return err
300-
}
298+
return k.swapToBaseViaStdAddr(ctx, addr, quoteCoin, func() error {
299+
return clammKeeper.SwapToBase(ctx, vmtypes.StdAddress, metadataLP, metadataQuote, quoteCoin.Amount)
300+
})
301+
}
302+
}
301303

302-
// CLAMM cannot swap directly from blocked recipients such as the fee collector,
303-
// so route the quote through StdAddr, execute the swap there, then send only
304-
// the base-denom delta back to the original caller.
305-
if err := k.moveBankKeeper.SendCoin(ctx, addr, types.StdAddr, quoteCoin.Denom, quoteCoin.Amount); err != nil {
306-
return err
307-
}
308-
if err := clammKeeper.SwapToBase(ctx, vmtypes.StdAddress, metadataLP, metadataQuote, quoteCoin.Amount); err != nil {
309-
return err
310-
}
304+
return types.ErrInvalidRequest.Wrapf("LP `%s` is not a supported DEX pool", metadataLP.String())
305+
}
311306

312-
postBaseBalance, err := k.moveBankKeeper.GetBalance(ctx, types.StdAddr, baseDenom)
313-
if err != nil {
314-
return err
315-
}
307+
func (k DexKeeper) swapToBaseViaStdAddr(
308+
ctx context.Context,
309+
addr sdk.AccAddress,
310+
quoteCoin sdk.Coin,
311+
swap func() error,
312+
) error {
313+
params, err := k.GetParams(ctx)
314+
if err != nil {
315+
return err
316+
}
316317

317-
if postBaseBalance.GT(prevBaseBalance) {
318-
baseBalanceDiff := postBaseBalance.Sub(prevBaseBalance)
319-
if err := k.moveBankKeeper.SendCoin(ctx, types.StdAddr, addr, baseDenom, baseBalanceDiff); err != nil {
320-
return err
321-
}
322-
}
318+
baseDenom := params.BaseDenom
319+
prevBaseBalance, err := k.moveBankKeeper.GetBalance(ctx, types.StdAddr, baseDenom)
320+
if err != nil {
321+
return err
322+
}
323323

324-
return nil
324+
if err := k.moveBankKeeper.SendCoin(ctx, addr, types.StdAddr, quoteCoin.Denom, quoteCoin.Amount); err != nil {
325+
return err
326+
}
327+
if err := swap(); err != nil {
328+
return err
329+
}
330+
331+
postBaseBalance, err := k.moveBankKeeper.GetBalance(ctx, types.StdAddr, baseDenom)
332+
if err != nil {
333+
return err
334+
}
335+
336+
if postBaseBalance.GT(prevBaseBalance) {
337+
baseBalanceDiff := postBaseBalance.Sub(prevBaseBalance)
338+
if err := k.moveBankKeeper.SendCoin(ctx, types.StdAddr, addr, baseDenom, baseBalanceDiff); err != nil {
339+
return err
325340
}
326341
}
327342

328-
return types.ErrInvalidRequest.Wrapf("LP `%s` is not a supported DEX pool", metadataLP.String())
343+
return nil
329344
}

x/move/keeper/dex_test.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -300,14 +300,18 @@ func TestDex_SwapToBase_StableSwap(t *testing.T) {
300300

301301
expectedOut := mustParseJSONUint64(t, simRes.Ret)
302302
quoteOfferCoin := sdk.NewCoin(denomCoinB, math.NewIntFromUint64(offerAmount))
303-
fundedAddr := input.Faucet.NewFundedAccount(ctx, quoteOfferCoin)
304-
before := input.BankKeeper.GetAllBalances(ctx, fundedAddr)
303+
feeCollectorAddr := authtypes.NewModuleAddress(authtypes.FeeCollectorName)
304+
input.Faucet.Fund(ctx, feeCollectorAddr, quoteOfferCoin)
305+
before := input.BankKeeper.GetAllBalances(ctx, feeCollectorAddr)
306+
stdBaseBefore := input.BankKeeper.GetBalance(ctx, types.StdAddr, bondDenom).Amount
305307

306-
require.NoError(t, dexKeeper.SwapToBase(ctx, fundedAddr, quoteOfferCoin))
308+
require.NoError(t, dexKeeper.SwapToBase(ctx, feeCollectorAddr, quoteOfferCoin))
307309

308-
after := input.BankKeeper.GetAllBalances(ctx, fundedAddr)
310+
after := input.BankKeeper.GetAllBalances(ctx, feeCollectorAddr)
311+
stdBaseAfter := input.BankKeeper.GetBalance(ctx, types.StdAddr, bondDenom).Amount
309312
require.True(t, before.AmountOf(denomCoinB).Sub(math.NewIntFromUint64(offerAmount)).Equal(after.AmountOf(denomCoinB)))
310313
require.True(t, before.AmountOf(bondDenom).Add(math.NewIntFromUint64(expectedOut)).Equal(after.AmountOf(bondDenom)))
314+
require.True(t, stdBaseBefore.Equal(stdBaseAfter))
311315
}
312316

313317
func TestDex_SwapToBase_UnsupportedPool(t *testing.T) {

x/move/keeper/stableswap_test.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"cosmossdk.io/math"
1212

1313
sdk "github.com/cosmos/cosmos-sdk/types"
14+
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
1415

1516
vmtypes "github.com/initia-labs/movevm/types"
1617

@@ -291,6 +292,40 @@ func Test_StableSwap_SwapToBase(t *testing.T) {
291292
require.True(t, before.AmountOf(baseDenom).Add(math.NewIntFromUint64(expectedOut)).Equal(after.AmountOf(baseDenom)))
292293
}
293294

295+
func Test_StableSwap_SwapToBase_BlockedRecipient(t *testing.T) {
296+
ctx, input := createDefaultTestInput(t)
297+
stableSwapKeeper := keeper.NewStableSwapKeeper(&input.MoveKeeper)
298+
299+
baseDenom := bondDenom
300+
denomCoinB := "milkINIT"
301+
denomCoinC := "ibiINIT"
302+
303+
metadataCoinB, err := types.MetadataAddressFromDenom(denomCoinB)
304+
require.NoError(t, err)
305+
306+
metadataLP := createStableSwapPool(
307+
t, ctx, input,
308+
sdk.NewCoins(
309+
sdk.NewCoin(baseDenom, math.NewInt(1_000_000_000_000)),
310+
sdk.NewCoin(denomCoinB, math.NewInt(1_000_000_000_001)),
311+
sdk.NewCoin(denomCoinC, math.NewInt(1_000_000_000_002)),
312+
),
313+
)
314+
315+
quoteOfferCoin := sdk.NewInt64Coin(denomCoinB, 1_000)
316+
feeCollectorAddr := authtypes.NewModuleAddress(authtypes.FeeCollectorName)
317+
input.Faucet.Fund(ctx, feeCollectorAddr, quoteOfferCoin)
318+
319+
err = stableSwapKeeper.SwapToBase(
320+
ctx,
321+
types.ConvertSDKAddressToVMAddress(feeCollectorAddr),
322+
metadataLP,
323+
metadataCoinB,
324+
quoteOfferCoin.Amount,
325+
)
326+
require.Error(t, err)
327+
}
328+
294329
func mustParseJSONUint64ForStableSwap(t *testing.T, raw string) uint64 {
295330
t.Helper()
296331

0 commit comments

Comments
 (0)