From 7096ecf15f89a79460ec8fe941d3950ca58cd8e5 Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 16 Dec 2022 13:39:32 -0800 Subject: [PATCH] refactor(wasmbindings): remove gamm and twap bindings --- CHANGELOG.md | 1 + app/keepers/keepers.go | 2 +- wasmbinding/bindings/msg.go | 8 - wasmbinding/bindings/pool.go | 8 - wasmbinding/bindings/query.go | 73 ---- wasmbinding/bindings/types.go | 74 ---- wasmbinding/bindings/types_test.go | 179 --------- wasmbinding/message_plugin.go | 78 +--- wasmbinding/queries.go | 117 +----- wasmbinding/query_plugin.go | 51 --- wasmbinding/test/custom_msg_test.go | 375 ------------------- wasmbinding/test/custom_query_test.go | 196 ---------- wasmbinding/test/messages_test.go | 417 --------------------- wasmbinding/test/queries_test.go | 428 +--------------------- wasmbinding/testdata/download_releases.sh | 24 -- wasmbinding/testdata/reflect.wasm | Bin 262334 -> 0 bytes wasmbinding/testdata/version.txt | 1 - wasmbinding/wasm.go | 8 +- 18 files changed, 7 insertions(+), 2033 deletions(-) delete mode 100644 wasmbinding/bindings/pool.go delete mode 100644 wasmbinding/bindings/types.go delete mode 100644 wasmbinding/bindings/types_test.go delete mode 100755 wasmbinding/testdata/download_releases.sh delete mode 100644 wasmbinding/testdata/reflect.wasm delete mode 100644 wasmbinding/testdata/version.txt diff --git a/CHANGELOG.md b/CHANGELOG.md index a1e8ebb5a26..484ccc89f2b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -54,6 +54,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### API breaks * [#3763](https://github.com/osmosis-labs/osmosis/pull/3763) Move binary search and error tolerance code from `osmoutils` into `osmomath` +* [#376x](https://github.com/osmosis-labs/osmosis/pull/3763) Remove Osmosis gamm and twap `bindings` that were previously supported as custom wasm plugins. ### Bug fixes diff --git a/app/keepers/keepers.go b/app/keepers/keepers.go index 40bc3b90838..465ed614c11 100644 --- a/app/keepers/keepers.go +++ b/app/keepers/keepers.go @@ -369,7 +369,7 @@ func (appKeepers *AppKeepers) InitNormalKeepers( // if we want to allow any custom callbacks supportedFeatures := "iterator,staking,stargate,osmosis,cosmwasm_1_1" - wasmOpts = append(owasm.RegisterCustomPlugins(appKeepers.GAMMKeeper, appKeepers.BankKeeper, appKeepers.TwapKeeper, appKeepers.TokenFactoryKeeper), wasmOpts...) + wasmOpts = append(owasm.RegisterCustomPlugins(appKeepers.BankKeeper, appKeepers.TokenFactoryKeeper), wasmOpts...) wasmOpts = append(owasm.RegisterStargateQueries(*bApp.GRPCQueryRouter(), appCodec), wasmOpts...) wasmKeeper := wasm.NewKeeper( diff --git a/wasmbinding/bindings/msg.go b/wasmbinding/bindings/msg.go index c58dd5e0e7c..d91feb6bd3b 100644 --- a/wasmbinding/bindings/msg.go +++ b/wasmbinding/bindings/msg.go @@ -15,8 +15,6 @@ type OsmosisMsg struct { /// that they are the admin of. /// Currently, the burn from address must be the admin contract. BurnTokens *BurnTokens `json:"burn_tokens,omitempty"` - /// Swap over one or more pools - Swap *SwapMsg `json:"swap,omitempty"` } // CreateDenom creates a new factory denom, of denomination: @@ -48,9 +46,3 @@ type BurnTokens struct { // BurnFromAddress must be set to "" for now. BurnFromAddress string `json:"burn_from_address"` } - -type SwapMsg struct { - First Swap `json:"first"` - Route []Step `json:"route"` - Amount SwapAmountWithLimit `json:"amount"` -} diff --git a/wasmbinding/bindings/pool.go b/wasmbinding/bindings/pool.go deleted file mode 100644 index e7c78b8c40d..00000000000 --- a/wasmbinding/bindings/pool.go +++ /dev/null @@ -1,8 +0,0 @@ -package bindings - -import sdk "github.com/cosmos/cosmos-sdk/types" - -type PoolAssets struct { - Assets []sdk.Coin - Shares sdk.Coin -} diff --git a/wasmbinding/bindings/query.go b/wasmbinding/bindings/query.go index e2bc9f70c3f..de6c96fec4f 100644 --- a/wasmbinding/bindings/query.go +++ b/wasmbinding/bindings/query.go @@ -1,24 +1,11 @@ package bindings -import ( - wasmvmtypes "github.com/CosmWasm/wasmvm/types" -) - // OsmosisQuery contains osmosis custom queries. // See https://github.com/osmosis-labs/osmosis-bindings/blob/main/packages/bindings/src/query.rs type OsmosisQuery struct { /// Given a subdenom minted by a contract via `OsmosisMsg::MintTokens`, /// returns the full denom as used by `BankMsg::Send`. FullDenom *FullDenom `json:"full_denom,omitempty"` - /// For a given pool ID, list all tokens traded on it with current liquidity (spot). - /// As well as the total number of LP shares and their denom. - PoolState *PoolState `json:"pool_state,omitempty"` - /// Return current spot price swapping In for Out on given pool ID. - /// Warning: this can easily be manipulated via sandwich attacks, do not use as price oracle. - /// We will add TWAP for more robust price feed. - SpotPrice *SpotPrice `json:"spot_price,omitempty"` - /// Return current spot price swapping In for Out on given pool ID. - EstimateSwap *EstimateSwap `json:"estimate_swap,omitempty"` /// Returns the admin of a denom, if the denom is a Token Factory denom. DenomAdmin *DenomAdmin `json:"denom_admin,omitempty"` } @@ -36,66 +23,6 @@ type DenomAdminResponse struct { Admin string `json:"admin"` } -type PoolState struct { - PoolId uint64 `json:"id"` -} - -type SpotPrice struct { - Swap Swap `json:"swap"` - WithSwapFee bool `json:"with_swap_fee"` -} - -type EstimateSwap struct { - Sender string `json:"sender"` - First Swap `json:"first"` - Route []Step `json:"route"` - Amount SwapAmount `json:"amount"` -} - -type ArithmeticTwap struct { - PoolId uint64 `json:"id"` - QuoteAssetDenom string `json:"quote_asset_denom"` - BaseAssetDenom string `json:"base_asset_denom"` - // NOTE: StartTime is expected to be in Unix time milliseconds. - StartTime int64 `json:"start_time"` - // NOTE: EndTime is expected to be in Unix time milliseconds. - EndTime int64 `json:"end_time"` -} - -type ArithmeticTwapToNow struct { - PoolId uint64 `json:"id"` - QuoteAssetDenom string `json:"quote_asset_denom"` - BaseAssetDenom string `json:"base_asset_denom"` - // NOTE: StartTime is expected to be in Unix time milliseconds. - StartTime int64 `json:"start_time"` -} - -func (e *EstimateSwap) ToSwapMsg() *SwapMsg { - return &SwapMsg{ - First: e.First, - Route: e.Route, - Amount: e.Amount.Unlimited(), - } -} - type FullDenomResponse struct { Denom string `json:"denom"` } - -type PoolStateResponse struct { - /// The various assets that be swapped. Including current liquidity. - Assets []wasmvmtypes.Coin `json:"assets"` - /// The number of LP shares and their amount - Shares wasmvmtypes.Coin `json:"shares"` -} - -type SpotPriceResponse struct { - /// How many output we would get for 1 input - Price string `json:"price"` -} - -type EstimatePriceResponse struct { - // If you query with SwapAmount::Input, this is SwapAmount::Output. - // If you query with SwapAmount::Output, this is SwapAmount::Input. - Amount SwapAmount `json:"swap_amount"` -} diff --git a/wasmbinding/bindings/types.go b/wasmbinding/bindings/types.go deleted file mode 100644 index 2f3d89a48f3..00000000000 --- a/wasmbinding/bindings/types.go +++ /dev/null @@ -1,74 +0,0 @@ -package bindings - -import ( - "math" - - sdk "github.com/cosmos/cosmos-sdk/types" -) - -type Swap struct { - PoolId uint64 `json:"pool_id"` - DenomIn string `json:"denom_in"` - DenomOut string `json:"denom_out"` -} - -type Step struct { - PoolId uint64 `json:"pool_id"` - DenomOut string `json:"denom_out"` -} - -type SwapAmount struct { - In *sdk.Int `json:"in,omitempty"` - Out *sdk.Int `json:"out,omitempty"` -} - -// This returns SwapAmountWithLimit with the largest possible limits (that will never be hit) -func (s SwapAmount) Unlimited() SwapAmountWithLimit { - if s.In != nil { - return SwapAmountWithLimit{ - ExactIn: &ExactIn{ - Input: *s.In, - MinOutput: sdk.NewInt(1), - }, - } - } - if s.Out != nil { - return SwapAmountWithLimit{ - ExactOut: &ExactOut{ - Output: *s.Out, - MaxInput: sdk.NewInt(math.MaxInt64), - }, - } - } - panic("Must define In or Out") -} - -type SwapAmountWithLimit struct { - ExactIn *ExactIn `json:"exact_in,omitempty"` - ExactOut *ExactOut `json:"exact_out,omitempty"` -} - -// This returns the amount without min/max to use as simpler argument -func (s SwapAmountWithLimit) RemoveLimit() SwapAmount { - if s.ExactIn != nil { - return SwapAmount{ - In: &s.ExactIn.Input, - } - } - if s.ExactOut != nil { - return SwapAmount{ - Out: &s.ExactOut.Output, - } - } - panic("Must define ExactIn or ExactOut") -} - -type ExactIn struct { - Input sdk.Int `json:"input"` - MinOutput sdk.Int `json:"min_output"` -} - -type ExactOut struct { - MaxInput sdk.Int `json:"max_input"` - Output sdk.Int `json:"output"` -} diff --git a/wasmbinding/bindings/types_test.go b/wasmbinding/bindings/types_test.go deleted file mode 100644 index 859c264d54b..00000000000 --- a/wasmbinding/bindings/types_test.go +++ /dev/null @@ -1,179 +0,0 @@ -package bindings - -import ( - "encoding/json" - "testing" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -var ( - swapJson = []byte("{ \"pool_id\": 1, \"denom_in\": \"denomIn\", \"denom_out\": \"denomOut\" }") - swap = Swap{ - PoolId: 1, - DenomIn: "denomIn", - DenomOut: "denomOut", - } - stepJson = []byte("{ \"pool_id\": 2, \"denom_out\": \"denomOut\" }") - step = Step{ - PoolId: 2, - DenomOut: "denomOut", - } - swapAmountInJson = []byte("{ \"in\": \"123\", \"out\": null }") - in = sdk.NewInt(123) - swapAmountIn = SwapAmount{ - In: &in, - } - swapAmountOutJson = []byte("{ \"out\": \"456\" }") - out = sdk.NewInt(456) - swapAmountOut = SwapAmount{ - Out: &out, - } - exactIn = ExactIn{ - Input: sdk.NewInt(789), - MinOutput: sdk.NewInt(101112), - } - exactOut = ExactOut{ - MaxInput: sdk.NewInt(131415), - Output: sdk.NewInt(161718), - } - swapAmountExactInJson = []byte("{ \"exact_in\": { \"input\": \"789\", \"min_output\": \"101112\" } }") - swapAmountExactIn = SwapAmountWithLimit{ - ExactIn: &exactIn, - } - swapAmountExactOutJson = []byte("{ \"exact_in\": null, \"exact_out\": { \"max_input\": \"131415\", \"output\": \"161718\" } }") - swapAmountExactOut = SwapAmountWithLimit{ - ExactOut: &exactOut, - } -) - -func TestTypesEncodeDecode(t *testing.T) { - // Swap - // Marshal - bzSwap, err := json.Marshal(swap) - require.NoError(t, err) - // Unmarshal - var swap1 Swap - err = json.Unmarshal(bzSwap, &swap1) - require.NoError(t, err) - // Check - assert.Equal(t, swap, swap1) - - // Step - // Marshal - bzStep, err := json.Marshal(step) - require.NoError(t, err) - // Unmarshal - var step1 Step - err = json.Unmarshal(bzStep, &step1) - require.NoError(t, err) - // Check - assert.Equal(t, step, step1) - - // SwapAmount - // Marshal - bzSwapAmount, err := json.Marshal(swapAmountOut) - require.NoError(t, err) - // Unmarshal - var swapAmount1 SwapAmount - err = json.Unmarshal(bzSwapAmount, &swapAmount1) - require.NoError(t, err) - // Check - assert.Equal(t, swapAmountOut, swapAmount1) - - // SwapAmount in - // Marshal - bzSwapAmount2, err := json.Marshal(swapAmountIn) - require.NoError(t, err) - // Unmarshal - var swapAmount2 SwapAmount - err = json.Unmarshal(bzSwapAmount2, &swapAmount2) - require.NoError(t, err) - // Check - assert.Equal(t, swapAmountIn, swapAmount2) - - // SwapAmount out - // Marshal - bzSwapAmount3, err := json.Marshal(swapAmountOut) - require.NoError(t, err) - // Unmarshal - var swapAmount3 SwapAmount - err = json.Unmarshal(bzSwapAmount3, &swapAmount3) - require.NoError(t, err) - // Check - assert.Equal(t, swapAmountOut, swapAmount3) - - // SwapAmount exact in - // Marshal - bzSwapAmountWithLimit1, err := json.Marshal(swapAmountExactIn) - require.NoError(t, err) - // Unmarshal - var swapAmountWithLimit1 SwapAmountWithLimit - err = json.Unmarshal(bzSwapAmountWithLimit1, &swapAmountWithLimit1) - require.NoError(t, err) - // Check - assert.Equal(t, swapAmountExactIn, swapAmountWithLimit1) - - // SwapAmount exact out - // Marshal - bzSwapAmountWithLimit2, err := json.Marshal(swapAmountExactOut) - require.NoError(t, err) - // Unmarshal - var swapAmountWithLimit2 SwapAmountWithLimit - err = json.Unmarshal(bzSwapAmountWithLimit2, &swapAmountWithLimit2) - require.NoError(t, err) - // Check - assert.Equal(t, swapAmountExactOut, swapAmountWithLimit2) -} - -func TestTypesDecode(t *testing.T) { - // Swap - // Unmarshal - var swap1 Swap - err := json.Unmarshal([]byte(swapJson), &swap1) - require.NoError(t, err) - // Check - assert.Equal(t, swap, swap1) - - // Step - // Unmarshal - var step1 Step - err = json.Unmarshal(stepJson, &step1) - require.NoError(t, err) - // Check - assert.Equal(t, step, step1) - - // SwapAmount in - // Unmarshal - var swapAmount1 SwapAmount - err = json.Unmarshal(swapAmountInJson, &swapAmount1) - require.NoError(t, err) - // Check - assert.Equal(t, swapAmountIn, swapAmount1) - - // SwapAmount out - // Unmarshal - var swapAmount2 SwapAmount - err = json.Unmarshal(swapAmountOutJson, &swapAmount2) - require.NoError(t, err) - // Check - assert.Equal(t, swapAmountOut, swapAmount2) - - // SwapAmount exact in - // Unmarshal - var swapAmountWithLimit1 SwapAmountWithLimit - err = json.Unmarshal(swapAmountExactInJson, &swapAmountWithLimit1) - require.NoError(t, err) - // Check - assert.Equal(t, swapAmountExactIn, swapAmountWithLimit1) - - // SwapAmount exact out - // Unmarshal - var swapAmountWithLimit2 SwapAmountWithLimit - err = json.Unmarshal(swapAmountExactOutJson, &swapAmountWithLimit2) - require.NoError(t, err) - // Check - assert.Equal(t, swapAmountExactOut, swapAmountWithLimit2) -} diff --git a/wasmbinding/message_plugin.go b/wasmbinding/message_plugin.go index c57452ae1e6..4d6b775e38b 100644 --- a/wasmbinding/message_plugin.go +++ b/wasmbinding/message_plugin.go @@ -10,20 +10,17 @@ import ( bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" "github.com/osmosis-labs/osmosis/v13/wasmbinding/bindings" - gammkeeper "github.com/osmosis-labs/osmosis/v13/x/gamm/keeper" - gammtypes "github.com/osmosis-labs/osmosis/v13/x/gamm/types" tokenfactorykeeper "github.com/osmosis-labs/osmosis/v13/x/tokenfactory/keeper" tokenfactorytypes "github.com/osmosis-labs/osmosis/v13/x/tokenfactory/types" ) // CustomMessageDecorator returns decorator for custom CosmWasm bindings messages -func CustomMessageDecorator(gammKeeper *gammkeeper.Keeper, bank *bankkeeper.BaseKeeper, tokenFactory *tokenfactorykeeper.Keeper) func(wasmkeeper.Messenger) wasmkeeper.Messenger { +func CustomMessageDecorator(bank *bankkeeper.BaseKeeper, tokenFactory *tokenfactorykeeper.Keeper) func(wasmkeeper.Messenger) wasmkeeper.Messenger { return func(old wasmkeeper.Messenger) wasmkeeper.Messenger { return &CustomMessenger{ wrapped: old, bank: bank, - gammKeeper: gammKeeper, tokenFactory: tokenFactory, } } @@ -32,7 +29,6 @@ func CustomMessageDecorator(gammKeeper *gammkeeper.Keeper, bank *bankkeeper.Base type CustomMessenger struct { wrapped wasmkeeper.Messenger bank *bankkeeper.BaseKeeper - gammKeeper *gammkeeper.Keeper tokenFactory *tokenfactorykeeper.Keeper } @@ -59,9 +55,6 @@ func (m *CustomMessenger) DispatchMsg(ctx sdk.Context, contractAddr sdk.AccAddre if contractMsg.BurnTokens != nil { return m.burnTokens(ctx, contractAddr, contractMsg.BurnTokens) } - if contractMsg.Swap != nil { - return m.swapTokens(ctx, contractAddr, contractMsg.Swap) - } } return m.wrapped.DispatchMsg(ctx, contractAddr, contractIBCPortID, msg) } @@ -203,75 +196,6 @@ func PerformBurn(f *tokenfactorykeeper.Keeper, ctx sdk.Context, contractAddr sdk return nil } -// swapTokens swaps one denom for another. -func (m *CustomMessenger) swapTokens(ctx sdk.Context, contractAddr sdk.AccAddress, swap *bindings.SwapMsg) ([]sdk.Event, [][]byte, error) { - _, err := PerformSwap(m.gammKeeper, ctx, contractAddr, swap) - if err != nil { - return nil, nil, sdkerrors.Wrap(err, "perform swap") - } - return nil, nil, nil -} - -// PerformSwap can be used both for the real swap, and the EstimateSwap query -func PerformSwap(keeper *gammkeeper.Keeper, ctx sdk.Context, contractAddr sdk.AccAddress, swap *bindings.SwapMsg) (*bindings.SwapAmount, error) { - if swap == nil { - return nil, wasmvmtypes.InvalidRequest{Err: "gamm perform swap null swap"} - } - if swap.Amount.ExactIn != nil { - routes := []gammtypes.SwapAmountInRoute{{ - PoolId: swap.First.PoolId, - TokenOutDenom: swap.First.DenomOut, - }} - for _, step := range swap.Route { - routes = append(routes, gammtypes.SwapAmountInRoute{ - PoolId: step.PoolId, - TokenOutDenom: step.DenomOut, - }) - } - if swap.Amount.ExactIn.Input.IsNegative() { - return nil, wasmvmtypes.InvalidRequest{Err: "gamm perform swap negative amount in"} - } - tokenIn := sdk.Coin{ - Denom: swap.First.DenomIn, - Amount: swap.Amount.ExactIn.Input, - } - tokenOutMinAmount := swap.Amount.ExactIn.MinOutput - tokenOutAmount, err := keeper.MultihopSwapExactAmountIn(ctx, contractAddr, routes, tokenIn, tokenOutMinAmount) - if err != nil { - return nil, sdkerrors.Wrap(err, "gamm perform swap exact amount in") - } - return &bindings.SwapAmount{Out: &tokenOutAmount}, nil - } else if swap.Amount.ExactOut != nil { - routes := []gammtypes.SwapAmountOutRoute{{ - PoolId: swap.First.PoolId, - TokenInDenom: swap.First.DenomIn, - }} - output := swap.First.DenomOut - for _, step := range swap.Route { - routes = append(routes, gammtypes.SwapAmountOutRoute{ - PoolId: step.PoolId, - TokenInDenom: output, - }) - output = step.DenomOut - } - tokenInMaxAmount := swap.Amount.ExactOut.MaxInput - if swap.Amount.ExactOut.Output.IsNegative() { - return nil, wasmvmtypes.InvalidRequest{Err: "gamm perform swap negative amount out"} - } - tokenOut := sdk.Coin{ - Denom: output, - Amount: swap.Amount.ExactOut.Output, - } - tokenInAmount, err := keeper.MultihopSwapExactAmountOut(ctx, contractAddr, routes, tokenInMaxAmount, tokenOut) - if err != nil { - return nil, sdkerrors.Wrap(err, "gamm perform swap exact amount out") - } - return &bindings.SwapAmount{In: &tokenInAmount}, nil - } else { - return nil, wasmvmtypes.UnsupportedRequest{Kind: "must support either Swap.ExactIn or Swap.ExactOut"} - } -} - // GetFullDenom is a function, not method, so the message_plugin can use it func GetFullDenom(contract string, subDenom string) (string, error) { // Address validation diff --git a/wasmbinding/queries.go b/wasmbinding/queries.go index 59df2e89161..d03475897a8 100644 --- a/wasmbinding/queries.go +++ b/wasmbinding/queries.go @@ -2,30 +2,20 @@ package wasmbinding import ( "fmt" - "time" - wasmvmtypes "github.com/CosmWasm/wasmvm/types" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/osmosis-labs/osmosis/v13/wasmbinding/bindings" - gammkeeper "github.com/osmosis-labs/osmosis/v13/x/gamm/keeper" - gammtypes "github.com/osmosis-labs/osmosis/v13/x/gamm/types" tokenfactorykeeper "github.com/osmosis-labs/osmosis/v13/x/tokenfactory/keeper" - twapkeeper "github.com/osmosis-labs/osmosis/v13/x/twap" ) type QueryPlugin struct { - gammKeeper *gammkeeper.Keeper - twapKeeper *twapkeeper.Keeper tokenFactoryKeeper *tokenfactorykeeper.Keeper } // NewQueryPlugin returns a reference to a new QueryPlugin. -func NewQueryPlugin(gk *gammkeeper.Keeper, tk *twapkeeper.Keeper, tfk *tokenfactorykeeper.Keeper) *QueryPlugin { +func NewQueryPlugin(tfk *tokenfactorykeeper.Keeper) *QueryPlugin { return &QueryPlugin{ - gammKeeper: gk, - twapKeeper: tk, tokenFactoryKeeper: tfk, } } @@ -39,108 +29,3 @@ func (qp QueryPlugin) GetDenomAdmin(ctx sdk.Context, denom string) (*bindings.De return &bindings.DenomAdminResponse{Admin: metadata.Admin}, nil } - -// GetPoolState is a query to get pool liquidity and amount of each denoms' pool shares. -func (qp QueryPlugin) GetPoolState(ctx sdk.Context, poolID uint64) (*bindings.PoolAssets, error) { - poolData, err := qp.gammKeeper.GetPoolAndPoke(ctx, poolID) - if err != nil { - return nil, sdkerrors.Wrap(err, "gamm get pool") - } - - return &bindings.PoolAssets{ - Assets: poolData.GetTotalPoolLiquidity(ctx), - Shares: sdk.Coin{ - Denom: gammtypes.GetPoolShareDenom(poolID), - Amount: poolData.GetTotalShares(), - }, - }, nil -} - -// GetSpotPrice is a query to get spot price of denoms. -func (qp QueryPlugin) GetSpotPrice(ctx sdk.Context, spotPrice *bindings.SpotPrice) (*sdk.Dec, error) { - if spotPrice == nil { - return nil, wasmvmtypes.InvalidRequest{Err: "gamm spot price null"} - } - - poolId := spotPrice.Swap.PoolId - denomIn := spotPrice.Swap.DenomIn - denomOut := spotPrice.Swap.DenomOut - withSwapFee := spotPrice.WithSwapFee - - price, err := qp.gammKeeper.CalculateSpotPrice(ctx, poolId, denomIn, denomOut) - if err != nil { - return nil, sdkerrors.Wrap(err, "gamm get spot price") - } - - if withSwapFee { - poolData, err := qp.gammKeeper.GetPoolAndPoke(ctx, poolId) - if err != nil { - return nil, sdkerrors.Wrap(err, "gamm get pool") - } - - price = price.Mul(sdk.OneDec().Sub(poolData.GetSwapFee(ctx))) - } - - return &price, nil -} - -// EstimateSwap validates each denom (in / out) and performs a swap. -func (qp QueryPlugin) EstimateSwap(ctx sdk.Context, estimateSwap *bindings.EstimateSwap) (*bindings.SwapAmount, error) { - if estimateSwap == nil { - return nil, wasmvmtypes.InvalidRequest{Err: "gamm estimate swap null"} - } - if err := sdk.ValidateDenom(estimateSwap.First.DenomIn); err != nil { - return nil, sdkerrors.Wrap(err, "gamm estimate swap denom in") - } - if err := sdk.ValidateDenom(estimateSwap.First.DenomOut); err != nil { - return nil, sdkerrors.Wrap(err, "gamm estimate swap denom out") - } - senderAddr, err := sdk.AccAddressFromBech32(estimateSwap.Sender) - if err != nil { - return nil, sdkerrors.Wrap(err, "gamm estimate swap sender address") - } - - if estimateSwap.Amount == (bindings.SwapAmount{}) { - return nil, wasmvmtypes.InvalidRequest{Err: "gamm estimate swap empty swap"} - } - - estimate, err := PerformSwap(qp.gammKeeper, ctx, senderAddr, estimateSwap.ToSwapMsg()) - return estimate, err -} - -func (qp QueryPlugin) ArithmeticTwap(ctx sdk.Context, arithmeticTwap *bindings.ArithmeticTwap) (*sdk.Dec, error) { - if arithmeticTwap == nil { - return nil, wasmvmtypes.InvalidRequest{Err: "gamm arithmetic twap null"} - } - - poolId := arithmeticTwap.PoolId - quoteAssetDenom := arithmeticTwap.QuoteAssetDenom - baseAssetDenom := arithmeticTwap.BaseAssetDenom - startTime := time.UnixMilli(arithmeticTwap.StartTime) - endTime := time.UnixMilli(arithmeticTwap.EndTime) - - twap, err := qp.twapKeeper.GetArithmeticTwap(ctx, poolId, baseAssetDenom, quoteAssetDenom, startTime, endTime) - if err != nil { - return nil, sdkerrors.Wrap(err, "gamm arithmetic twap") - } - - return &twap, nil -} - -func (qp QueryPlugin) ArithmeticTwapToNow(ctx sdk.Context, arithmeticTwap *bindings.ArithmeticTwapToNow) (*sdk.Dec, error) { - if arithmeticTwap == nil { - return nil, wasmvmtypes.InvalidRequest{Err: "gamm arithmetic twap null"} - } - - poolId := arithmeticTwap.PoolId - quoteAssetDenom := arithmeticTwap.QuoteAssetDenom - baseAssetDenom := arithmeticTwap.BaseAssetDenom - startTime := time.UnixMilli(arithmeticTwap.StartTime) - - twap, err := qp.twapKeeper.GetArithmeticTwapToNow(ctx, poolId, baseAssetDenom, quoteAssetDenom, startTime) - if err != nil { - return nil, sdkerrors.Wrap(err, "gamm arithmetic twap") - } - - return &twap, nil -} diff --git a/wasmbinding/query_plugin.go b/wasmbinding/query_plugin.go index 1016043b47f..aa1626d060e 100644 --- a/wasmbinding/query_plugin.go +++ b/wasmbinding/query_plugin.go @@ -86,57 +86,6 @@ func CustomQuerier(qp *QueryPlugin) func(ctx sdk.Context, request json.RawMessag return bz, nil - case contractQuery.PoolState != nil: - poolId := contractQuery.PoolState.PoolId - - state, err := qp.GetPoolState(ctx, poolId) - if err != nil { - return nil, sdkerrors.Wrap(err, "osmo pool state query") - } - - assets := ConvertSdkCoinsToWasmCoins(state.Assets) - shares := ConvertSdkCoinToWasmCoin(state.Shares) - - res := bindings.PoolStateResponse{ - Assets: assets, - Shares: shares, - } - - bz, err := json.Marshal(res) - if err != nil { - return nil, sdkerrors.Wrap(err, "osmo pool state query response") - } - - return bz, nil - - case contractQuery.SpotPrice != nil: - spotPrice, err := qp.GetSpotPrice(ctx, contractQuery.SpotPrice) - if err != nil { - return nil, sdkerrors.Wrap(err, "osmo spot price query") - } - - res := bindings.SpotPriceResponse{Price: spotPrice.String()} - bz, err := json.Marshal(res) - if err != nil { - return nil, sdkerrors.Wrap(err, "osmo spot price query response") - } - - return bz, nil - - case contractQuery.EstimateSwap != nil: - swapAmount, err := qp.EstimateSwap(ctx, contractQuery.EstimateSwap) - if err != nil { - return nil, sdkerrors.Wrap(err, "osmo estimate swap query") - } - - res := bindings.EstimatePriceResponse{Amount: *swapAmount} - bz, err := json.Marshal(res) - if err != nil { - return nil, sdkerrors.Wrap(err, "osmo estimate swap query response") - } - - return bz, nil - default: return nil, wasmvmtypes.UnsupportedRequest{Kind: "unknown osmosis query variant"} } diff --git a/wasmbinding/test/custom_msg_test.go b/wasmbinding/test/custom_msg_test.go index 9b49239c8e9..cf9e46dca35 100644 --- a/wasmbinding/test/custom_msg_test.go +++ b/wasmbinding/test/custom_msg_test.go @@ -240,381 +240,6 @@ type BaseState struct { RegenPool uint64 } -func TestSwapMsg(t *testing.T) { - // table tests with this setup - cases := []struct { - name string - msg func(BaseState) *bindings.SwapMsg - expectErr bool - initFunds sdk.Coin - finalFunds []sdk.Coin - }{ - { - name: "exact in: simple swap works", - msg: func(state BaseState) *bindings.SwapMsg { - return &bindings.SwapMsg{ - First: bindings.Swap{ - PoolId: state.StarPool, - DenomIn: "uosmo", - DenomOut: "ustar", - }, - // Note: you must use empty array, not nil, for valid Rust JSON - Route: []bindings.Step{}, - Amount: bindings.SwapAmountWithLimit{ - ExactIn: &bindings.ExactIn{ - Input: sdk.NewInt(12000000), - MinOutput: sdk.NewInt(5000000), - }, - }, - } - }, - initFunds: sdk.NewInt64Coin("uosmo", 13000000), - finalFunds: []sdk.Coin{ - sdk.NewInt64Coin("uosmo", 1000000), - sdk.NewInt64Coin("ustar", 120000000), - }, - }, - { - name: "exact in: price too low", - msg: func(state BaseState) *bindings.SwapMsg { - return &bindings.SwapMsg{ - First: bindings.Swap{ - PoolId: state.StarPool, - DenomIn: "uosmo", - DenomOut: "ustar", - }, - // Note: you must use empty array, not nil, for valid Rust JSON - Route: []bindings.Step{}, - Amount: bindings.SwapAmountWithLimit{ - ExactIn: &bindings.ExactIn{ - Input: sdk.NewInt(12000000), - MinOutput: sdk.NewInt(555000000), - }, - }, - } - }, - initFunds: sdk.NewInt64Coin("uosmo", 13000000), - expectErr: true, - }, - { - name: "exact in: not enough funds to swap", - msg: func(state BaseState) *bindings.SwapMsg { - return &bindings.SwapMsg{ - First: bindings.Swap{ - PoolId: state.StarPool, - DenomIn: "uosmo", - DenomOut: "ustar", - }, - // Note: you must use empty array, not nil, for valid Rust JSON - Route: []bindings.Step{}, - Amount: bindings.SwapAmountWithLimit{ - ExactIn: &bindings.ExactIn{ - Input: sdk.NewInt(12000000), - MinOutput: sdk.NewInt(5000000), - }, - }, - } - }, - initFunds: sdk.NewInt64Coin("uosmo", 7000000), - expectErr: true, - }, - { - name: "exact in: invalidPool", - msg: func(state BaseState) *bindings.SwapMsg { - return &bindings.SwapMsg{ - First: bindings.Swap{ - PoolId: state.StarPool, - DenomIn: "uosmo", - DenomOut: "uatom", - }, - // Note: you must use empty array, not nil, for valid Rust JSON - Route: []bindings.Step{}, - Amount: bindings.SwapAmountWithLimit{ - ExactIn: &bindings.ExactIn{ - Input: sdk.NewInt(12000000), - MinOutput: sdk.NewInt(100000), - }, - }, - } - }, - initFunds: sdk.NewInt64Coin("uosmo", 13000000), - expectErr: true, - }, - - // FIXME: this panics in GAMM module !?! hits a known TODO - // https://github.com/osmosis-labs/osmosis/blob/a380ab2fcd39fb94c2b10411e07daf664911257a/osmomath/math.go#L47-L51 - //"exact out: panics if too much swapped": { - // msg: func(state BaseState) *bindings.SwapMsg { - // return &bindings.SwapMsg{ - // First: bindings.Swap{ - // PoolId: state.StarPool, - // DenomIn: "uosmo", - // DenomOut: "ustar", - // }, - // // Note: you must use empty array, not nil, for valid Rust JSON - // Route: []bindings.Step{}, - // Amount: bindings.SwapAmountWithLimit{ - // ExactOut: &bindings.ExactOut{ - // MaxInput: sdk.NewInt(22000000), - // Output: sdk.NewInt(120000000), - // }, - // }, - // } - // }, - // initFunds: sdk.NewInt64Coin("uosmo", 15000000), - // finalFunds: []sdk.Coin{ - // sdk.NewInt64Coin("uosmo", 3000000), - // sdk.NewInt64Coin("ustar", 120000000), - // }, - //}, - { - name: "exact out: simple swap works", - msg: func(state BaseState) *bindings.SwapMsg { - return &bindings.SwapMsg{ - First: bindings.Swap{ - PoolId: state.AtomPool, - DenomIn: "uosmo", - DenomOut: "uatom", - }, - // Note: you must use empty array, not nil, for valid Rust JSON - Route: []bindings.Step{}, - Amount: bindings.SwapAmountWithLimit{ - ExactOut: &bindings.ExactOut{ - // 12 OSMO * 6 ATOM == 18 OSMO * 4 ATOM (+6 OSMO, -2 ATOM) - MaxInput: sdk.NewInt(7000000), - Output: sdk.NewInt(2000000), - }, - }, - } - }, - initFunds: sdk.NewInt64Coin("uosmo", 8000000), - finalFunds: []sdk.Coin{ - sdk.NewInt64Coin("uatom", 2000000), - sdk.NewInt64Coin("uosmo", 2000000), - }, - }, - { - name: "exact in: 2 step multi-hop", - msg: func(state BaseState) *bindings.SwapMsg { - return &bindings.SwapMsg{ - First: bindings.Swap{ - PoolId: state.StarPool, - DenomIn: "ustar", - DenomOut: "uosmo", - }, - Route: []bindings.Step{{ - PoolId: state.AtomPool, - DenomOut: "uatom", - }}, - Amount: bindings.SwapAmountWithLimit{ - ExactIn: &bindings.ExactIn{ - Input: sdk.NewInt(240000000), - MinOutput: sdk.NewInt(1999000), - }, - }, - } - }, - initFunds: sdk.NewInt64Coin("ustar", 240000000), - finalFunds: []sdk.Coin{ - // 240 STAR -> 6 OSMO - // 6 OSMO -> 2 ATOM (with minor rounding) - sdk.NewInt64Coin("uatom", 1999999), - }, - }, - { - name: "exact out: 2 step multi-hop", - msg: func(state BaseState) *bindings.SwapMsg { - return &bindings.SwapMsg{ - First: bindings.Swap{ - PoolId: state.AtomPool, - DenomIn: "uosmo", - DenomOut: "uatom", - }, - Route: []bindings.Step{{ - PoolId: state.RegenPool, - DenomOut: "uregen", - }}, - Amount: bindings.SwapAmountWithLimit{ - ExactOut: &bindings.ExactOut{ - MaxInput: sdk.NewInt(2000000), - Output: sdk.NewInt(12000000 - 12), - }, - }, - } - }, - initFunds: sdk.NewInt64Coin("uosmo", 2000000), - finalFunds: []sdk.Coin{ - // 2 OSMO -> 1.2 ATOM - // 1.2 ATOM -> 12 REGEN (with minor rounding) - sdk.NewInt64Coin("uosmo", 2), - sdk.NewInt64Coin("uregen", 12000000-12), - }, - }, - // FIXME: this panics in GAMM module !?! hits a known TODO - // https://github.com/osmosis-labs/osmosis/blob/a380ab2fcd39fb94c2b10411e07daf664911257a/osmomath/math.go#L47-L51 - // { - // name: "exact out: panics on math power stuff", - // msg: func(state BaseState) *bindings.SwapMsg { - // return &bindings.SwapMsg{ - // First: bindings.Swap{ - // PoolId: state.StarPool, - // DenomIn: "ustar", - // DenomOut: "uosmo", - // }, - // Route: []bindings.Step{{ - // PoolId: state.AtomPool, - // DenomOut: "uatom", - // }}, - // Amount: bindings.SwapAmountWithLimit{ - // ExactOut: &bindings.ExactOut{ - // MaxInput: sdk.NewInt(240005000), - // Output: sdk.NewInt(2000000), - // }, - // }, - // } - // }, - // initFunds: sdk.NewInt64Coin("ustar", 240005000), - // finalFunds: []sdk.Coin{ - // // 240 STAR -> 6 OSMO - // // 6 OSMO -> 2 ATOM (with minor rounding) - // sdk.NewInt64Coin("uatom", 2000000), - // sdk.NewInt64Coin("ustar", 5000), - // }, - // }, - { - name: "exact in: 3 step multi-hop", - msg: func(state BaseState) *bindings.SwapMsg { - return &bindings.SwapMsg{ - First: bindings.Swap{ - PoolId: state.StarPool, - DenomIn: "ustar", - DenomOut: "uosmo", - }, - Route: []bindings.Step{{ - PoolId: state.AtomPool, - DenomOut: "uatom", - }, { - PoolId: state.RegenPool, - DenomOut: "uregen", - }}, - Amount: bindings.SwapAmountWithLimit{ - ExactIn: &bindings.ExactIn{ - Input: sdk.NewInt(240000000), - MinOutput: sdk.NewInt(23900000), - }, - }, - } - }, - initFunds: sdk.NewInt64Coin("ustar", 240000000), - finalFunds: []sdk.Coin{ - // 240 STAR -> 6 OSMO - // 6 OSMO -> 2 ATOM - // 2 ATOM -> 24 REGEN (with minor rounding) - sdk.NewInt64Coin("uregen", 23999990), - }, - }, - { - name: "exact out: 3 step multi-hop", - msg: func(state BaseState) *bindings.SwapMsg { - return &bindings.SwapMsg{ - First: bindings.Swap{ - PoolId: state.StarPool, - DenomIn: "ustar", - DenomOut: "uosmo", - }, - Route: []bindings.Step{{ - PoolId: state.AtomPool, - DenomOut: "uatom", - }, { - PoolId: state.RegenPool, - DenomOut: "uregen", - }}, - Amount: bindings.SwapAmountWithLimit{ - ExactOut: &bindings.ExactOut{ - MaxInput: sdk.NewInt(50000000), - Output: sdk.NewInt(12000000), - }, - }, - } - }, - initFunds: sdk.NewInt64Coin("ustar", 50000000), - finalFunds: []sdk.Coin{ - // ~48 STAR -> 2 OSMO - // 2 OSMO -> .857 ATOM - // .857 ATOM -> 12 REGEN (with minor rounding) - sdk.NewInt64Coin("uregen", 12000000), - sdk.NewInt64Coin("ustar", 1999971), - }, - }, - } - for _, tc := range cases { - tc := tc - t.Run(tc.name, func(t *testing.T) { - creator := RandomAccountAddress() - osmosis, ctx := SetupCustomApp(t, creator) - state := prepareSwapState(t, ctx, osmosis) - - trader := RandomAccountAddress() - fundAccount(t, ctx, osmosis, trader, []sdk.Coin{tc.initFunds}) - reflect := instantiateReflectContract(t, ctx, osmosis, trader) - require.NotEmpty(t, reflect) - - msg := bindings.OsmosisMsg{Swap: tc.msg(state)} - err := executeCustom(t, ctx, osmosis, reflect, trader, msg, tc.initFunds) - if tc.expectErr { - require.Error(t, err) - } else { - require.NoError(t, err) - balances := osmosis.BankKeeper.GetAllBalances(ctx, reflect) - // uncomment these to debug any confusing results (show balances, not (*big.Int)(0x140005e51e0)) - // fmt.Printf("Expected: %s\n", tc.finalFunds) - // fmt.Printf("Got: %s\n", balances) - require.EqualValues(t, tc.finalFunds, balances) - } - }) - } -} - -// test setup for each run through the table test above -func prepareSwapState(t *testing.T, ctx sdk.Context, osmosis *app.OsmosisApp) BaseState { - actor := RandomAccountAddress() - - swapperFunds := sdk.NewCoins( - sdk.NewInt64Coin("uatom", 333000000), - sdk.NewInt64Coin("uosmo", 555000000+3*poolFee), - sdk.NewInt64Coin("uregen", 777000000), - sdk.NewInt64Coin("ustar", 999000000), - ) - fundAccount(t, ctx, osmosis, actor, swapperFunds) - - // 20 star to 1 osmo - funds1 := []sdk.Coin{ - sdk.NewInt64Coin("uosmo", 12000000), - sdk.NewInt64Coin("ustar", 240000000), - } - starPool := preparePool(t, ctx, osmosis, actor, funds1) - - // 2 osmo to 1 atom - funds2 := []sdk.Coin{ - sdk.NewInt64Coin("uatom", 6000000), - sdk.NewInt64Coin("uosmo", 12000000), - } - atomPool := preparePool(t, ctx, osmosis, actor, funds2) - - // 16 regen to 1 atom - funds3 := []sdk.Coin{ - sdk.NewInt64Coin("uatom", 6000000), - sdk.NewInt64Coin("uregen", 96000000), - } - regenPool := preparePool(t, ctx, osmosis, actor, funds3) - - return BaseState{ - StarPool: starPool, - AtomPool: atomPool, - RegenPool: regenPool, - } -} - type ReflectExec struct { ReflectMsg *ReflectMsgs `json:"reflect_msg,omitempty"` ReflectSubMsg *ReflectSubMsgs `json:"reflect_sub_msg,omitempty"` diff --git a/wasmbinding/test/custom_query_test.go b/wasmbinding/test/custom_query_test.go index 639889f03cb..1f9a93cd6cd 100644 --- a/wasmbinding/test/custom_query_test.go +++ b/wasmbinding/test/custom_query_test.go @@ -4,7 +4,6 @@ import ( "encoding/json" "fmt" "os" - "strconv" "testing" "github.com/stretchr/testify/require" @@ -16,7 +15,6 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/osmosis-labs/osmosis/v13/app" - "github.com/osmosis-labs/osmosis/v13/wasmbinding" "github.com/osmosis-labs/osmosis/v13/wasmbinding/bindings" "github.com/osmosis-labs/osmosis/v13/x/gamm/pool-models/balancer" ) @@ -63,200 +61,6 @@ func TestQueryFullDenom(t *testing.T) { require.EqualValues(t, expected, resp.Denom) } -func TestQueryPool(t *testing.T) { - actor := RandomAccountAddress() - osmosis, ctx := SetupCustomApp(t, actor) - - fundAccount(t, ctx, osmosis, actor, defaultFunds) - - poolFunds := []sdk.Coin{ - sdk.NewInt64Coin("uosmo", 12000000), - sdk.NewInt64Coin("ustar", 240000000), - } - // 2 star to 1 osmo - starPool := preparePool(t, ctx, osmosis, actor, poolFunds) - - pool2Funds := []sdk.Coin{ - sdk.NewInt64Coin("uatom", 6000000), - sdk.NewInt64Coin("uosmo", 12000000), - } - // 2 star to 1 osmo - atomPool := preparePool(t, ctx, osmosis, actor, pool2Funds) - - reflect := instantiateReflectContract(t, ctx, osmosis, actor) - require.NotEmpty(t, reflect) - - // query pool state - query := bindings.OsmosisQuery{ - PoolState: &bindings.PoolState{PoolId: starPool}, - } - resp := bindings.PoolStateResponse{} - queryCustom(t, ctx, osmosis, reflect, query, &resp) - expected := wasmbinding.ConvertSdkCoinsToWasmCoins(poolFunds) - require.EqualValues(t, expected, resp.Assets) - assertValidShares(t, resp.Shares, starPool) - - // query second pool state - query = bindings.OsmosisQuery{ - PoolState: &bindings.PoolState{PoolId: atomPool}, - } - resp = bindings.PoolStateResponse{} - queryCustom(t, ctx, osmosis, reflect, query, &resp) - expected = wasmbinding.ConvertSdkCoinsToWasmCoins(pool2Funds) - require.EqualValues(t, expected, resp.Assets) - assertValidShares(t, resp.Shares, atomPool) -} - -func TestQuerySpotPrice(t *testing.T) { - actor := RandomAccountAddress() - osmosis, ctx := SetupCustomApp(t, actor) - swapFee := 0. // FIXME: Set / support an actual fee - epsilon := 1e-6 - - fundAccount(t, ctx, osmosis, actor, defaultFunds) - - poolFunds := []sdk.Coin{ - sdk.NewInt64Coin("uosmo", 12000000), - sdk.NewInt64Coin("ustar", 240000000), - } - // 20 star to 1 osmo - starPool := preparePool(t, ctx, osmosis, actor, poolFunds) - - reflect := instantiateReflectContract(t, ctx, osmosis, actor) - require.NotEmpty(t, reflect) - - // query spot price - query := bindings.OsmosisQuery{ - SpotPrice: &bindings.SpotPrice{ - Swap: bindings.Swap{ - PoolId: starPool, - DenomIn: "ustar", - DenomOut: "uosmo", - }, - WithSwapFee: false, - }, - } - resp := bindings.SpotPriceResponse{} - queryCustom(t, ctx, osmosis, reflect, query, &resp) - - price, err := strconv.ParseFloat(resp.Price, 64) - require.NoError(t, err) - - uosmo, err := poolFunds[0].Amount.ToDec().Float64() - require.NoError(t, err) - ustar, err := poolFunds[1].Amount.ToDec().Float64() - require.NoError(t, err) - - expected := ustar / uosmo - require.InEpsilonf(t, expected, price, epsilon, fmt.Sprintf("Outside of tolerance (%f)", epsilon)) - - // and the reverse conversion (with swap fee) - // query spot price - query = bindings.OsmosisQuery{ - SpotPrice: &bindings.SpotPrice{ - Swap: bindings.Swap{ - PoolId: starPool, - DenomIn: "uosmo", - DenomOut: "ustar", - }, - WithSwapFee: true, - }, - } - resp = bindings.SpotPriceResponse{} - queryCustom(t, ctx, osmosis, reflect, query, &resp) - - price, err = strconv.ParseFloat(resp.Price, 32) - require.NoError(t, err) - - expected = 1. / expected - require.InEpsilonf(t, expected+swapFee, price, epsilon, fmt.Sprintf("Outside of tolerance (%f)", epsilon)) -} - -func TestQueryEstimateSwap(t *testing.T) { - actor := RandomAccountAddress() - osmosis, ctx := SetupCustomApp(t, actor) - epsilon := 2e-3 - - fundAccount(t, ctx, osmosis, actor, defaultFunds) - - poolFunds := []sdk.Coin{ - sdk.NewInt64Coin("uosmo", 12000000), - sdk.NewInt64Coin("ustar", 240000000), - } - // 2 star to 1 osmo - starPool := preparePool(t, ctx, osmosis, actor, poolFunds) - - reflect := instantiateReflectContract(t, ctx, osmosis, actor) - require.NotEmpty(t, reflect) - - // The contract/sender needs to have funds for estimating the price - fundAccount(t, ctx, osmosis, reflect, defaultFunds) - - // Estimate swap rate - uosmo, err := poolFunds[0].Amount.ToDec().Float64() - require.NoError(t, err) - ustar, err := poolFunds[1].Amount.ToDec().Float64() - require.NoError(t, err) - swapRate := ustar / uosmo - - // Query estimate cost (Exact in. No route) - amountIn := sdk.NewInt(10000) - query := bindings.OsmosisQuery{ - EstimateSwap: &bindings.EstimateSwap{ - Sender: reflect.String(), - First: bindings.Swap{ - PoolId: starPool, - DenomIn: "uosmo", - DenomOut: "ustar", - }, - Route: []bindings.Step{}, - Amount: bindings.SwapAmount{ - In: &amountIn, - }, - }, - } - resp := bindings.EstimatePriceResponse{} - queryCustom(t, ctx, osmosis, reflect, query, &resp) - require.NotNil(t, resp.Amount.Out) - require.Nil(t, resp.Amount.In) - cost, err := (*resp.Amount.Out).ToDec().Float64() - require.NoError(t, err) - - amount, err := amountIn.ToDec().Float64() - require.NoError(t, err) - expected := amount * swapRate // out - require.InEpsilonf(t, expected, cost, epsilon, fmt.Sprintf("Outside of tolerance (%f)", epsilon)) - - // And the other way around - // Query estimate cost (Exact out. No route) - amountOut := sdk.NewInt(10000) - query = bindings.OsmosisQuery{ - EstimateSwap: &bindings.EstimateSwap{ - Sender: reflect.String(), - First: bindings.Swap{ - PoolId: starPool, - DenomIn: "uosmo", - DenomOut: "ustar", - }, - Route: []bindings.Step{}, - Amount: bindings.SwapAmount{ - Out: &amountOut, - }, - }, - } - resp = bindings.EstimatePriceResponse{} - queryCustom(t, ctx, osmosis, reflect, query, &resp) - require.NotNil(t, resp.Amount.In) - require.Nil(t, resp.Amount.Out) - cost, err = (*resp.Amount.In).ToDec().Float64() - require.NoError(t, err) - - amount, err = amountOut.ToDec().Float64() - require.NoError(t, err) - expected = amount * 1. / swapRate - require.InEpsilonf(t, expected, cost, epsilon, fmt.Sprintf("Outside of tolerance (%f)", epsilon)) -} - type ReflectQuery struct { Chain *ChainRequest `json:"chain,omitempty"` } diff --git a/wasmbinding/test/messages_test.go b/wasmbinding/test/messages_test.go index 1851cff40f5..7db69dcc92f 100644 --- a/wasmbinding/test/messages_test.go +++ b/wasmbinding/test/messages_test.go @@ -2,7 +2,6 @@ package wasmbinding import ( "fmt" - "math" "testing" sdk "github.com/cosmos/cosmos-sdk/types" @@ -11,7 +10,6 @@ import ( "github.com/osmosis-labs/osmosis/v13/wasmbinding/bindings" "github.com/osmosis-labs/osmosis/v13/x/tokenfactory/types" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -414,418 +412,3 @@ func TestBurn(t *testing.T) { }) } } - -func TestSwap(t *testing.T) { - actor := RandomAccountAddress() - osmosis, ctx := SetupCustomApp(t, actor) - epsilon := 1e-3 - - fundAccount(t, ctx, osmosis, actor, defaultFunds) - - poolFunds := []sdk.Coin{ - sdk.NewInt64Coin("uosmo", 12_000_000), - sdk.NewInt64Coin("ustar", 240_000_000), - } - // 20 star to 1 osmo - starPool := preparePool(t, ctx, osmosis, actor, poolFunds) - - // Estimate swap rate - uosmo := poolFunds[0].Amount.ToDec().MustFloat64() - ustar := poolFunds[1].Amount.ToDec().MustFloat64() - swapRate := ustar / uosmo - - amountIn := bindings.ExactIn{ - Input: sdk.NewInt(10000), - MinOutput: sdk.OneInt(), - } - zeroAmountIn := amountIn - zeroAmountIn.Input = sdk.ZeroInt() - negativeAmountIn := amountIn - negativeAmountIn.Input = negativeAmountIn.Input.Neg() - - amountOut := bindings.ExactOut{ - MaxInput: sdk.NewInt(math.MaxInt64), - Output: sdk.NewInt(10000), - } - zeroAmountOut := amountOut - zeroAmountOut.Output = sdk.ZeroInt() - negativeAmountOut := amountOut - negativeAmountOut.Output = negativeAmountOut.Output.Neg() - - amount := amountIn.Input.ToDec().MustFloat64() - starAmount := sdk.NewInt(int64(amount * swapRate)) - - starSwapAmount := bindings.SwapAmount{Out: &starAmount} - - specs := map[string]struct { - swap *bindings.SwapMsg - expCost *bindings.SwapAmount - expErr bool - }{ - "valid swap (exact in)": { - swap: &bindings.SwapMsg{ - First: bindings.Swap{ - PoolId: starPool, - DenomIn: "uosmo", - DenomOut: "ustar", - }, - Route: nil, - Amount: bindings.SwapAmountWithLimit{ - ExactIn: &amountIn, - }, - }, - expCost: &starSwapAmount, - }, - "non-existent pool id": { - swap: &bindings.SwapMsg{ - First: bindings.Swap{ - PoolId: starPool + 4, - DenomIn: "uosmo", - DenomOut: "ustar", - }, - Route: nil, - Amount: bindings.SwapAmountWithLimit{ - ExactIn: &amountIn, - }, - }, - expErr: true, - }, - "zero pool id": { - swap: &bindings.SwapMsg{ - First: bindings.Swap{ - PoolId: 0, - DenomIn: "uosmo", - DenomOut: "ustar", - }, - Route: nil, - Amount: bindings.SwapAmountWithLimit{ - ExactIn: &amountIn, - }, - }, - expErr: true, - }, - "invalid denom in": { - swap: &bindings.SwapMsg{ - First: bindings.Swap{ - PoolId: starPool, - DenomIn: "invalid", - DenomOut: "ustar", - }, - Route: nil, - Amount: bindings.SwapAmountWithLimit{ - ExactIn: &amountIn, - }, - }, - expErr: true, - }, - "empty denom in": { - swap: &bindings.SwapMsg{ - First: bindings.Swap{ - PoolId: starPool, - DenomIn: "", - DenomOut: "ustar", - }, - Route: nil, - Amount: bindings.SwapAmountWithLimit{ - ExactIn: &amountIn, - }, - }, - expErr: true, - }, - "invalid denom out": { - swap: &bindings.SwapMsg{ - First: bindings.Swap{ - PoolId: starPool, - DenomIn: "ustar", - DenomOut: "invalid", - }, - Route: nil, - Amount: bindings.SwapAmountWithLimit{ - ExactIn: &amountIn, - }, - }, - expErr: true, - }, - "empty denom out": { - swap: &bindings.SwapMsg{ - First: bindings.Swap{ - PoolId: starPool, - DenomIn: "ustar", - DenomOut: "", - }, - Route: nil, - Amount: bindings.SwapAmountWithLimit{ - ExactIn: &amountIn, - }, - }, - expErr: true, - }, - "null swap": { - swap: nil, - expErr: true, - }, - "empty swap amount": { - swap: &bindings.SwapMsg{ - First: bindings.Swap{ - PoolId: starPool, - DenomIn: "ustar", - DenomOut: "", - }, - Route: nil, - Amount: bindings.SwapAmountWithLimit{}, - }, - expErr: true, - }, - "zero amount in": { - swap: &bindings.SwapMsg{ - First: bindings.Swap{ - PoolId: starPool, - DenomIn: "uosmo", - DenomOut: "ustar", - }, - Route: nil, - Amount: bindings.SwapAmountWithLimit{ - ExactIn: &zeroAmountIn, - }, - }, - expErr: true, - }, - "zero amount out": { - swap: &bindings.SwapMsg{ - First: bindings.Swap{ - PoolId: starPool, - DenomIn: "uosmo", - DenomOut: "ustar", - }, - Route: nil, - Amount: bindings.SwapAmountWithLimit{ - ExactOut: &zeroAmountOut, - }, - }, - expErr: true, - }, - "negative amount in": { - swap: &bindings.SwapMsg{ - First: bindings.Swap{ - PoolId: starPool, - DenomIn: "uosmo", - DenomOut: "ustar", - }, - Route: nil, - Amount: bindings.SwapAmountWithLimit{ - ExactIn: &negativeAmountIn, - }, - }, - expErr: true, - }, - "negative amount out": { - swap: &bindings.SwapMsg{ - First: bindings.Swap{ - PoolId: starPool, - DenomIn: "uosmo", - DenomOut: "ustar", - }, - Route: nil, - Amount: bindings.SwapAmountWithLimit{ - ExactOut: &negativeAmountOut, - }, - }, - expErr: true, - }, - } - for name, spec := range specs { - t.Run(name, func(t *testing.T) { - // when - gotAmount, gotErr := wasmbinding.PerformSwap(osmosis.GAMMKeeper, ctx, actor, spec.swap) - // then - if spec.expErr { - require.Error(t, gotErr) - return - } - require.NoError(t, gotErr) - assert.InEpsilonf(t, (*spec.expCost.Out).ToDec().MustFloat64(), (*gotAmount.Out).ToDec().MustFloat64(), epsilon, "exp %s but got %s", spec.expCost.Out.String(), gotAmount.Out.String()) - }) - } -} - -func TestSwapMultiHop(t *testing.T) { - actor := RandomAccountAddress() - osmosis, ctx := SetupCustomApp(t, actor) - epsilon := 1e-3 - - fundAccount(t, ctx, osmosis, actor, defaultFunds) - - poolFunds := []sdk.Coin{ - sdk.NewInt64Coin("uosmo", 12_000_000), - sdk.NewInt64Coin("ustar", 240_000_000), - } - // 20 star to 1 osmo - starPool := preparePool(t, ctx, osmosis, actor, poolFunds) - - // 2 osmo to 1 atom - poolFunds2 := []sdk.Coin{ - sdk.NewInt64Coin("uatom", 6_000_000), - sdk.NewInt64Coin("uosmo", 12_000_000), - } - atomPool := preparePool(t, ctx, osmosis, actor, poolFunds2) - - amountIn := bindings.ExactIn{ - Input: sdk.NewInt(1_000_000), - MinOutput: sdk.NewInt(20_000), - } - - // Multi-hop - // Estimate 1st swap rate - uosmo := poolFunds[0].Amount.ToDec().MustFloat64() - ustar := poolFunds[1].Amount.ToDec().MustFloat64() - expectedOut1 := uosmo - uosmo*ustar/(ustar+amountIn.Input.ToDec().MustFloat64()) - - // Estimate 2nd swap rate - uatom2 := poolFunds2[0].Amount.ToDec().MustFloat64() - uosmo2 := poolFunds2[1].Amount.ToDec().MustFloat64() - expectedOut2 := uatom2 - uosmo2*uatom2/(uosmo2+expectedOut1) - - atomAmount := sdk.NewInt(int64(expectedOut2)) - atomSwapAmount := bindings.SwapAmount{Out: &atomAmount} - - specs := map[string]struct { - swap *bindings.SwapMsg - expCost *bindings.SwapAmount - expErr bool - }{ - "valid swap (exact in, 2 step multi-hop)": { - swap: &bindings.SwapMsg{ - First: bindings.Swap{ - PoolId: starPool, - DenomIn: "ustar", - DenomOut: "uosmo", - }, - Route: []bindings.Step{{ - PoolId: atomPool, - DenomOut: "uatom", - }}, - Amount: bindings.SwapAmountWithLimit{ - ExactIn: &amountIn, - }, - }, - expCost: &atomSwapAmount, - }, - "non-existent step pool id": { - swap: &bindings.SwapMsg{ - First: bindings.Swap{ - PoolId: starPool, - DenomIn: "ustar", - DenomOut: "uosmo", - }, - Route: []bindings.Step{{ - PoolId: atomPool + 2, - DenomOut: "uatom", - }}, - Amount: bindings.SwapAmountWithLimit{ - ExactIn: &amountIn, - }, - }, - expErr: true, - }, - "zero step pool id": { - swap: &bindings.SwapMsg{ - First: bindings.Swap{ - PoolId: starPool, - DenomIn: "ustar", - DenomOut: "uosmo", - }, - Route: []bindings.Step{{ - PoolId: 0, - DenomOut: "uatom", - }}, - Amount: bindings.SwapAmountWithLimit{ - ExactIn: &amountIn, - }, - }, - expErr: true, - }, - "wrong step denom out": { - swap: &bindings.SwapMsg{ - First: bindings.Swap{ - PoolId: starPool, - DenomIn: "ustar", - DenomOut: "uosmo", - }, - Route: []bindings.Step{{ - PoolId: atomPool, - DenomOut: "ATOM", - }}, - Amount: bindings.SwapAmountWithLimit{ - ExactIn: &amountIn, - }, - }, - expErr: true, - }, - "self-swap not allowed": { - swap: &bindings.SwapMsg{ - First: bindings.Swap{ - PoolId: starPool, - DenomIn: "ustar", - DenomOut: "uosmo", - }, - Route: []bindings.Step{{ - PoolId: atomPool, - DenomOut: "uosmo", // this is same as the input (output of first swap) - }}, - Amount: bindings.SwapAmountWithLimit{ - ExactIn: &amountIn, - }, - }, - expErr: true, - }, - "invalid step denom out": { - swap: &bindings.SwapMsg{ - First: bindings.Swap{ - PoolId: starPool, - DenomIn: "ustar", - DenomOut: "uosmo", - }, - Route: []bindings.Step{{ - PoolId: atomPool, - DenomOut: "invalid", - }}, - Amount: bindings.SwapAmountWithLimit{ - ExactIn: &amountIn, - }, - }, - expErr: true, - }, - "empty step denom out": { - swap: &bindings.SwapMsg{ - First: bindings.Swap{ - PoolId: starPool, - DenomIn: "ustar", - DenomOut: "uosmo", - }, - Route: []bindings.Step{{ - PoolId: atomPool, - DenomOut: "", - }}, - Amount: bindings.SwapAmountWithLimit{ - ExactIn: &amountIn, - }, - }, - expErr: true, - }, - } - for name, spec := range specs { - t.Run(name, func(t *testing.T) { - // use scratch context to avoid interference between tests - subCtx, _ := ctx.CacheContext() - // when - gotAmount, gotErr := wasmbinding.PerformSwap(osmosis.GAMMKeeper, subCtx, actor, spec.swap) - // then - if spec.expErr { - require.Error(t, gotErr) - return - } - require.NoError(t, gotErr) - assert.InEpsilonf(t, (*spec.expCost.Out).ToDec().MustFloat64(), (*gotAmount.Out).ToDec().MustFloat64(), epsilon, "exp %s but got %s", spec.expCost.Out.String(), gotAmount.Out.String()) - }) - } -} diff --git a/wasmbinding/test/queries_test.go b/wasmbinding/test/queries_test.go index 334418a17d6..90b05128838 100644 --- a/wasmbinding/test/queries_test.go +++ b/wasmbinding/test/queries_test.go @@ -10,7 +10,6 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/osmosis-labs/osmosis/v13/wasmbinding" - "github.com/osmosis-labs/osmosis/v13/wasmbinding/bindings" ) func TestFullDenom(t *testing.T) { @@ -78,7 +77,7 @@ func TestDenomAdmin(t *testing.T) { require.NoError(t, err) require.NotEmpty(t, tfDenom) - queryPlugin := wasmbinding.NewQueryPlugin(app.GAMMKeeper, app.TwapKeeper, app.TokenFactoryKeeper) + queryPlugin := wasmbinding.NewQueryPlugin(app.TokenFactoryKeeper) testCases := []struct { name string @@ -114,428 +113,3 @@ func TestDenomAdmin(t *testing.T) { }) } } - -func TestPoolState(t *testing.T) { - actor := RandomAccountAddress() - osmosis, ctx := SetupCustomApp(t, actor) - - fundAccount(t, ctx, osmosis, actor, defaultFunds) - - poolFunds := []sdk.Coin{ - sdk.NewInt64Coin("uosmo", 12000000), - sdk.NewInt64Coin("ustar", 240000000), - } - // 20 star to 1 osmo - starPool := preparePool(t, ctx, osmosis, actor, poolFunds) - - // FIXME: Derive / obtain these values - starSharesDenom := fmt.Sprintf("gamm/pool/%d", starPool) - starSharedAmount, _ := sdk.NewIntFromString("100_000_000_000_000_000_000") - - queryPlugin := wasmbinding.NewQueryPlugin(osmosis.GAMMKeeper, osmosis.TwapKeeper, osmosis.TokenFactoryKeeper) - - specs := map[string]struct { - poolId uint64 - expPoolState *bindings.PoolAssets - expErr bool - }{ - "existent pool id": { - poolId: starPool, - expPoolState: &bindings.PoolAssets{ - Assets: poolFunds, - Shares: sdk.NewCoin(starSharesDenom, starSharedAmount), - }, - }, - "non-existent pool id": { - poolId: starPool + 1, - expErr: true, - }, - "zero pool id": { - poolId: 0, - expErr: true, - }, - } - for name, spec := range specs { - t.Run(name, func(t *testing.T) { - // when - gotPoolState, gotErr := queryPlugin.GetPoolState(ctx, spec.poolId) - // then - if spec.expErr { - require.Error(t, gotErr) - return - } - require.NoError(t, gotErr) - assert.Equal(t, spec.expPoolState, gotPoolState, "exp %s but got %s", spec.expPoolState, gotPoolState) - }) - } -} - -func TestSpotPrice(t *testing.T) { - actor := RandomAccountAddress() - swapFee := 0. // FIXME: Set / support an actual fee - epsilon := 1e-6 - osmosis, ctx := SetupCustomApp(t, actor) - - fundAccount(t, ctx, osmosis, actor, defaultFunds) - - poolFunds := []sdk.Coin{ - sdk.NewInt64Coin("uosmo", 12000000), - sdk.NewInt64Coin("ustar", 240000000), - } - // 20 star to 1 osmo - starPool := preparePool(t, ctx, osmosis, actor, poolFunds) - - uosmo := poolFunds[0].Amount.ToDec().MustFloat64() - ustar := poolFunds[1].Amount.ToDec().MustFloat64() - - starPrice := sdk.MustNewDecFromStr(fmt.Sprintf("%f", uosmo/ustar)) - starFee := sdk.MustNewDecFromStr(fmt.Sprintf("%f", swapFee)) - starPriceWithFee := starPrice.Add(starFee) - - queryPlugin := wasmbinding.NewQueryPlugin(osmosis.GAMMKeeper, osmosis.TwapKeeper, osmosis.TokenFactoryKeeper) - - specs := map[string]struct { - spotPrice *bindings.SpotPrice - expPrice *sdk.Dec - expErr bool - }{ - "valid spot price": { - spotPrice: &bindings.SpotPrice{ - Swap: bindings.Swap{ - PoolId: starPool, - DenomIn: "uosmo", - DenomOut: "ustar", - }, - WithSwapFee: false, - }, - expPrice: &starPrice, - }, - "valid spot price with fee": { - spotPrice: &bindings.SpotPrice{ - Swap: bindings.Swap{ - PoolId: starPool, - DenomIn: "uosmo", - DenomOut: "ustar", - }, - WithSwapFee: true, - }, - expPrice: &starPriceWithFee, - }, - "non-existent pool id": { - spotPrice: &bindings.SpotPrice{ - Swap: bindings.Swap{ - PoolId: starPool + 2, - DenomIn: "uosmo", - DenomOut: "ustar", - }, - WithSwapFee: false, - }, - expErr: true, - }, - "zero pool id": { - spotPrice: &bindings.SpotPrice{ - Swap: bindings.Swap{ - PoolId: 0, - DenomIn: "uosmo", - DenomOut: "ustar", - }, - WithSwapFee: false, - }, - expErr: true, - }, - "invalid denom in": { - spotPrice: &bindings.SpotPrice{ - Swap: bindings.Swap{ - PoolId: starPool, - DenomIn: "invalid", - DenomOut: "ustar", - }, - WithSwapFee: false, - }, - expErr: true, - }, - "empty denom in": { - spotPrice: &bindings.SpotPrice{ - Swap: bindings.Swap{ - PoolId: starPool, - DenomIn: "", - DenomOut: "ustar", - }, - WithSwapFee: false, - }, - expErr: true, - }, - "invalid denom out": { - spotPrice: &bindings.SpotPrice{ - Swap: bindings.Swap{ - PoolId: starPool, - DenomIn: "uosmo", - DenomOut: "invalid", - }, - WithSwapFee: false, - }, - expErr: true, - }, - "empty denom out": { - spotPrice: &bindings.SpotPrice{ - Swap: bindings.Swap{ - PoolId: starPool, - DenomIn: "uosmo", - DenomOut: "", - }, - WithSwapFee: false, - }, - expErr: true, - }, - "null spot price": { - spotPrice: nil, - expErr: true, - }, - } - for name, spec := range specs { - t.Run(name, func(t *testing.T) { - // when - gotPrice, gotErr := queryPlugin.GetSpotPrice(ctx, spec.spotPrice) - // then - if spec.expErr { - require.Error(t, gotErr) - return - } - require.NoError(t, gotErr) - assert.InEpsilonf(t, spec.expPrice.MustFloat64(), gotPrice.MustFloat64(), epsilon, "exp %s but got %s", spec.expPrice.String(), gotPrice.String()) - }) - } -} - -func TestEstimateSwap(t *testing.T) { - actor := RandomAccountAddress() - osmosis, ctx := SetupCustomApp(t, actor) - epsilon := 1e-3 - - fundAccount(t, ctx, osmosis, actor, defaultFunds) - - poolFunds := []sdk.Coin{ - sdk.NewInt64Coin("uosmo", 12000000), - sdk.NewInt64Coin("ustar", 240000000), - } - // 20 star to 1 osmo - starPool := preparePool(t, ctx, osmosis, actor, poolFunds) - - // Estimate swap rate - uosmo := poolFunds[0].Amount.ToDec().MustFloat64() - ustar := poolFunds[1].Amount.ToDec().MustFloat64() - swapRate := ustar / uosmo - - amountIn := sdk.NewInt(10000) - zeroAmount := sdk.ZeroInt() - negativeAmount := amountIn.Neg() - - amount := amountIn.ToDec().MustFloat64() - starAmount := sdk.NewInt(int64(amount * swapRate)) - - starSwapAmount := bindings.SwapAmount{Out: &starAmount} - - queryPlugin := wasmbinding.NewQueryPlugin(osmosis.GAMMKeeper, osmosis.TwapKeeper, osmosis.TokenFactoryKeeper) - - specs := map[string]struct { - estimateSwap *bindings.EstimateSwap - expCost *bindings.SwapAmount - expErr bool - }{ - "valid estimate swap (exact in)": { - estimateSwap: &bindings.EstimateSwap{ - Sender: actor.String(), - First: bindings.Swap{ - PoolId: starPool, - DenomIn: "uosmo", - DenomOut: "ustar", - }, - Route: nil, - Amount: bindings.SwapAmount{ - In: &amountIn, - }, - }, - expCost: &starSwapAmount, - }, - "non-existent pool id": { - estimateSwap: &bindings.EstimateSwap{ - Sender: actor.String(), - First: bindings.Swap{ - PoolId: starPool + 3, - DenomIn: "uosmo", - DenomOut: "ustar", - }, - Route: nil, - Amount: bindings.SwapAmount{ - In: &amountIn, - }, - }, - expErr: true, - }, - "zero pool id": { - estimateSwap: &bindings.EstimateSwap{ - Sender: actor.String(), - First: bindings.Swap{ - PoolId: 0, - DenomIn: "uosmo", - DenomOut: "ustar", - }, - Route: nil, - Amount: bindings.SwapAmount{ - In: &amountIn, - }, - }, - expErr: true, - }, - "invalid denom in": { - estimateSwap: &bindings.EstimateSwap{ - Sender: actor.String(), - First: bindings.Swap{ - PoolId: starPool, - DenomIn: "invalid", - DenomOut: "ustar", - }, - Route: nil, - Amount: bindings.SwapAmount{ - In: &amountIn, - }, - }, - expErr: true, - }, - "empty denom in": { - estimateSwap: &bindings.EstimateSwap{ - Sender: actor.String(), - First: bindings.Swap{ - PoolId: starPool, - DenomIn: "", - DenomOut: "ustar", - }, - Route: nil, - Amount: bindings.SwapAmount{ - In: &amountIn, - }, - }, - expErr: true, - }, - "invalid denom out": { - estimateSwap: &bindings.EstimateSwap{ - Sender: actor.String(), - First: bindings.Swap{ - PoolId: starPool, - DenomIn: "ustar", - DenomOut: "invalid", - }, - Route: nil, - Amount: bindings.SwapAmount{ - In: &amountIn, - }, - }, - expErr: true, - }, - "empty denom out": { - estimateSwap: &bindings.EstimateSwap{ - Sender: actor.String(), - First: bindings.Swap{ - PoolId: starPool, - DenomIn: "ustar", - DenomOut: "", - }, - Route: nil, - Amount: bindings.SwapAmount{ - In: &amountIn, - }, - }, - expErr: true, - }, - "null estimate swap": { - estimateSwap: nil, - expErr: true, - }, - "empty swap amount": { - estimateSwap: &bindings.EstimateSwap{ - Sender: actor.String(), - First: bindings.Swap{ - PoolId: starPool, - DenomIn: "uosmo", - DenomOut: "ustar", - }, - Route: nil, - Amount: bindings.SwapAmount{}, - }, - expErr: true, - }, - "zero amount in": { - estimateSwap: &bindings.EstimateSwap{ - Sender: actor.String(), - First: bindings.Swap{ - PoolId: starPool, - DenomIn: "uosmo", - DenomOut: "ustar", - }, - Route: nil, - Amount: bindings.SwapAmount{ - In: &zeroAmount, - }, - }, - expErr: true, - }, - "zero amount out": { - estimateSwap: &bindings.EstimateSwap{ - Sender: actor.String(), - First: bindings.Swap{ - PoolId: starPool, - DenomIn: "uosmo", - DenomOut: "ustar", - }, - Route: nil, - Amount: bindings.SwapAmount{ - Out: &zeroAmount, - }, - }, - expErr: true, - }, - "negative amount in": { - estimateSwap: &bindings.EstimateSwap{ - Sender: actor.String(), - First: bindings.Swap{ - PoolId: starPool, - DenomIn: "uosmo", - DenomOut: "ustar", - }, - Route: nil, - Amount: bindings.SwapAmount{ - In: &negativeAmount, - }, - }, - expErr: true, - }, - "negative amount out": { - estimateSwap: &bindings.EstimateSwap{ - Sender: actor.String(), - First: bindings.Swap{ - PoolId: starPool, - DenomIn: "uosmo", - DenomOut: "ustar", - }, - Route: nil, - Amount: bindings.SwapAmount{ - Out: &negativeAmount, - }, - }, - expErr: true, - }, - } - for name, spec := range specs { - t.Run(name, func(t *testing.T) { - // when - gotCost, gotErr := queryPlugin.EstimateSwap(ctx, spec.estimateSwap) - // then - if spec.expErr { - require.Error(t, gotErr) - return - } - require.NoError(t, gotErr) - assert.InEpsilonf(t, (*spec.expCost.Out).ToDec().MustFloat64(), (*gotCost.Out).ToDec().MustFloat64(), epsilon, "exp %s but got %s", spec.expCost.Out.String(), gotCost.Out.String()) - }) - } -} diff --git a/wasmbinding/testdata/download_releases.sh b/wasmbinding/testdata/download_releases.sh deleted file mode 100755 index 89d26ab6b55..00000000000 --- a/wasmbinding/testdata/download_releases.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash -set -o errexit -o nounset -o pipefail -command -v shellcheck > /dev/null && shellcheck "$0" - -if [ $# -ne 1 ]; then - echo "Usage: ./download_releases.sh RELEASE_TAG" - exit 1 -fi - -tag="v1.0.0-beta6" - -for contract in hackatom reflect; do - url="https://github.com/CosmWasm/cosmwasm/releases/download/$tag/${contract}.wasm" - echo "Downloading $url ..." - wget -O "${contract}.wasm" "$url" -done - -tag="$1" -url="https://github.com/osmosis-labs/osmosis-bindings/releases/download/$tag/osmo_reflect.wasm" -echo "Downloading $url ..." -wget -O "osmo_reflect.wasm" "$url" - -rm -f version.txt -echo "$tag" >version.txt \ No newline at end of file diff --git a/wasmbinding/testdata/reflect.wasm b/wasmbinding/testdata/reflect.wasm deleted file mode 100644 index dadaadf7cc3af876290f55ceed9c6f744a219b1a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 262334 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 K>1-K0@c#f8^OHXS diff --git a/wasmbinding/testdata/version.txt b/wasmbinding/testdata/version.txt deleted file mode 100644 index dc6b1f0b600..00000000000 --- a/wasmbinding/testdata/version.txt +++ /dev/null @@ -1 +0,0 @@ -v0.6.0 7414d1b diff --git a/wasmbinding/wasm.go b/wasmbinding/wasm.go index 0d67c535449..91f9abba39f 100644 --- a/wasmbinding/wasm.go +++ b/wasmbinding/wasm.go @@ -9,24 +9,20 @@ import ( bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" - gammkeeper "github.com/osmosis-labs/osmosis/v13/x/gamm/keeper" tokenfactorykeeper "github.com/osmosis-labs/osmosis/v13/x/tokenfactory/keeper" - twap "github.com/osmosis-labs/osmosis/v13/x/twap" ) func RegisterCustomPlugins( - gammKeeper *gammkeeper.Keeper, bank *bankkeeper.BaseKeeper, - twap *twap.Keeper, tokenFactory *tokenfactorykeeper.Keeper, ) []wasmkeeper.Option { - wasmQueryPlugin := NewQueryPlugin(gammKeeper, twap, tokenFactory) + wasmQueryPlugin := NewQueryPlugin(tokenFactory) queryPluginOpt := wasmkeeper.WithQueryPlugins(&wasmkeeper.QueryPlugins{ Custom: CustomQuerier(wasmQueryPlugin), }) messengerDecoratorOpt := wasmkeeper.WithMessageHandlerDecorator( - CustomMessageDecorator(gammKeeper, bank, tokenFactory), + CustomMessageDecorator(bank, tokenFactory), ) return []wasm.Option{