From e3dda03660bc1e9e017e1d7173600510b3a20aec Mon Sep 17 00:00:00 2001 From: Uday Patil Date: Tue, 28 Jun 2022 12:01:39 -0400 Subject: [PATCH 1/3] [oracle][wasm] Add support for wasm custom querier --- app/app.go | 2 + wasmbinding/README.md | 10 + wasmbinding/message_plugin.go | 282 +++++++++++++++++++++++ wasmbinding/queries.go | 44 ++++ wasmbinding/query_plugin.go | 61 +++++ wasmbinding/test/query_test.go | 55 +++++ wasmbinding/wasm.go | 27 +++ x/oracle/client/wasm/bindings/queries.go | 25 ++ x/oracle/client/wasm/query.go | 23 ++ 9 files changed, 529 insertions(+) create mode 100644 wasmbinding/README.md create mode 100644 wasmbinding/message_plugin.go create mode 100644 wasmbinding/queries.go create mode 100644 wasmbinding/query_plugin.go create mode 100644 wasmbinding/test/query_test.go create mode 100644 wasmbinding/wasm.go create mode 100644 x/oracle/client/wasm/bindings/queries.go create mode 100644 x/oracle/client/wasm/query.go diff --git a/app/app.go b/app/app.go index fdee04b458..3969839ab3 100644 --- a/app/app.go +++ b/app/app.go @@ -9,6 +9,7 @@ import ( "strings" appparams "github.com/sei-protocol/sei-chain/app/params" + "github.com/sei-protocol/sei-chain/wasmbinding" "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/client" @@ -455,6 +456,7 @@ func New( // The last arguments can contain custom message handlers, and custom query handlers, // if we want to allow any custom callbacks supportedFeatures := "iterator,staking,stargate" + wasmOpts = append(wasmbinding.RegisterCustomPlugins(&app.OracleKeeper), wasmOpts...) app.WasmKeeper = wasm.NewKeeper( appCodec, keys[wasm.StoreKey], diff --git a/wasmbinding/README.md b/wasmbinding/README.md new file mode 100644 index 0000000000..5f222ac319 --- /dev/null +++ b/wasmbinding/README.md @@ -0,0 +1,10 @@ +# CosmWasm support + +This package contains CosmWasm integration points. + +This package provides first class support for: + +- Queries + - OracleExchangeRates +- Messages / Execution + - N/A \ No newline at end of file diff --git a/wasmbinding/message_plugin.go b/wasmbinding/message_plugin.go new file mode 100644 index 0000000000..7b411dfb2f --- /dev/null +++ b/wasmbinding/message_plugin.go @@ -0,0 +1,282 @@ +package wasmbinding + +// import ( +// "encoding/json" + +// wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" +// wasmvmtypes "github.com/CosmWasm/wasmvm/types" +// sdk "github.com/cosmos/cosmos-sdk/types" +// sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +// bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" + +// ) + +// func CustomMessageDecorator(gammKeeper *gammkeeper.Keeper, 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, +// } +// } +// } + +// type CustomMessenger struct { +// wrapped wasmkeeper.Messenger +// bank *bankkeeper.BaseKeeper +// gammKeeper *gammkeeper.Keeper +// tokenFactory *tokenfactorykeeper.Keeper +// } + +// var _ wasmkeeper.Messenger = (*CustomMessenger)(nil) + +// func (m *CustomMessenger) DispatchMsg(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg) ([]sdk.Event, [][]byte, error) { +// if msg.Custom != nil { +// // only handle the happy path where this is really creating / minting / swapping ... +// // leave everything else for the wrapped version +// var contractMsg bindings.OsmosisMsg +// if err := json.Unmarshal(msg.Custom, &contractMsg); err != nil { +// return nil, nil, sdkerrors.Wrap(err, "osmosis msg") +// } +// if contractMsg.CreateDenom != nil { +// return m.createDenom(ctx, contractAddr, contractMsg.CreateDenom) +// } +// if contractMsg.MintTokens != nil { +// return m.mintTokens(ctx, contractAddr, contractMsg.MintTokens) +// } +// if contractMsg.ChangeAdmin != nil { +// return m.changeAdmin(ctx, contractAddr, contractMsg.ChangeAdmin) +// } +// 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) +// } + +// func (m *CustomMessenger) createDenom(ctx sdk.Context, contractAddr sdk.AccAddress, createDenom *bindings.CreateDenom) ([]sdk.Event, [][]byte, error) { +// err := PerformCreateDenom(m.tokenFactory, m.bank, ctx, contractAddr, createDenom) +// if err != nil { +// return nil, nil, sdkerrors.Wrap(err, "perform create denom") +// } +// return nil, nil, nil +// } + +// func PerformCreateDenom(f *tokenfactorykeeper.Keeper, b *bankkeeper.BaseKeeper, ctx sdk.Context, contractAddr sdk.AccAddress, createDenom *bindings.CreateDenom) error { +// if createDenom == nil { +// return wasmvmtypes.InvalidRequest{Err: "create denom null create denom"} +// } + +// msgServer := tokenfactorykeeper.NewMsgServerImpl(*f) + +// msgCreateDenom := tokenfactorytypes.NewMsgCreateDenom(contractAddr.String(), createDenom.Subdenom) + +// if err := msgCreateDenom.ValidateBasic(); err != nil { +// return sdkerrors.Wrap(err, "failed validating MsgCreateDenom") +// } + +// // Create denom +// _, err := msgServer.CreateDenom( +// sdk.WrapSDKContext(ctx), +// msgCreateDenom, +// ) +// if err != nil { +// return sdkerrors.Wrap(err, "creating denom") +// } +// return nil +// } + +// func (m *CustomMessenger) mintTokens(ctx sdk.Context, contractAddr sdk.AccAddress, mint *bindings.MintTokens) ([]sdk.Event, [][]byte, error) { +// err := PerformMint(m.tokenFactory, m.bank, ctx, contractAddr, mint) +// if err != nil { +// return nil, nil, sdkerrors.Wrap(err, "perform mint") +// } +// return nil, nil, nil +// } + +// func PerformMint(f *tokenfactorykeeper.Keeper, b *bankkeeper.BaseKeeper, ctx sdk.Context, contractAddr sdk.AccAddress, mint *bindings.MintTokens) error { +// if mint == nil { +// return wasmvmtypes.InvalidRequest{Err: "mint token null mint"} +// } +// rcpt, err := parseAddress(mint.MintToAddress) +// if err != nil { +// return err +// } + +// coin := sdk.Coin{Denom: mint.Denom, Amount: mint.Amount} +// sdkMsg := tokenfactorytypes.NewMsgMint(contractAddr.String(), coin) +// if err = sdkMsg.ValidateBasic(); err != nil { +// return err +// } + +// // Mint through token factory / message server +// msgServer := tokenfactorykeeper.NewMsgServerImpl(*f) +// _, err = msgServer.Mint(sdk.WrapSDKContext(ctx), sdkMsg) +// if err != nil { +// return sdkerrors.Wrap(err, "minting coins from message") +// } +// err = b.SendCoins(ctx, contractAddr, rcpt, sdk.NewCoins(coin)) +// if err != nil { +// return sdkerrors.Wrap(err, "sending newly minted coins from message") +// } +// return nil +// } + +// func (m *CustomMessenger) changeAdmin(ctx sdk.Context, contractAddr sdk.AccAddress, changeAdmin *bindings.ChangeAdmin) ([]sdk.Event, [][]byte, error) { +// err := ChangeAdmin(m.tokenFactory, ctx, contractAddr, changeAdmin) +// if err != nil { +// return nil, nil, sdkerrors.Wrap(err, "failed to change admin") +// } +// return nil, nil, nil +// } + +// func ChangeAdmin(f *tokenfactorykeeper.Keeper, ctx sdk.Context, contractAddr sdk.AccAddress, changeAdmin *bindings.ChangeAdmin) error { +// if changeAdmin == nil { +// return wasmvmtypes.InvalidRequest{Err: "changeAdmin is nil"} +// } +// newAdminAddr, err := parseAddress(changeAdmin.NewAdminAddress) +// if err != nil { +// return err +// } + +// changeAdminMsg := tokenfactorytypes.NewMsgChangeAdmin(contractAddr.String(), changeAdmin.Denom, newAdminAddr.String()) +// if err := changeAdminMsg.ValidateBasic(); err != nil { +// return err +// } + +// msgServer := tokenfactorykeeper.NewMsgServerImpl(*f) +// _, err = msgServer.ChangeAdmin(sdk.WrapSDKContext(ctx), changeAdminMsg) +// if err != nil { +// return sdkerrors.Wrap(err, "failed changing admin from message") +// } +// return nil +// } + +// func (m *CustomMessenger) burnTokens(ctx sdk.Context, contractAddr sdk.AccAddress, burn *bindings.BurnTokens) ([]sdk.Event, [][]byte, error) { +// err := PerformBurn(m.tokenFactory, ctx, contractAddr, burn) +// if err != nil { +// return nil, nil, sdkerrors.Wrap(err, "perform burn") +// } +// return nil, nil, nil +// } + +// func PerformBurn(f *tokenfactorykeeper.Keeper, ctx sdk.Context, contractAddr sdk.AccAddress, burn *bindings.BurnTokens) error { +// if burn == nil { +// return wasmvmtypes.InvalidRequest{Err: "burn token null mint"} +// } +// if burn.BurnFromAddress != "" && burn.BurnFromAddress != contractAddr.String() { +// return wasmvmtypes.InvalidRequest{Err: "BurnFromAddress must be \"\""} +// } + +// coin := sdk.Coin{Denom: burn.Denom, Amount: burn.Amount} +// sdkMsg := tokenfactorytypes.NewMsgBurn(contractAddr.String(), coin) +// if err := sdkMsg.ValidateBasic(); err != nil { +// return err +// } + +// // Burn through token factory / message server +// msgServer := tokenfactorykeeper.NewMsgServerImpl(*f) +// _, err := msgServer.Burn(sdk.WrapSDKContext(ctx), sdkMsg) +// if err != nil { +// return sdkerrors.Wrap(err, "burning coins from message") +// } +// return nil +// } + +// 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 +// if _, err := parseAddress(contract); err != nil { +// return "", err +// } +// fullDenom, err := tokenfactorytypes.GetTokenDenom(contract, subDenom) +// if err != nil { +// return "", sdkerrors.Wrap(err, "validate sub-denom") +// } + +// return fullDenom, nil +// } + +// func parseAddress(addr string) (sdk.AccAddress, error) { +// parsed, err := sdk.AccAddressFromBech32(addr) +// if err != nil { +// return nil, sdkerrors.Wrap(err, "address from bech32") +// } +// err = sdk.VerifyAddressFormat(parsed) +// if err != nil { +// return nil, sdkerrors.Wrap(err, "verify address format") +// } +// return parsed, nil +// } diff --git a/wasmbinding/queries.go b/wasmbinding/queries.go new file mode 100644 index 0000000000..9de4ad286e --- /dev/null +++ b/wasmbinding/queries.go @@ -0,0 +1,44 @@ +package wasmbinding + +import ( + "encoding/json" + + wasmvmtypes "github.com/CosmWasm/wasmvm/types" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + oraclewasm "github.com/sei-protocol/sei-chain/x/oracle/client/wasm" + oraclebindings "github.com/sei-protocol/sei-chain/x/oracle/client/wasm/bindings" +) + +type QueryPlugin struct { + oracleHandler oraclewasm.OracleWasmQueryHandler +} + +// NewQueryPlugin returns a reference to a new QueryPlugin. +func NewQueryPlugin(oh *oraclewasm.OracleWasmQueryHandler) *QueryPlugin { + return &QueryPlugin{ + oracleHandler: *oh, + } +} + +func (qp QueryPlugin) HandleOracleQuery(ctx sdk.Context, queryData json.RawMessage) ([]byte, error) { + var parsedQuery oraclebindings.SeiOracleQuery + if err := json.Unmarshal(queryData, &parsedQuery); err != nil { + return nil, sdkerrors.Wrap(err, "sei oracle query") + } + switch { + case parsedQuery.ExchangeRates != nil: + res, err := qp.oracleHandler.GetExchangeRates(ctx) + if err != nil { + return nil, sdkerrors.Wrap(err, "sei oracle GetExchangeRates query") + } + bz, err := json.Marshal(res) + if err != nil { + return nil, sdkerrors.Wrap(err, "sei oracle GetExchangeRates response") + } + + return bz, nil + default: + return nil, wasmvmtypes.UnsupportedRequest{Kind: "Unknown Sei Oracle Query"} + } +} diff --git a/wasmbinding/query_plugin.go b/wasmbinding/query_plugin.go new file mode 100644 index 0000000000..4180fb3aae --- /dev/null +++ b/wasmbinding/query_plugin.go @@ -0,0 +1,61 @@ +package wasmbinding + +import ( + "encoding/json" + + wasmvmtypes "github.com/CosmWasm/wasmvm/types" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +const ( + OracleRoute = "oracle" +) + +type SeiQueryWrapper struct { + // specifies which module handler should handle the query + Route string `json:"route,omitempty"` + // The query data that should be parsed into the module query + QueryData json.RawMessage `json:"query_data,omitempty"` +} + +func CustomQuerier(qp *QueryPlugin) func(ctx sdk.Context, request json.RawMessage) ([]byte, error) { + return func(ctx sdk.Context, request json.RawMessage) ([]byte, error) { + var contractQuery SeiQueryWrapper + // TODO: debugging, remove this + ctx.Logger().Error("received a custom query") + if err := json.Unmarshal(request, &contractQuery); err != nil { + return nil, sdkerrors.Wrap(err, "sei query") + } + switch contractQuery.Route { + case OracleRoute: + // TODO: debugging, remove this + ctx.Logger().Error("parse route: oracle query") + bz, _ := json.Marshal(contractQuery) + ctx.Logger().Error(string(bz)) + return qp.HandleOracleQuery(ctx, contractQuery.QueryData) + default: + return nil, wasmvmtypes.UnsupportedRequest{Kind: "Unknown Sei Query Route"} + } + } +} + +// TODO: do we need this stuff from osmosis implementation? maybe remove +// ConvertSdkCoinsToWasmCoins converts sdk type coins to wasm vm type coins +func ConvertSdkCoinsToWasmCoins(coins []sdk.Coin) wasmvmtypes.Coins { + var toSend wasmvmtypes.Coins + for _, coin := range coins { + c := ConvertSdkCoinToWasmCoin(coin) + toSend = append(toSend, c) + } + return toSend +} + +// ConvertSdkCoinToWasmCoin converts a sdk type coin to a wasm vm type coin +func ConvertSdkCoinToWasmCoin(coin sdk.Coin) wasmvmtypes.Coin { + return wasmvmtypes.Coin{ + Denom: coin.Denom, + // Note: gamm tokens have 18 decimal places, so 10^22 is common, no longer in u64 range + Amount: coin.Amount.String(), + } +} diff --git a/wasmbinding/test/query_test.go b/wasmbinding/test/query_test.go new file mode 100644 index 0000000000..8c2ad7e248 --- /dev/null +++ b/wasmbinding/test/query_test.go @@ -0,0 +1,55 @@ +package wasmbinding + +import ( + "encoding/json" + "testing" + "time" + + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/sei-protocol/sei-chain/app" + "github.com/sei-protocol/sei-chain/wasmbinding" + oraclewasm "github.com/sei-protocol/sei-chain/x/oracle/client/wasm" + oraclebinding "github.com/sei-protocol/sei-chain/x/oracle/client/wasm/bindings" + oracletypes "github.com/sei-protocol/sei-chain/x/oracle/types" + oracleutils "github.com/sei-protocol/sei-chain/x/oracle/utils" + "github.com/stretchr/testify/require" +) + +func TestWasmGetOracleExchangeRates(t *testing.T) { + tm := time.Now().UTC() + valPub := secp256k1.GenPrivKey().PubKey() + + testWrapper := app.NewTestWrapper(t, tm, valPub) + + req := oraclebinding.SeiOracleQuery{ExchangeRates: &oraclebinding.WasmQueryExchangeRatesRequest{}} + queryData, err := json.Marshal(req) + require.NoError(t, err) + query := wasmbinding.SeiQueryWrapper{Route: "oracle", QueryData: queryData} + + oh := oraclewasm.NewOracleWasmQueryHandler(&testWrapper.App.OracleKeeper) + qp := wasmbinding.NewQueryPlugin(oh) + customQuerier := wasmbinding.CustomQuerier(qp) + + rawQuery, err := json.Marshal(query) + require.NoError(t, err) + + res, err := customQuerier(testWrapper.Ctx, rawQuery) + require.NoError(t, err) + + var parsedRes oraclebinding.WasmQueryExchangeRatesResponse + err = json.Unmarshal(res, &parsedRes) + require.NoError(t, err) + require.Equal(t, oraclebinding.WasmQueryExchangeRatesResponse{DenomOracleExchangeRatePairs: oracletypes.DenomOracleExchangeRatePairs(nil)}, parsedRes) + + testWrapper.Ctx = testWrapper.Ctx.WithBlockHeight(11) + testWrapper.App.OracleKeeper.SetBaseExchangeRate(testWrapper.Ctx, oracleutils.MicroAtomDenom, sdk.NewDec(12)) + + res, err = customQuerier(testWrapper.Ctx, rawQuery) + require.NoError(t, err) + + var parsedRes2 oraclebinding.WasmQueryExchangeRatesResponse + err = json.Unmarshal(res, &parsedRes2) + require.NoError(t, err) + require.Equal(t, oraclebinding.WasmQueryExchangeRatesResponse{DenomOracleExchangeRatePairs: oracletypes.DenomOracleExchangeRatePairs{oracletypes.NewDenomOracleExchangeRatePair(oracleutils.MicroAtomDenom, sdk.NewDec(12), sdk.NewInt(11))}}, parsedRes2) +} diff --git a/wasmbinding/wasm.go b/wasmbinding/wasm.go new file mode 100644 index 0000000000..fd894aeacc --- /dev/null +++ b/wasmbinding/wasm.go @@ -0,0 +1,27 @@ +package wasmbinding + +import ( + "github.com/CosmWasm/wasmd/x/wasm" + wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" + oraclewasm "github.com/sei-protocol/sei-chain/x/oracle/client/wasm" + oraclekeeper "github.com/sei-protocol/sei-chain/x/oracle/keeper" +) + +func RegisterCustomPlugins( + oracle *oraclekeeper.Keeper, +) []wasmkeeper.Option { + oracleHandler := oraclewasm.NewOracleWasmQueryHandler(oracle) + wasmQueryPlugin := NewQueryPlugin(oracleHandler) + + queryPluginOpt := wasmkeeper.WithQueryPlugins(&wasmkeeper.QueryPlugins{ + Custom: CustomQuerier(wasmQueryPlugin), + }) + // messengerDecoratorOpt := wasmkeeper.WithMessageHandlerDecorator( + // CustomMessageDecorator(gammKeeper, bank, tokenFactory), + // ) + + return []wasm.Option{ + queryPluginOpt, + // messengerDecoratorOpt, + } +} diff --git a/x/oracle/client/wasm/bindings/queries.go b/x/oracle/client/wasm/bindings/queries.go new file mode 100644 index 0000000000..cea98453cb --- /dev/null +++ b/x/oracle/client/wasm/bindings/queries.go @@ -0,0 +1,25 @@ +package bindings + +import "github.com/sei-protocol/sei-chain/x/oracle/types" + +type SeiOracleQuery struct { + // queries the oracle exchange rates + ExchangeRates *WasmQueryExchangeRatesRequest `json:"exchange_rates,omitempty"` +} + +type WasmQueryExchangeRatesRequest struct{} + +// type WasmQueryOracleExchangeRate struct { +// ExchangeRate string `json:"exchange_rate,omitempty"` +// LastUpdate string `json:"last_update,omitempty"` +// } + +// type WasmQueryDenomOracleExchangeRate struct { +// Denom string `json:"denom,omitempty"` +// OracleExchangeRate WasmQueryOracleExchangeRate `json:"oracle_exchange_rate,omitempty"` +// } + +type WasmQueryExchangeRatesResponse struct { + DenomOracleExchangeRatePairs types.DenomOracleExchangeRatePairs `json:"denom_oracle_exchange_rate_pairs,omitempty"` + // DenomOracleExchangeRatePairs []WasmQueryDenomOracleExchangeRate `json:"denom_oracle_exchange_rate_pairs,omitempty"` +} diff --git a/x/oracle/client/wasm/query.go b/x/oracle/client/wasm/query.go new file mode 100644 index 0000000000..6a02e5158c --- /dev/null +++ b/x/oracle/client/wasm/query.go @@ -0,0 +1,23 @@ +package wasm + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + oraclekeeper "github.com/sei-protocol/sei-chain/x/oracle/keeper" + "github.com/sei-protocol/sei-chain/x/oracle/types" +) + +type OracleWasmQueryHandler struct { + oracleKeeper oraclekeeper.Keeper +} + +func NewOracleWasmQueryHandler(keeper *oraclekeeper.Keeper) *OracleWasmQueryHandler { + return &OracleWasmQueryHandler{ + oracleKeeper: *keeper, + } +} + +func (handler OracleWasmQueryHandler) GetExchangeRates(ctx sdk.Context) (*types.QueryExchangeRatesResponse, error) { + querier := oraclekeeper.NewQuerier(handler.oracleKeeper) + c := sdk.WrapSDKContext(ctx) + return querier.ExchangeRates(c, &types.QueryExchangeRatesRequest{}) +} From f8d890088d337a4653dea98af626d8a657dba879 Mon Sep 17 00:00:00 2001 From: Uday Patil Date: Tue, 28 Jun 2022 19:01:11 -0400 Subject: [PATCH 2/3] [wasmbinding] Removed some unnecessary debug statements and commented code --- wasmbinding/message_plugin.go | 245 +---------------------- wasmbinding/queries.go | 6 +- wasmbinding/query_plugin.go | 8 +- x/oracle/client/wasm/bindings/queries.go | 10 - 4 files changed, 11 insertions(+), 258 deletions(-) diff --git a/wasmbinding/message_plugin.go b/wasmbinding/message_plugin.go index 7b411dfb2f..50eb0e3b72 100644 --- a/wasmbinding/message_plugin.go +++ b/wasmbinding/message_plugin.go @@ -8,25 +8,20 @@ package wasmbinding // sdk "github.com/cosmos/cosmos-sdk/types" // sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" // bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" - // ) -// func CustomMessageDecorator(gammKeeper *gammkeeper.Keeper, bank *bankkeeper.BaseKeeper, tokenFactory *tokenfactorykeeper.Keeper) func(wasmkeeper.Messenger) wasmkeeper.Messenger { +// func CustomMessageDecorator(bank *bankkeeper.BaseKeeper) func(wasmkeeper.Messenger) wasmkeeper.Messenger { // return func(old wasmkeeper.Messenger) wasmkeeper.Messenger { // return &CustomMessenger{ -// wrapped: old, -// bank: bank, -// gammKeeper: gammKeeper, -// tokenFactory: tokenFactory, +// wrapped: old, +// bank: bank, // } // } // } // type CustomMessenger struct { -// wrapped wasmkeeper.Messenger -// bank *bankkeeper.BaseKeeper -// gammKeeper *gammkeeper.Keeper -// tokenFactory *tokenfactorykeeper.Keeper +// wrapped wasmkeeper.Messenger +// bank *bankkeeper.BaseKeeper // } // var _ wasmkeeper.Messenger = (*CustomMessenger)(nil) @@ -35,240 +30,14 @@ package wasmbinding // if msg.Custom != nil { // // only handle the happy path where this is really creating / minting / swapping ... // // leave everything else for the wrapped version -// var contractMsg bindings.OsmosisMsg +// var contractMsg bindings.SeiMessage // if err := json.Unmarshal(msg.Custom, &contractMsg); err != nil { -// return nil, nil, sdkerrors.Wrap(err, "osmosis msg") -// } -// if contractMsg.CreateDenom != nil { -// return m.createDenom(ctx, contractAddr, contractMsg.CreateDenom) -// } -// if contractMsg.MintTokens != nil { -// return m.mintTokens(ctx, contractAddr, contractMsg.MintTokens) -// } -// if contractMsg.ChangeAdmin != nil { -// return m.changeAdmin(ctx, contractAddr, contractMsg.ChangeAdmin) -// } -// if contractMsg.BurnTokens != nil { -// return m.burnTokens(ctx, contractAddr, contractMsg.BurnTokens) -// } -// if contractMsg.Swap != nil { -// return m.swapTokens(ctx, contractAddr, contractMsg.Swap) +// return nil, nil, sdkerrors.Wrap(err, "sei msg") // } // } // return m.wrapped.DispatchMsg(ctx, contractAddr, contractIBCPortID, msg) // } -// func (m *CustomMessenger) createDenom(ctx sdk.Context, contractAddr sdk.AccAddress, createDenom *bindings.CreateDenom) ([]sdk.Event, [][]byte, error) { -// err := PerformCreateDenom(m.tokenFactory, m.bank, ctx, contractAddr, createDenom) -// if err != nil { -// return nil, nil, sdkerrors.Wrap(err, "perform create denom") -// } -// return nil, nil, nil -// } - -// func PerformCreateDenom(f *tokenfactorykeeper.Keeper, b *bankkeeper.BaseKeeper, ctx sdk.Context, contractAddr sdk.AccAddress, createDenom *bindings.CreateDenom) error { -// if createDenom == nil { -// return wasmvmtypes.InvalidRequest{Err: "create denom null create denom"} -// } - -// msgServer := tokenfactorykeeper.NewMsgServerImpl(*f) - -// msgCreateDenom := tokenfactorytypes.NewMsgCreateDenom(contractAddr.String(), createDenom.Subdenom) - -// if err := msgCreateDenom.ValidateBasic(); err != nil { -// return sdkerrors.Wrap(err, "failed validating MsgCreateDenom") -// } - -// // Create denom -// _, err := msgServer.CreateDenom( -// sdk.WrapSDKContext(ctx), -// msgCreateDenom, -// ) -// if err != nil { -// return sdkerrors.Wrap(err, "creating denom") -// } -// return nil -// } - -// func (m *CustomMessenger) mintTokens(ctx sdk.Context, contractAddr sdk.AccAddress, mint *bindings.MintTokens) ([]sdk.Event, [][]byte, error) { -// err := PerformMint(m.tokenFactory, m.bank, ctx, contractAddr, mint) -// if err != nil { -// return nil, nil, sdkerrors.Wrap(err, "perform mint") -// } -// return nil, nil, nil -// } - -// func PerformMint(f *tokenfactorykeeper.Keeper, b *bankkeeper.BaseKeeper, ctx sdk.Context, contractAddr sdk.AccAddress, mint *bindings.MintTokens) error { -// if mint == nil { -// return wasmvmtypes.InvalidRequest{Err: "mint token null mint"} -// } -// rcpt, err := parseAddress(mint.MintToAddress) -// if err != nil { -// return err -// } - -// coin := sdk.Coin{Denom: mint.Denom, Amount: mint.Amount} -// sdkMsg := tokenfactorytypes.NewMsgMint(contractAddr.String(), coin) -// if err = sdkMsg.ValidateBasic(); err != nil { -// return err -// } - -// // Mint through token factory / message server -// msgServer := tokenfactorykeeper.NewMsgServerImpl(*f) -// _, err = msgServer.Mint(sdk.WrapSDKContext(ctx), sdkMsg) -// if err != nil { -// return sdkerrors.Wrap(err, "minting coins from message") -// } -// err = b.SendCoins(ctx, contractAddr, rcpt, sdk.NewCoins(coin)) -// if err != nil { -// return sdkerrors.Wrap(err, "sending newly minted coins from message") -// } -// return nil -// } - -// func (m *CustomMessenger) changeAdmin(ctx sdk.Context, contractAddr sdk.AccAddress, changeAdmin *bindings.ChangeAdmin) ([]sdk.Event, [][]byte, error) { -// err := ChangeAdmin(m.tokenFactory, ctx, contractAddr, changeAdmin) -// if err != nil { -// return nil, nil, sdkerrors.Wrap(err, "failed to change admin") -// } -// return nil, nil, nil -// } - -// func ChangeAdmin(f *tokenfactorykeeper.Keeper, ctx sdk.Context, contractAddr sdk.AccAddress, changeAdmin *bindings.ChangeAdmin) error { -// if changeAdmin == nil { -// return wasmvmtypes.InvalidRequest{Err: "changeAdmin is nil"} -// } -// newAdminAddr, err := parseAddress(changeAdmin.NewAdminAddress) -// if err != nil { -// return err -// } - -// changeAdminMsg := tokenfactorytypes.NewMsgChangeAdmin(contractAddr.String(), changeAdmin.Denom, newAdminAddr.String()) -// if err := changeAdminMsg.ValidateBasic(); err != nil { -// return err -// } - -// msgServer := tokenfactorykeeper.NewMsgServerImpl(*f) -// _, err = msgServer.ChangeAdmin(sdk.WrapSDKContext(ctx), changeAdminMsg) -// if err != nil { -// return sdkerrors.Wrap(err, "failed changing admin from message") -// } -// return nil -// } - -// func (m *CustomMessenger) burnTokens(ctx sdk.Context, contractAddr sdk.AccAddress, burn *bindings.BurnTokens) ([]sdk.Event, [][]byte, error) { -// err := PerformBurn(m.tokenFactory, ctx, contractAddr, burn) -// if err != nil { -// return nil, nil, sdkerrors.Wrap(err, "perform burn") -// } -// return nil, nil, nil -// } - -// func PerformBurn(f *tokenfactorykeeper.Keeper, ctx sdk.Context, contractAddr sdk.AccAddress, burn *bindings.BurnTokens) error { -// if burn == nil { -// return wasmvmtypes.InvalidRequest{Err: "burn token null mint"} -// } -// if burn.BurnFromAddress != "" && burn.BurnFromAddress != contractAddr.String() { -// return wasmvmtypes.InvalidRequest{Err: "BurnFromAddress must be \"\""} -// } - -// coin := sdk.Coin{Denom: burn.Denom, Amount: burn.Amount} -// sdkMsg := tokenfactorytypes.NewMsgBurn(contractAddr.String(), coin) -// if err := sdkMsg.ValidateBasic(); err != nil { -// return err -// } - -// // Burn through token factory / message server -// msgServer := tokenfactorykeeper.NewMsgServerImpl(*f) -// _, err := msgServer.Burn(sdk.WrapSDKContext(ctx), sdkMsg) -// if err != nil { -// return sdkerrors.Wrap(err, "burning coins from message") -// } -// return nil -// } - -// 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 -// if _, err := parseAddress(contract); err != nil { -// return "", err -// } -// fullDenom, err := tokenfactorytypes.GetTokenDenom(contract, subDenom) -// if err != nil { -// return "", sdkerrors.Wrap(err, "validate sub-denom") -// } - -// return fullDenom, nil -// } - // func parseAddress(addr string) (sdk.AccAddress, error) { // parsed, err := sdk.AccAddressFromBech32(addr) // if err != nil { diff --git a/wasmbinding/queries.go b/wasmbinding/queries.go index 9de4ad286e..68b3b9763c 100644 --- a/wasmbinding/queries.go +++ b/wasmbinding/queries.go @@ -24,17 +24,17 @@ func NewQueryPlugin(oh *oraclewasm.OracleWasmQueryHandler) *QueryPlugin { func (qp QueryPlugin) HandleOracleQuery(ctx sdk.Context, queryData json.RawMessage) ([]byte, error) { var parsedQuery oraclebindings.SeiOracleQuery if err := json.Unmarshal(queryData, &parsedQuery); err != nil { - return nil, sdkerrors.Wrap(err, "sei oracle query") + return nil, sdkerrors.Wrap(err, "Error parsing SeiOracleQuery") } switch { case parsedQuery.ExchangeRates != nil: res, err := qp.oracleHandler.GetExchangeRates(ctx) if err != nil { - return nil, sdkerrors.Wrap(err, "sei oracle GetExchangeRates query") + return nil, sdkerrors.Wrap(err, "Error while Exchange Rates") } bz, err := json.Marshal(res) if err != nil { - return nil, sdkerrors.Wrap(err, "sei oracle GetExchangeRates response") + return nil, sdkerrors.Wrap(err, "Error encoding exchange rates as JSON") } return bz, nil diff --git a/wasmbinding/query_plugin.go b/wasmbinding/query_plugin.go index 4180fb3aae..1f022f3bc8 100644 --- a/wasmbinding/query_plugin.go +++ b/wasmbinding/query_plugin.go @@ -22,17 +22,11 @@ type SeiQueryWrapper struct { func CustomQuerier(qp *QueryPlugin) func(ctx sdk.Context, request json.RawMessage) ([]byte, error) { return func(ctx sdk.Context, request json.RawMessage) ([]byte, error) { var contractQuery SeiQueryWrapper - // TODO: debugging, remove this - ctx.Logger().Error("received a custom query") if err := json.Unmarshal(request, &contractQuery); err != nil { - return nil, sdkerrors.Wrap(err, "sei query") + return nil, sdkerrors.Wrap(err, "Error parsing request data") } switch contractQuery.Route { case OracleRoute: - // TODO: debugging, remove this - ctx.Logger().Error("parse route: oracle query") - bz, _ := json.Marshal(contractQuery) - ctx.Logger().Error(string(bz)) return qp.HandleOracleQuery(ctx, contractQuery.QueryData) default: return nil, wasmvmtypes.UnsupportedRequest{Kind: "Unknown Sei Query Route"} diff --git a/x/oracle/client/wasm/bindings/queries.go b/x/oracle/client/wasm/bindings/queries.go index cea98453cb..bd1c433b3c 100644 --- a/x/oracle/client/wasm/bindings/queries.go +++ b/x/oracle/client/wasm/bindings/queries.go @@ -9,16 +9,6 @@ type SeiOracleQuery struct { type WasmQueryExchangeRatesRequest struct{} -// type WasmQueryOracleExchangeRate struct { -// ExchangeRate string `json:"exchange_rate,omitempty"` -// LastUpdate string `json:"last_update,omitempty"` -// } - -// type WasmQueryDenomOracleExchangeRate struct { -// Denom string `json:"denom,omitempty"` -// OracleExchangeRate WasmQueryOracleExchangeRate `json:"oracle_exchange_rate,omitempty"` -// } - type WasmQueryExchangeRatesResponse struct { DenomOracleExchangeRatePairs types.DenomOracleExchangeRatePairs `json:"denom_oracle_exchange_rate_pairs,omitempty"` // DenomOracleExchangeRatePairs []WasmQueryDenomOracleExchangeRate `json:"denom_oracle_exchange_rate_pairs,omitempty"` From 434565902bc44e5d88a3afcbfc2e2175209cc746 Mon Sep 17 00:00:00 2001 From: Uday Patil Date: Wed, 29 Jun 2022 15:18:14 -0400 Subject: [PATCH 3/3] [wasmbinding] remove message plugin stuff --- wasmbinding/message_plugin.go | 51 ----------------------------------- wasmbinding/wasm.go | 4 --- 2 files changed, 55 deletions(-) delete mode 100644 wasmbinding/message_plugin.go diff --git a/wasmbinding/message_plugin.go b/wasmbinding/message_plugin.go deleted file mode 100644 index 50eb0e3b72..0000000000 --- a/wasmbinding/message_plugin.go +++ /dev/null @@ -1,51 +0,0 @@ -package wasmbinding - -// import ( -// "encoding/json" - -// wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" -// wasmvmtypes "github.com/CosmWasm/wasmvm/types" -// sdk "github.com/cosmos/cosmos-sdk/types" -// sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" -// bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" -// ) - -// func CustomMessageDecorator(bank *bankkeeper.BaseKeeper) func(wasmkeeper.Messenger) wasmkeeper.Messenger { -// return func(old wasmkeeper.Messenger) wasmkeeper.Messenger { -// return &CustomMessenger{ -// wrapped: old, -// bank: bank, -// } -// } -// } - -// type CustomMessenger struct { -// wrapped wasmkeeper.Messenger -// bank *bankkeeper.BaseKeeper -// } - -// var _ wasmkeeper.Messenger = (*CustomMessenger)(nil) - -// func (m *CustomMessenger) DispatchMsg(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg) ([]sdk.Event, [][]byte, error) { -// if msg.Custom != nil { -// // only handle the happy path where this is really creating / minting / swapping ... -// // leave everything else for the wrapped version -// var contractMsg bindings.SeiMessage -// if err := json.Unmarshal(msg.Custom, &contractMsg); err != nil { -// return nil, nil, sdkerrors.Wrap(err, "sei msg") -// } -// } -// return m.wrapped.DispatchMsg(ctx, contractAddr, contractIBCPortID, msg) -// } - -// func parseAddress(addr string) (sdk.AccAddress, error) { -// parsed, err := sdk.AccAddressFromBech32(addr) -// if err != nil { -// return nil, sdkerrors.Wrap(err, "address from bech32") -// } -// err = sdk.VerifyAddressFormat(parsed) -// if err != nil { -// return nil, sdkerrors.Wrap(err, "verify address format") -// } -// return parsed, nil -// } diff --git a/wasmbinding/wasm.go b/wasmbinding/wasm.go index fd894aeacc..114f970bc4 100644 --- a/wasmbinding/wasm.go +++ b/wasmbinding/wasm.go @@ -16,12 +16,8 @@ func RegisterCustomPlugins( queryPluginOpt := wasmkeeper.WithQueryPlugins(&wasmkeeper.QueryPlugins{ Custom: CustomQuerier(wasmQueryPlugin), }) - // messengerDecoratorOpt := wasmkeeper.WithMessageHandlerDecorator( - // CustomMessageDecorator(gammKeeper, bank, tokenFactory), - // ) return []wasm.Option{ queryPluginOpt, - // messengerDecoratorOpt, } }