From fff2dc882492865262d4cd4f536e90a595d50371 Mon Sep 17 00:00:00 2001 From: Roman Date: Tue, 26 Dec 2023 01:16:35 -0700 Subject: [PATCH] refactor: arb filter for new authz exec swap (#7210) * refactor: arb filter for new authz exec swap * updates * updates * updates --- .vscode/launch.json | 4 +- x/txfees/keeper/txfee_filters/arb_tx.go | 108 +++++++++++++++++-- x/txfees/keeper/txfee_filters/arb_tx_test.go | 30 ++++++ 3 files changed, 130 insertions(+), 12 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 4845b192a4b..c6347cb24fe 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -283,12 +283,12 @@ "type": "go", "request": "launch", "mode": "test", - "program": "${workspaceFolder}/x/txfees", + "program": "${workspaceFolder}/x/txfees/keeper/txfee_filters", "args": [ "-test.timeout", "30m", "-test.run", - "TestKeeperTestSuite/TestYourName", + "TestTxFeeFilter/TestIsArbTxLooseAuthz_SwapMsg", "-test.v" ], }, diff --git a/x/txfees/keeper/txfee_filters/arb_tx.go b/x/txfees/keeper/txfee_filters/arb_tx.go index 00928422e70..8c8179a5825 100644 --- a/x/txfees/keeper/txfee_filters/arb_tx.go +++ b/x/txfees/keeper/txfee_filters/arb_tx.go @@ -52,6 +52,42 @@ func (m AffiliateSwapMsg) TokenOutDenom() string { var _ poolmanagertypes.SwapMsgRoute = AffiliateSwapMsg{} +type InputCoin struct { + Denom string `json:"denom"` + Amount string `json:"amount"` +} + +type Slippage struct { + MinOutputAmount string `json:"min_output_amount"` +} + +type ContractSwap struct { + InputCoin InputCoin `json:"input_coin"` + OutputDenom string `json:"output_denom"` + Slippage Slippage `json:"slippage"` +} + +type ContractSwapMsg struct { + ContractSwap `json:"swap"` +} + +// TokenDenomsOnPath implements types.SwapMsgRoute. +func (c ContractSwapMsg) TokenDenomsOnPath() []string { + return []string{c.InputCoin.Denom, c.OutputDenom} +} + +// TokenInDenom implements types.SwapMsgRoute. +func (c ContractSwapMsg) TokenInDenom() string { + return c.InputCoin.Denom +} + +// TokenOutDenom implements types.SwapMsgRoute. +func (c ContractSwapMsg) TokenOutDenom() string { + return c.OutputDenom +} + +var _ poolmanagertypes.SwapMsgRoute = ContractSwapMsg{} + // We check if a tx is an arbitrage for the mempool right now by seeing: // 1) does start token of a msg = final token of msg (definitionally correct) // 2) does it have multiple swap messages, with different tx ins. If so, we assume its an arb. @@ -107,21 +143,47 @@ func isArbTxLooseAuthz(msg sdk.Msg, swapInDenom string, lpTypesSeen map[gammtype contractMessage := msgExecuteContract.GetMsg() // Check that the contract message is an affiliate swap message - if ok := isAffiliateSwapMsg(contractMessage); !ok { + isAffliliateSwap := isAffiliateSwapMsg(contractMessage) + isContractSwap := isContractSwapContractMsg(contractMessage) + + if !isAffliliateSwap && !isContractSwap { return swapInDenom, false } - var affiliateSwapMsg AffiliateSwapMsg - if err := json.Unmarshal(contractMessage, &affiliateSwapMsg); err != nil { - // If we can't unmarshal it, it's not an affiliate swap message - return swapInDenom, false + if isAffliliateSwap { + var affiliateSwapMsg AffiliateSwapMsg + if err := json.Unmarshal(contractMessage, &affiliateSwapMsg); err != nil { + // If we can't unmarshal it, it's not an affiliate swap message + return swapInDenom, false + } + + // Otherwise, we have an affiliate swap message, so we check if it's an arb + affiliateSwapMsg.TokenIn = tokenIn.Denom + swapInDenom, isArb := isArbTxLooseSwapMsg(affiliateSwapMsg, swapInDenom) + if isArb { + return swapInDenom, true + } } - // Otherwise, we have an affiliate swap message, so we check if it's an arb - affiliateSwapMsg.TokenIn = tokenIn.Denom - swapInDenom, isArb := isArbTxLooseSwapMsg(affiliateSwapMsg, swapInDenom) - if isArb { - return swapInDenom, true + if isContractSwap { + var contractSwapMsg ContractSwapMsg + if err := json.Unmarshal(contractMessage, &contractSwapMsg); err != nil { + // If we can't unmarshal it, it's not a contract swap message + return swapInDenom, false + } + + // Otherwise, we have a contract swap message, so we check if it's an arb + swapInDenom, isArb := isArbTxLooseSwapMsg(contractSwapMsg, swapInDenom) + if isArb { + return swapInDenom, true + } + + // Also, check sent tokenIn just in case. + contractSwapMsg.InputCoin.Denom = tokenIn.Denom + swapInDenom, isArb = isArbTxLooseSwapMsg(contractSwapMsg, swapInDenom) + if isArb { + return swapInDenom, true + } } return swapInDenom, false @@ -195,3 +257,29 @@ func isAffiliateSwapMsg(msg []byte) bool { return true } + +// check if this: https://celatone.osmosis.zone/osmosis-1/txs/8D20755D4E009CB72C763963A76886BCCCC5C2EBFC3F57266332710216A0D10D +func isContractSwapContractMsg(msg []byte) bool { + // Check that the contract message is a valid JSON object + jsonObject := make(map[string]interface{}) + err := json.Unmarshal(msg, &jsonObject) + if err != nil { + return false + } + + // check the main key is "swap" + swap, ok := jsonObject["swap"].(map[string]interface{}) + if !ok { + return false + } + + if input_coin, ok := swap["input_coin"].(map[string]interface{}); !ok || len(input_coin) == 0 { + return false + } + + if outputDenom, ok := swap["output_denom"].(string); !ok || len(outputDenom) == 0 { + return false + } + + return true +} diff --git a/x/txfees/keeper/txfee_filters/arb_tx_test.go b/x/txfees/keeper/txfee_filters/arb_tx_test.go index 1c7f24e822a..4268944b702 100644 --- a/x/txfees/keeper/txfee_filters/arb_tx_test.go +++ b/x/txfees/keeper/txfee_filters/arb_tx_test.go @@ -65,6 +65,36 @@ func (suite *KeeperTestSuite) TestIsArbTxLooseAuthz_AffiliateSwapMsg() { suite.Require().True(isArb) } +// Tests that the arb filter is enabled on swap msg. +func (suite *KeeperTestSuite) TestIsArbTxLooseAuthz_SwapMsg() { + contractSwapMsg := &txfee_filters.ContractSwapMsg{ + ContractSwap: txfee_filters.ContractSwap{ + InputCoin: txfee_filters.InputCoin{ + Amount: "2775854", + Denom: "ibc/D1542AA8762DB13087D8364F3EA6509FD6F009A34F00426AF9E4F9FA85CBBF1F", + }, + OutputDenom: "ibc/D1542AA8762DB13087D8364F3EA6509FD6F009A34F00426AF9E4F9FA85CBBF1F", + Slippage: txfee_filters.Slippage{ + MinOutputAmount: "2775854", + }, + }, + } + + msgBz, err := json.Marshal(contractSwapMsg) + suite.Require().NoError(err) + + // https://celatone.osmosis.zone/osmosis-1/txs/8D20755D4E009CB72C763963A76886BCCCC5C2EBFC3F57266332710216A0D10D + executeMsg := &wasmtypes.MsgExecuteContract{ + Contract: "osmo1etpha3a65tds0hmn3wfjeag6wgxgrkuwg2zh94cf5hapz7mz04dq6c25s5", + Sender: "osmo1dldrxz5p8uezxz3qstpv92de7wgfp7hvr72dcm", + Funds: sdk.NewCoins(sdk.NewCoin("ibc/498A0751C798A0D9A389AA3691123DADA57DAA4FE165D5C75894505B876BA6E4", sdk.NewInt(217084399))), + Msg: msgBz, + } + + _, isArb := txfee_filters.IsArbTxLooseAuthz(executeMsg, executeMsg.Funds[0].Denom, map[types.LiquidityChangeType]bool{}) + suite.Require().True(isArb) +} + func (suite *KeeperTestSuite) TestIsArbTxLooseAuthz_OtherMsg() { otherMsg := []byte(`{"update_feed": {}}`)