diff --git a/.gitignore b/.gitignore index dcc5cd8462..905a59c025 100644 --- a/.gitignore +++ b/.gitignore @@ -47,3 +47,4 @@ private* #IntelliJ .idea/ +.vscode \ No newline at end of file diff --git a/app/app.go b/app/app.go index 207a60b4c9..53385ec4b5 100644 --- a/app/app.go +++ b/app/app.go @@ -140,6 +140,7 @@ import ( ugovmodule "github.com/umee-network/umee/v4/x/ugov/module" // umee ibc-transfer and quota for ibc-transfer + uwasm "github.com/umee-network/umee/v4/app/wasm" "github.com/umee-network/umee/v4/x/uibc" uibcmodule "github.com/umee-network/umee/v4/x/uibc/module" uibcoracle "github.com/umee-network/umee/v4/x/uibc/oracle" @@ -648,6 +649,10 @@ func New( // The last arguments can contain custom message handlers, and custom query handlers, // if we want to allow any custom callbacks availableCapabilities := "iterator,staking,stargate,cosmwasm_1_1,umee" + + // Register umee custom plugin to wasm + wasmOpts = append(uwasm.RegisterCustomPlugins(app.LeverageKeeper, app.OracleKeeper), wasmOpts...) + app.WasmKeeper = wasm.NewKeeper( appCodec, keys[wasm.StoreKey], diff --git a/app/wasm/custom_plugins.go b/app/wasm/custom_plugins.go new file mode 100644 index 0000000000..586d76fa3f --- /dev/null +++ b/app/wasm/custom_plugins.go @@ -0,0 +1,29 @@ +package wasm + +import ( + "github.com/CosmWasm/wasmd/x/wasm" + wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" + + "github.com/umee-network/umee/v4/app/wasm/msg" + "github.com/umee-network/umee/v4/app/wasm/query" + leveragekeeper "github.com/umee-network/umee/v4/x/leverage/keeper" + oraclekeeper "github.com/umee-network/umee/v4/x/oracle/keeper" +) + +// RegisterCustomPlugins expose the queries and msgs of native modules to wasm. +func RegisterCustomPlugins( + leverageKeeper leveragekeeper.Keeper, + oracleKeeper oraclekeeper.Keeper, +) []wasmkeeper.Option { + wasmQueryPlugin := query.NewQueryPlugin(leverageKeeper, oracleKeeper) + queryPluginOpt := wasmkeeper.WithQueryPlugins(&wasmkeeper.QueryPlugins{ + Custom: wasmQueryPlugin.CustomQuerier(), + }) + + messagePluginOpt := wasmkeeper.WithMessageHandlerDecorator(msg.NewMessagePlugin(leverageKeeper)) + + return []wasm.Option{ + queryPluginOpt, + messagePluginOpt, + } +} diff --git a/app/wasm/msg/handler_leverage.go b/app/wasm/msg/handler_leverage.go new file mode 100644 index 0000000000..90a4e9d49f --- /dev/null +++ b/app/wasm/msg/handler_leverage.go @@ -0,0 +1,106 @@ +package msg + +import ( + "context" + + "github.com/gogo/protobuf/proto" + + lvtypes "github.com/umee-network/umee/v4/x/leverage/types" +) + +// HandleSupply handles the Supply value of an address. +func (m UmeeMsg) HandleSupply( + ctx context.Context, sender string, + s lvtypes.MsgServer, +) (proto.Message, error) { + req := &lvtypes.MsgSupply{Supplier: sender, Asset: m.Supply.Asset} + return s.Supply(ctx, req) +} + +// HandleWithdraw handles the Withdraw value of an address. +func (m UmeeMsg) HandleWithdraw( + ctx context.Context, sender string, + s lvtypes.MsgServer, +) (proto.Message, error) { + req := &lvtypes.MsgWithdraw{Supplier: sender, Asset: m.Withdraw.Asset} + return s.Withdraw(ctx, req) +} + +// HandleMaxWithdraw handles the maximum withdraw value of an address. +func (m UmeeMsg) HandleMaxWithdraw( + ctx context.Context, sender string, + s lvtypes.MsgServer, +) (proto.Message, error) { + req := &lvtypes.MsgMaxWithdraw{Supplier: sender, Denom: m.MaxWithdraw.Denom} + return s.MaxWithdraw(ctx, req) +} + +// HandleCollateralize handles the enable selected uTokens as collateral. +func (m UmeeMsg) HandleCollateralize( + ctx context.Context, sender string, + s lvtypes.MsgServer, +) (proto.Message, error) { + req := &lvtypes.MsgCollateralize{Borrower: sender, Asset: m.Collateralize.Asset} + return s.Collateralize(ctx, req) +} + +// HandleDecollateralize handles the disable amount of an selected uTokens +// as collateral. +func (m UmeeMsg) HandleDecollateralize( + ctx context.Context, sender string, + s lvtypes.MsgServer, +) (proto.Message, error) { + req := &lvtypes.MsgDecollateralize{Borrower: sender, Asset: m.Decollateralize.Asset} + return s.Decollateralize(ctx, req) +} + +// HandleBorrow handles the borrowing coins from the capital facility. +func (m UmeeMsg) HandleBorrow( + ctx context.Context, sender string, + s lvtypes.MsgServer, +) (proto.Message, error) { + req := &lvtypes.MsgBorrow{Borrower: sender, Asset: m.Borrow.Asset} + return s.Borrow(ctx, req) +} + +// HandleMaxBorrow handles the borrowing maximum coins from the capital facility. +func (m UmeeMsg) HandleMaxBorrow( + ctx context.Context, sender string, + s lvtypes.MsgServer, +) (proto.Message, error) { + req := &lvtypes.MsgMaxBorrow{Borrower: sender, Denom: m.MaxBorrow.Denom} + return s.MaxBorrow(ctx, req) +} + +// HandleRepay handles repaying borrowed coins to the capital facility. +func (m UmeeMsg) HandleRepay( + ctx context.Context, sender string, + s lvtypes.MsgServer, +) (proto.Message, error) { + req := &lvtypes.MsgRepay{Borrower: sender, Asset: m.Repay.Asset} + return s.Repay(ctx, req) +} + +// HandleLiquidate handles the repaying a different user's borrowed coins +// to the capital facility in exchange for some of their collateral. +func (m UmeeMsg) HandleLiquidate( + ctx context.Context, sender string, + s lvtypes.MsgServer, +) (proto.Message, error) { + req := &lvtypes.MsgLiquidate{ + Liquidator: sender, + Borrower: m.Liquidate.Borrower, + Repayment: m.Liquidate.Repayment, + RewardDenom: m.Liquidate.RewardDenom, + } + return s.Liquidate(ctx, req) +} + +// HandleSupplyCollateral handles the supply the assets and collateral their assets. +func (m UmeeMsg) HandleSupplyCollateral( + ctx context.Context, sender string, + s lvtypes.MsgServer, +) (proto.Message, error) { + req := &lvtypes.MsgSupplyCollateral{Supplier: sender, Asset: m.SupplyCollateral.Asset} + return s.SupplyCollateral(ctx, req) +} diff --git a/app/wasm/msg/plugin.go b/app/wasm/msg/plugin.go new file mode 100644 index 0000000000..1c4ebf7929 --- /dev/null +++ b/app/wasm/msg/plugin.go @@ -0,0 +1,82 @@ +package msg + +import ( + "encoding/json" + + sdkerrors "cosmossdk.io/errors" + wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" + wasmvmtypes "github.com/CosmWasm/wasmvm/types" + sdk "github.com/cosmos/cosmos-sdk/types" + + lvkeeper "github.com/umee-network/umee/v4/x/leverage/keeper" + lvtypes "github.com/umee-network/umee/v4/x/leverage/types" +) + +// Plugin wraps the msg plugin with Messengers. +type Plugin struct { + lvMsgServer lvtypes.MsgServer + wrapped wasmkeeper.Messenger +} + +var _ wasmkeeper.Messenger = (*Plugin)(nil) + +// CustomMessageDecorator returns decorator for custom CosmWasm bindings messages +func NewMessagePlugin(leverageKeeper lvkeeper.Keeper) func(wasmkeeper.Messenger) wasmkeeper.Messenger { + return func(old wasmkeeper.Messenger) wasmkeeper.Messenger { + return &Plugin{ + wrapped: old, + lvMsgServer: lvkeeper.NewMsgServerImpl(leverageKeeper), + } + } +} + +// DispatchCustomMsg responsible for handling custom messages (umee native messages). +func (plugin *Plugin) DispatchCustomMsg(ctx sdk.Context, contractAddr sdk.AccAddress, rawMsg json.RawMessage) error { + var smartcontractMessage UmeeMsg + if err := json.Unmarshal(rawMsg, &smartcontractMessage); err != nil { + return sdkerrors.Wrap(err, "invalid umee custom msg") + } + + sender := contractAddr.String() + var err error + sdkCtx := sdk.WrapSDKContext(ctx) + switch { + case smartcontractMessage.Supply != nil: + _, err = smartcontractMessage.HandleSupply(sdkCtx, sender, plugin.lvMsgServer) + case smartcontractMessage.Withdraw != nil: + _, err = smartcontractMessage.HandleWithdraw(sdkCtx, sender, plugin.lvMsgServer) + case smartcontractMessage.MaxWithdraw != nil: + _, err = smartcontractMessage.HandleMaxWithdraw(sdkCtx, sender, plugin.lvMsgServer) + case smartcontractMessage.Collateralize != nil: + _, err = smartcontractMessage.HandleCollateralize(sdkCtx, sender, plugin.lvMsgServer) + case smartcontractMessage.Decollateralize != nil: + _, err = smartcontractMessage.HandleDecollateralize(sdkCtx, sender, plugin.lvMsgServer) + case smartcontractMessage.Borrow != nil: + _, err = smartcontractMessage.HandleBorrow(sdkCtx, sender, plugin.lvMsgServer) + case smartcontractMessage.MaxBorrow != nil: + _, err = smartcontractMessage.HandleMaxBorrow(sdkCtx, sender, plugin.lvMsgServer) + case smartcontractMessage.Repay != nil: + _, err = smartcontractMessage.HandleRepay(sdkCtx, sender, plugin.lvMsgServer) + case smartcontractMessage.Liquidate != nil: + _, err = smartcontractMessage.HandleLiquidate(sdkCtx, sender, plugin.lvMsgServer) + case smartcontractMessage.SupplyCollateral != nil: + _, err = smartcontractMessage.HandleSupplyCollateral(sdkCtx, sender, plugin.lvMsgServer) + default: + err = wasmvmtypes.UnsupportedRequest{Kind: "invalid assigned umee msg"} + } + + return err +} + +// DispatchMsg encodes the wasmVM message and dispatches it. +func (plugin *Plugin) DispatchMsg( + ctx sdk.Context, + contractAddr sdk.AccAddress, + contractIBCPortID string, + msg wasmvmtypes.CosmosMsg, +) (events []sdk.Event, data [][]byte, err error) { + if msg.Custom != nil { + return nil, nil, plugin.DispatchCustomMsg(ctx, contractAddr, msg.Custom) + } + return plugin.wrapped.DispatchMsg(ctx, contractAddr, contractIBCPortID, msg) +} diff --git a/app/wasm/msg/types.go b/app/wasm/msg/types.go new file mode 100644 index 0000000000..574cd05714 --- /dev/null +++ b/app/wasm/msg/types.go @@ -0,0 +1,30 @@ +package msg + +import ( + lvtypes "github.com/umee-network/umee/v4/x/leverage/types" +) + +// UmeeMsg wraps all the messages availables for cosmwasm smartcontracts. +type UmeeMsg struct { + // Used to supply coins to the capital facility. + Supply *lvtypes.MsgSupply `json:"supply,omitempty"` + // Used to withdraw previously loaned coins from the capital facility. + Withdraw *lvtypes.MsgWithdraw `json:"withdraw,omitempty"` + // Used to do withdraw maximum assets by supplier. + MaxWithdraw *lvtypes.MsgMaxWithdraw `json:"max_withdraw,omitempty"` + // Used to enable an amount of selected uTokens as collateral. + Collateralize *lvtypes.MsgCollateralize `json:"collateralize,omitempty"` + // Used to disable amount of an selected uTokens as collateral. + Decollateralize *lvtypes.MsgDecollateralize `json:"decollateralize,omitempty"` + // Used to borrowing coins from the capital facility. + Borrow *lvtypes.MsgBorrow `json:"borrow,omitempty"` + // Used to borrowing coins from the capital facility. + MaxBorrow *lvtypes.MsgMaxBorrow `json:"max_borrow,omitempty"` + // Used to repaying borrowed coins to the capital facility. + Repay *lvtypes.MsgRepay `json:"repay,omitempty"` + // Used to repaying a different user's borrowed coins + // to the capital facility in exchange for some of their collateral. + Liquidate *lvtypes.MsgLiquidate `json:"liquidate,omitempty"` + // Used to do supply and collateralize their assets. + SupplyCollateral *lvtypes.MsgSupplyCollateral `json:"supply_collateral,omitempty"` +} diff --git a/app/wasm/query/handle_leverage.go b/app/wasm/query/handle_leverage.go new file mode 100644 index 0000000000..496b7f7222 --- /dev/null +++ b/app/wasm/query/handle_leverage.go @@ -0,0 +1,91 @@ +package query + +import ( + "context" + + "github.com/gogo/protobuf/proto" + + lvtypes "github.com/umee-network/umee/v4/x/leverage/types" +) + +// HandleLeverageParams handles the get the x/leverage module's parameters. +func (q UmeeQuery) HandleLeverageParams( + ctx context.Context, + qs lvtypes.QueryServer, +) (proto.Message, error) { + return qs.Params(ctx, &lvtypes.QueryParams{}) +} + +// HandleRegisteredTokens handles the get all registered tokens query and response. +func (q UmeeQuery) HandleRegisteredTokens( + ctx context.Context, + qs lvtypes.QueryServer, +) (proto.Message, error) { + if len(q.RegisteredTokens.BaseDenom) != 0 { + return qs.RegisteredTokens(ctx, &lvtypes.QueryRegisteredTokens{ + BaseDenom: q.RegisteredTokens.BaseDenom, + }) + } + return qs.RegisteredTokens(ctx, &lvtypes.QueryRegisteredTokens{}) +} + +// HandleMarketSummary queries a base asset's current borrowing and supplying conditions. +func (q UmeeQuery) HandleMarketSummary( + ctx context.Context, + qs lvtypes.QueryServer, +) (proto.Message, error) { + return qs.MarketSummary(ctx, &lvtypes.QueryMarketSummary{Denom: q.MarketSummary.Denom}) +} + +// HandleAccountBalances queries an account's current supply, collateral, and borrow positions. +func (q UmeeQuery) HandleAccountBalances( + ctx context.Context, + qs lvtypes.QueryServer, +) (proto.Message, error) { + req := &lvtypes.QueryAccountBalances{Address: q.AccountBalances.Address} + return qs.AccountBalances(ctx, req) +} + +// HandleAccountSummary queries USD values representing an account's total +// positions and borrowing limits. It requires oracle prices to return successfully. +func (q UmeeQuery) HandleAccountSummary( + ctx context.Context, + qs lvtypes.QueryServer, +) (proto.Message, error) { + req := &lvtypes.QueryAccountSummary{Address: q.AccountSummary.Address} + return qs.AccountSummary(ctx, req) +} + +// HandleLiquidationTargets queries a list of all borrower account addresses eligible for liquidation. +func (q UmeeQuery) HandleLiquidationTargets( + ctx context.Context, + qs lvtypes.QueryServer, +) (proto.Message, error) { + return qs.LiquidationTargets(ctx, &lvtypes.QueryLiquidationTargets{}) +} + +// HandleBadDebts queries bad debts. +func (q UmeeQuery) HandleBadDebts( + ctx context.Context, + qs lvtypes.QueryServer, +) (proto.Message, error) { + return qs.BadDebts(ctx, &lvtypes.QueryBadDebts{}) +} + +// HandleBadDebts queries bad debts. +func (q UmeeQuery) HandleMaxWithdraw( + ctx context.Context, + qs lvtypes.QueryServer, +) (proto.Message, error) { + req := &lvtypes.QueryMaxWithdraw{Address: q.MaxWithdraw.Address, Denom: q.MaxWithdraw.Denom} + return qs.MaxWithdraw(ctx, req) +} + +// HandleMaxBorrow queries max borrow. +func (q UmeeQuery) HandleMaxBorrow( + ctx context.Context, + qs lvtypes.QueryServer, +) (proto.Message, error) { + req := &lvtypes.QueryMaxBorrow{Address: q.MaxBorrow.Address, Denom: q.MaxBorrow.Address} + return qs.MaxBorrow(ctx, req) +} diff --git a/app/wasm/query/handle_oracle.go b/app/wasm/query/handle_oracle.go new file mode 100644 index 0000000000..2f328b3789 --- /dev/null +++ b/app/wasm/query/handle_oracle.go @@ -0,0 +1,110 @@ +package query + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/gogo/protobuf/proto" + + octypes "github.com/umee-network/umee/v4/x/oracle/types" +) + +// HandleFeederDelegation gets all the feeder delegation of a validator. +func (q UmeeQuery) HandleFeederDelegation( + ctx sdk.Context, + qs octypes.QueryServer, +) (proto.Message, error) { + req := &octypes.QueryFeederDelegation{ValidatorAddr: q.FeederDelegation.ValidatorAddr} + return qs.FeederDelegation(sdk.WrapSDKContext(ctx), req) +} + +// HandleMissCounter gets all the oracle miss counter of a validator. +func (q UmeeQuery) HandleMissCounter( + ctx sdk.Context, + qs octypes.QueryServer, +) (proto.Message, error) { + req := &octypes.QueryMissCounter{ValidatorAddr: q.MissCounter.ValidatorAddr} + return qs.MissCounter(sdk.WrapSDKContext(ctx), req) +} + +// HandleSlashWindow gets slash window information. +func (q UmeeQuery) HandleSlashWindow( + ctx sdk.Context, + qs octypes.QueryServer, +) (proto.Message, error) { + return qs.SlashWindow(sdk.WrapSDKContext(ctx), &octypes.QuerySlashWindow{}) +} + +// HandleAggregatePrevote gets an aggregate prevote of a validator. +func (q UmeeQuery) HandleAggregatePrevote( + ctx sdk.Context, + qs octypes.QueryServer, +) (proto.Message, error) { + req := &octypes.QueryAggregatePrevote{ValidatorAddr: q.AggregatePrevote.ValidatorAddr} + return qs.AggregatePrevote(sdk.WrapSDKContext(ctx), req) +} + +// HandleAggregatePrevotes gets an aggregate prevote of all validators. +func (q UmeeQuery) HandleAggregatePrevotes( + ctx sdk.Context, + qs octypes.QueryServer, +) (proto.Message, error) { + return qs.AggregatePrevotes(sdk.WrapSDKContext(ctx), &octypes.QueryAggregatePrevotes{}) +} + +// HandleAggregateVote gets an aggregate vote of a validator. +func (q UmeeQuery) HandleAggregateVote( + ctx sdk.Context, + qs octypes.QueryServer, +) (proto.Message, error) { + req := &octypes.QueryAggregateVote{ValidatorAddr: q.AggregateVote.ValidatorAddr} + return qs.AggregateVote(sdk.WrapSDKContext(ctx), req) +} + +// HandleAggregateVotes gets an aggregate vote of all validators. +func (q UmeeQuery) HandleAggregateVotes( + ctx sdk.Context, + qs octypes.QueryServer, +) (proto.Message, error) { + return qs.AggregateVotes(sdk.WrapSDKContext(ctx), &octypes.QueryAggregateVotes{}) +} + +// HandleOracleParams gets the x/oracle module's parameters. +func (q UmeeQuery) HandleOracleParams( + ctx sdk.Context, + qs octypes.QueryServer, +) (proto.Message, error) { + return qs.Params(sdk.WrapSDKContext(ctx), &octypes.QueryParams{}) +} + +// HandleExchangeRates gets the exchange rates of all denoms. +func (q UmeeQuery) HandleExchangeRates( + ctx sdk.Context, + qs octypes.QueryServer, +) (proto.Message, error) { + return qs.ExchangeRates(sdk.WrapSDKContext(ctx), &octypes.QueryExchangeRates{Denom: q.ExchangeRates.Denom}) +} + +// HandleActiveExchangeRates gets all active denoms. +func (q UmeeQuery) HandleActiveExchangeRates( + ctx sdk.Context, + qs octypes.QueryServer, +) (proto.Message, error) { + return qs.ActiveExchangeRates(sdk.WrapSDKContext(ctx), &octypes.QueryActiveExchangeRates{}) +} + +// HandleMedians gets medians. +func (q UmeeQuery) HandleMedians( + ctx sdk.Context, + qs octypes.QueryServer, +) (proto.Message, error) { + req := &octypes.QueryMedians{Denom: q.Medians.Denom, NumStamps: q.Medians.NumStamps} + return qs.Medians(sdk.WrapSDKContext(ctx), req) +} + +// HandleMedians gets median deviations. +func (q UmeeQuery) HandleMedianDeviations( + ctx sdk.Context, + qs octypes.QueryServer, +) (proto.Message, error) { + req := &octypes.QueryMedianDeviations{Denom: q.MedianDeviations.Denom} + return qs.MedianDeviations(sdk.WrapSDKContext(ctx), req) +} diff --git a/app/wasm/query/plugin.go b/app/wasm/query/plugin.go new file mode 100644 index 0000000000..8e717ed6d5 --- /dev/null +++ b/app/wasm/query/plugin.go @@ -0,0 +1,101 @@ +package query + +import ( + "encoding/json" + "fmt" + + sdkerrors "cosmossdk.io/errors" + wasmvmtypes "github.com/CosmWasm/wasmvm/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/gogo/protobuf/proto" + + lvkeeper "github.com/umee-network/umee/v4/x/leverage/keeper" + lvtypes "github.com/umee-network/umee/v4/x/leverage/types" + ockeeper "github.com/umee-network/umee/v4/x/oracle/keeper" + ocpes "github.com/umee-network/umee/v4/x/oracle/types" +) + +// Plugin wraps the query plugin with queriers. +type Plugin struct { + lvQueryServer lvtypes.QueryServer + ocQueryServer ocpes.QueryServer +} + +// NewQueryPlugin creates a plugin to query native modules. +func NewQueryPlugin( + leverageKeeper lvkeeper.Keeper, + oracleKeeper ockeeper.Keeper, +) *Plugin { + return &Plugin{ + lvQueryServer: lvkeeper.NewQuerier(leverageKeeper), + ocQueryServer: ockeeper.NewQuerier(oracleKeeper), + } +} + +// CustomQuerier implements custom querier for wasm smartcontracts acess umee native modules. +func (plugin *Plugin) CustomQuerier() func(ctx sdk.Context, request json.RawMessage) ([]byte, error) { + return func(ctx sdk.Context, request json.RawMessage) ([]byte, error) { + var smartcontractQuery UmeeQuery + if err := json.Unmarshal(request, &smartcontractQuery); err != nil { + return nil, sdkerrors.Wrap(err, "umee query") + } + + var resp proto.Message + var err error + + switch { + case smartcontractQuery.LeverageParameters != nil: + resp, err = smartcontractQuery.HandleLeverageParams(ctx, plugin.lvQueryServer) + case smartcontractQuery.RegisteredTokens != nil: + resp, err = smartcontractQuery.HandleRegisteredTokens(ctx, plugin.lvQueryServer) + case smartcontractQuery.MarketSummary != nil: + resp, err = smartcontractQuery.HandleMarketSummary(ctx, plugin.lvQueryServer) + case smartcontractQuery.AccountBalances != nil: + resp, err = smartcontractQuery.HandleAccountBalances(ctx, plugin.lvQueryServer) + case smartcontractQuery.AccountSummary != nil: + resp, err = smartcontractQuery.HandleAccountSummary(ctx, plugin.lvQueryServer) + case smartcontractQuery.LiquidationTargets != nil: + resp, err = smartcontractQuery.HandleLiquidationTargets(ctx, plugin.lvQueryServer) + case smartcontractQuery.BadDebts != nil: + resp, err = smartcontractQuery.HandleBadDebts(ctx, plugin.lvQueryServer) + case smartcontractQuery.MaxWithdraw != nil: + resp, err = smartcontractQuery.HandleMaxWithdraw(ctx, plugin.lvQueryServer) + case smartcontractQuery.MaxBorrow != nil: + resp, err = smartcontractQuery.HandleMaxBorrow(ctx, plugin.lvQueryServer) + + case smartcontractQuery.FeederDelegation != nil: + resp, err = smartcontractQuery.HandleFeederDelegation(ctx, plugin.ocQueryServer) + case smartcontractQuery.MissCounter != nil: + resp, err = smartcontractQuery.HandleMissCounter(ctx, plugin.ocQueryServer) + case smartcontractQuery.SlashWindow != nil: + resp, err = smartcontractQuery.HandleSlashWindow(ctx, plugin.ocQueryServer) + case smartcontractQuery.AggregatePrevote != nil: + resp, err = smartcontractQuery.HandleAggregatePrevote(ctx, plugin.ocQueryServer) + case smartcontractQuery.AggregatePrevotes != nil: + resp, err = smartcontractQuery.HandleAggregatePrevotes(ctx, plugin.ocQueryServer) + case smartcontractQuery.AggregateVote != nil: + resp, err = smartcontractQuery.HandleAggregateVote(ctx, plugin.ocQueryServer) + case smartcontractQuery.AggregateVotes != nil: + resp, err = smartcontractQuery.HandleAggregateVotes(ctx, plugin.ocQueryServer) + case smartcontractQuery.OracleParams != nil: + resp, err = smartcontractQuery.HandleOracleParams(ctx, plugin.ocQueryServer) + case smartcontractQuery.ExchangeRates != nil: + resp, err = smartcontractQuery.HandleExchangeRates(ctx, plugin.ocQueryServer) + case smartcontractQuery.ActiveExchangeRates != nil: + resp, err = smartcontractQuery.HandleActiveExchangeRates(ctx, plugin.ocQueryServer) + case smartcontractQuery.Medians != nil: + resp, err = smartcontractQuery.HandleMedians(ctx, plugin.ocQueryServer) + case smartcontractQuery.MedianDeviations != nil: + resp, err = smartcontractQuery.HandleMedianDeviations(ctx, plugin.ocQueryServer) + + default: + return nil, wasmvmtypes.UnsupportedRequest{Kind: "invalid umee query"} + } + + if err != nil { + return nil, wasmvmtypes.UnsupportedRequest{Kind: fmt.Sprintf("error %+v while query the assigned query ", err)} + } + + return MarshalResponse(resp) + } +} diff --git a/app/wasm/query/types.go b/app/wasm/query/types.go new file mode 100644 index 0000000000..cc0e2c19b5 --- /dev/null +++ b/app/wasm/query/types.go @@ -0,0 +1,68 @@ +package query + +import ( + "encoding/json" + "fmt" + + wasmvmtypes "github.com/CosmWasm/wasmvm/types" + lvtypes "github.com/umee-network/umee/v4/x/leverage/types" + octypes "github.com/umee-network/umee/v4/x/oracle/types" +) + +// UmeeQuery wraps all the queries availables for cosmwasm smartcontracts. +type UmeeQuery struct { + // Leverage queries + // Used to query the x/leverage module's parameters. + LeverageParameters *lvtypes.QueryParams `json:"leverage_parameters,omitempty"` + // Used to query all the registered tokens. + RegisteredTokens *lvtypes.QueryRegisteredTokens `json:"registered_tokens,omitempty"` + // Used to get the summary data of an denom. + MarketSummary *lvtypes.QueryMarketSummary `json:"market_summary,omitempty"` + // Used to get an account's current supply, collateral, and borrow positions. + AccountBalances *lvtypes.QueryAccountBalances `json:"account_balances,omitempty"` + // Used to queries USD values representing an account's total positions and borrowing limits. + AccountSummary *lvtypes.QueryAccountSummary `json:"account_summary,omitempty"` + // request to return a list of borrower addresses eligible for liquidation. + LiquidationTargets *lvtypes.QueryLiquidationTargets `json:"liquidation_targets,omitempty"` + // requet to returns list of bad debts + BadDebts *lvtypes.QueryBadDebts `json:"bad_debts_params,omitempty"` + // request to returns max withdraw + MaxWithdraw *lvtypes.QueryMaxWithdraw `json:"max_withdraw_params,omitempty"` + // request to get max borrows + MaxBorrow *lvtypes.QueryMaxBorrow `json:"max_borrow_params,omitempty"` + + // oracle queries + // Used to get all feeder delegation of a validator. + FeederDelegation *octypes.QueryFeederDelegation `json:"feeder_delegation,omitempty"` + // Used to get all the oracle miss counter of a validator. + MissCounter *octypes.QueryMissCounter `json:"miss_counter,omitempty"` + // Used to get information of an slash window. + SlashWindow *octypes.QuerySlashWindow `json:"slash_window,omitempty"` + // Used to get an aggregate prevote of a validator. + AggregatePrevote *octypes.QueryAggregatePrevote `json:"aggregate_prevote,omitempty"` + // Used to get an aggregate prevote of all validators. + AggregatePrevotes *octypes.QueryAggregatePrevotes `json:"aggregate_prevotes,omitempty"` + // Used to get an aggregate vote of a validator. + AggregateVote *octypes.QueryAggregateVote `json:"aggregate_vote,omitempty"` + // Used to get an aggregate vote of all validators. + AggregateVotes *octypes.QueryAggregateVotes `json:"aggregate_votes,omitempty"` + // Used to query the x/oracle module's parameters. + OracleParams *octypes.QueryParams `json:"oracle_params,omitempty"` + // Used to get the exchange rates of all denoms. + ExchangeRates *octypes.QueryExchangeRates `json:"exchange_rates,omitempty"` + // Used to get all active denoms. + ActiveExchangeRates *octypes.QueryActiveExchangeRates `json:"active_exchange_rates,omitempty"` + // Used to get all medians. + Medians *octypes.QueryMedians `json:"medians,omitempty"` + // Used to get all median deviations. + MedianDeviations *octypes.QueryMedianDeviations `json:"median_deviations,omitempty"` +} + +// MarshalResponse marshals any response. +func MarshalResponse(resp interface{}) ([]byte, error) { + bz, err := json.Marshal(resp) + if err != nil { + return nil, wasmvmtypes.UnsupportedRequest{Kind: fmt.Sprintf("error %+v umee query response error on marshal", err)} + } + return bz, err +} diff --git a/app/wasm/test/cosmwasm_test.go b/app/wasm/test/cosmwasm_test.go new file mode 100644 index 0000000000..d153bc801c --- /dev/null +++ b/app/wasm/test/cosmwasm_test.go @@ -0,0 +1,22 @@ +package wasm_test + +import "testing" + +func TestCosmwasm(t *testing.T) { + its := new(IntegrationTestSuite) + // setup the test configuration + its.SetupTest(t) + + // testing cw_plus base contract + its.TestCw20Store() + its.TestCw20Instantiate() + its.TestCw20ContractInfo() + its.TestCw20CheckBalance() + its.TestCw20Transfer() + + // testing the umee cosmwasm queries + its.InitiateUmeeCosmwasm() + its.TestLeverageQueries() + its.TestOracleQueries() + its.TestLeverageTxs() +} diff --git a/app/wasm/test/cw20_base_test.go b/app/wasm/test/cw20_base_test.go new file mode 100644 index 0000000000..818650960d --- /dev/null +++ b/app/wasm/test/cw20_base_test.go @@ -0,0 +1,358 @@ +package wasm_test + +import ( + "encoding/json" + "fmt" + "os" + "strconv" + "testing" + "time" + + "gotest.tools/v3/assert" + + "cosmossdk.io/math" + wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" + wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/simapp" + "github.com/cosmos/cosmos-sdk/simapp/params" + sdk "github.com/cosmos/cosmos-sdk/types" + govv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" + minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + tmrand "github.com/tendermint/tendermint/libs/rand" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + + umeeapp "github.com/umee-network/umee/v4/app" + appparams "github.com/umee-network/umee/v4/app/params" + wm "github.com/umee-network/umee/v4/app/wasm/msg" + wq "github.com/umee-network/umee/v4/app/wasm/query" + "github.com/umee-network/umee/v4/x/oracle/types" +) + +const ( + initialPower = int64(10000000000000) + cw20Artifact = "../../../tests/artifacts/cw20_base.wasm" + umeeArtifact = "../../../tests/artifacts/umee_cosmwasm-aarch64.wasm" + cw20Label = "cw20InstanceTest" +) + +// Test addresses +var ( + valPubKeys = simapp.CreateTestPubKeys(2) + + valPubKey = valPubKeys[0] + pubKey = secp256k1.GenPrivKey().PubKey() + addr = sdk.AccAddress(pubKey.Address()) + valAddr = sdk.ValAddress(pubKey.Address()) + + valPubKey2 = valPubKeys[1] + pubKey2 = secp256k1.GenPrivKey().PubKey() + addr2 = sdk.AccAddress(pubKey2.Address()) + valAddr2 = sdk.ValAddress(pubKey2.Address()) + + initTokens = sdk.TokensFromConsensusPower(initialPower, sdk.DefaultPowerReduction) + initCoins = sdk.NewCoins(sdk.NewCoin(appparams.BondDenom, initTokens)) +) + +type cw20InitMsg struct { + Name string `json:"name"` + Symbol string `json:"symbol"` + Decimals uint8 `json:"decimals"` + InitialBalances []Balance `json:"initial_balances"` +} + +type InstantiateMsg struct { +} + +type Address struct { + Address string `json:"address"` +} + +type Balance struct { + Address + Amount uint64 `json:"amount,string"` +} + +type cw20QueryBalance struct { + Balance struct { + Address + } `json:"balance"` +} + +type cw20QueryBalanceResp struct { + Balance string `json:"balance"` +} + +type cw20ExecMsg struct { + Transfer *transferMsg `json:"transfer,omitempty"` +} + +type transferMsg struct { + Recipient string `json:"recipient"` + Amount uint64 `json:"amount,string"` +} + +type CustomQuery struct { + Chain struct { + Custom wq.UmeeQuery `json:"custom"` + } `json:"chain"` +} + +type ExecuteMsg struct { + Umee struct { + Leverage wm.UmeeMsg `json:"leverage"` + } `json:"umee"` +} + +func UmeeCwCustomQuery(umeeCWQuery wq.UmeeQuery) CustomQuery { + c := CustomQuery{} + c.Chain.Custom = umeeCWQuery + return c +} + +func UmeeCwCustomTx(customMsg wm.UmeeMsg) ExecuteMsg { + c := ExecuteMsg{} + c.Umee.Leverage = customMsg + return c +} + +type IntegrationTestSuite struct { + T *testing.T + ctx sdk.Context + app *umeeapp.UmeeApp + + wasmMsgServer wasmtypes.MsgServer + wasmQueryClient wasmtypes.QueryClient + wasmProposalHandler govv1.Handler + + codeID uint64 + contractAddr string + encfg params.EncodingConfig +} + +func (s *IntegrationTestSuite) SetupTest(t *testing.T) { + app := umeeapp.Setup(t) + ctx := app.BaseApp.NewContext(false, tmproto.Header{ + ChainID: fmt.Sprintf("test-chain-%s", tmrand.Str(4)), + Height: 9, + Time: time.Date(2022, 4, 20, 10, 20, 15, 1, time.UTC), + }) + + // mint and send coins to addrs + assert.NilError(t, app.BankKeeper.MintCoins(ctx, minttypes.ModuleName, initCoins.MulInt(sdk.NewInt(10)))) + assert.NilError(t, app.BankKeeper.SendCoinsFromModuleToAccount(ctx, minttypes.ModuleName, addr, initCoins)) + assert.NilError(t, app.BankKeeper.MintCoins(ctx, minttypes.ModuleName, initCoins)) + assert.NilError(t, app.BankKeeper.SendCoinsFromModuleToAccount(ctx, minttypes.ModuleName, addr2, initCoins)) + s.T = t + s.app = app + s.ctx = ctx + s.wasmMsgServer = wasmkeeper.NewMsgServerImpl(wasmkeeper.NewDefaultPermissionKeeper(app.WasmKeeper)) + querier := app.GRPCQueryRouter() + wasmtypes.RegisterMsgServer(querier, s.wasmMsgServer) + + queryHelper := baseapp.NewQueryServerTestHelper(ctx, app.InterfaceRegistry()) + grpc := wasmkeeper.Querier(&app.WasmKeeper) + wasmtypes.RegisterQueryServer(queryHelper, grpc) + s.wasmQueryClient = wasmtypes.NewQueryClient(queryHelper) + s.wasmProposalHandler = wasmkeeper.NewWasmProposalHandler(app.WasmKeeper, umeeapp.GetWasmEnabledProposals()) + s.encfg = umeeapp.MakeEncodingConfig() +} + +// NewTestMsgCreateValidator test msg creator +func NewTestMsgCreateValidator(address sdk.ValAddress, pubKey cryptotypes.PubKey, amt math.Int) *stakingtypes.MsgCreateValidator { + commission := stakingtypes.NewCommissionRates(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()) + msg, _ := stakingtypes.NewMsgCreateValidator( + address, pubKey, sdk.NewCoin(types.UmeeDenom, amt), + stakingtypes.Description{}, commission, sdk.OneInt(), + ) + + return msg +} + +func (s *IntegrationTestSuite) cw20StoreCode(sender sdk.AccAddress, cwArtifacePath string) (codeId uint64) { + cw20Code, err := os.ReadFile(cwArtifacePath) + assert.NilError(s.T, err) + storeCodeProposal := wasmtypes.StoreCodeProposal{ + Title: cwArtifacePath, + Description: cwArtifacePath, + RunAs: sender.String(), + WASMByteCode: cw20Code, + InstantiatePermission: &wasmtypes.AllowEverybody, + } + + err = s.wasmProposalHandler(s.ctx, &storeCodeProposal) + assert.NilError(s.T, err) + + codes, err := s.wasmQueryClient.PinnedCodes(sdk.WrapSDKContext(s.ctx), &wasmtypes.QueryPinnedCodesRequest{}) + assert.NilError(s.T, err) + assert.Equal(s.T, true, len(codes.CodeIDs) > 0) + + return codes.CodeIDs[len(codes.CodeIDs)-1] +} + +func (s *IntegrationTestSuite) transfer(contracAddr string, amount uint64, from, to sdk.AccAddress) { + s.contractAddr = contracAddr + msg := cw20ExecMsg{Transfer: &transferMsg{ + Recipient: to.String(), + Amount: amount, + }} + transferBz, err := json.Marshal(msg) + assert.NilError(s.T, err) + + s.execContract(from, transferBz) +} + +func (s *IntegrationTestSuite) execContract(sender sdk.AccAddress, msg []byte) { + _, err := s.wasmMsgServer.ExecuteContract(sdk.WrapSDKContext(s.ctx), &wasmtypes.MsgExecuteContract{ + Sender: sender.String(), + Contract: s.contractAddr, + Msg: msg, + }) + assert.NilError(s.T, err) +} + +func (s *IntegrationTestSuite) queryContract(q []byte) *wasmtypes.QuerySmartContractStateResponse { + resp, err := s.wasmQueryClient.SmartContractState(sdk.WrapSDKContext(s.ctx), &wasmtypes.QuerySmartContractStateRequest{ + Address: s.contractAddr, QueryData: q, + }) + assert.NilError(s.T, err) + return resp +} + +func (s *IntegrationTestSuite) queryBalance(contracAddr string, address sdk.AccAddress) uint64 { + queryBobBalance := cw20QueryBalance{ + Balance: struct{ Address }{ + Address: Address{ + Address: address.String(), + }, + }, + } + queryBobBalanceBz, err := json.Marshal(queryBobBalance) + assert.NilError(s.T, err) + + queryBobBalanceResp, err := s.wasmQueryClient.SmartContractState(sdk.WrapSDKContext(s.ctx), &wasmtypes.QuerySmartContractStateRequest{Address: contracAddr, QueryData: queryBobBalanceBz}) + assert.NilError(s.T, err) + + var bobBalanceResp cw20QueryBalanceResp + err = json.Unmarshal(queryBobBalanceResp.Data, &bobBalanceResp) + assert.NilError(s.T, err) + + bobBalanceUint, err := strconv.ParseUint(bobBalanceResp.Balance, 10, 64) + assert.NilError(s.T, err) + return bobBalanceUint +} + +func intMsgCw20(addr2Amount uint64) cw20InitMsg { + return cw20InitMsg{ + Name: "Cw20TestToken", + Symbol: "CashSymbol", + Decimals: 4, + InitialBalances: []Balance{ + { + Address: Address{ + Address: addr.String(), + }, + Amount: 1003, + }, + { + Address: Address{ + Address: addr2.String(), + }, + Amount: addr2Amount, + }, + }, + } +} + +func (s *IntegrationTestSuite) cw20InitiateCode(sender sdk.AccAddress, init interface{}) (*wasmtypes.MsgInstantiateContractResponse, error) { + initBz, err := json.Marshal(init) + assert.NilError(s.T, err) + + initMsg := wasmtypes.MsgInstantiateContract{ + Sender: sender.String(), + CodeID: s.codeID, + Label: cw20Label, + Funds: sdk.Coins{sdk.NewCoin(appparams.BondDenom, sdk.NewIntFromUint64(10))}, + Msg: initBz, + Admin: sender.String(), + } + return s.wasmMsgServer.InstantiateContract(sdk.WrapSDKContext(s.ctx), &initMsg) +} + +func (s *IntegrationTestSuite) TestCw20Store() { + codeID := s.cw20StoreCode(addr, cw20Artifact) + s.codeID = codeID + assert.Equal(s.T, uint64(1), codeID) +} + +func (s *IntegrationTestSuite) TestCw20Instantiate() { + intantiateResp, err := s.cw20InitiateCode(addr, intMsgCw20(200000)) + assert.NilError(s.T, err) + assert.Equal(s.T, "umee14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9scsdqqx", intantiateResp.Address) + s.contractAddr = intantiateResp.Address +} + +func (s *IntegrationTestSuite) TestCw20ContractInfo() { + sender := addr + intantiateResp, err := s.cw20InitiateCode(sender, intMsgCw20(200)) + assert.NilError(s.T, err) + + cw20ContractInfo, err := s.wasmQueryClient.ContractInfo(sdk.WrapSDKContext(s.ctx), &wasmtypes.QueryContractInfoRequest{Address: intantiateResp.Address}) + assert.NilError(s.T, err) + assert.Equal(s.T, uint64(1), cw20ContractInfo.CodeID) + assert.Equal(s.T, sender.String(), cw20ContractInfo.Admin) + assert.Equal(s.T, cw20Label, cw20ContractInfo.Label) + assert.Equal(s.T, "umee1suhgf5svhu4usrurvxzlgn54ksxmn8gljarjtxqnapv8kjnp4nrs89p4g0", cw20ContractInfo.Address) +} + +func (s *IntegrationTestSuite) TestCw20CheckBalance() { + sender, bobAddr, bobAmount := addr, addr2, uint64(2500) + + intantiateResp, err := s.cw20InitiateCode(sender, intMsgCw20(bobAmount)) + assert.NilError(s.T, err) + + bobBalanceUint := s.queryBalance(intantiateResp.Address, bobAddr) + assert.Equal(s.T, bobAmount, bobBalanceUint) +} + +func (s *IntegrationTestSuite) TestCw20Transfer() { + sender, bobAddr, bobAmount := addr, addr2, uint64(2500) + + intantiateResp, err := s.cw20InitiateCode(sender, intMsgCw20(bobAmount)) + assert.NilError(s.T, err) + + contracAddr := intantiateResp.Address + amountToTransfer := uint64(100) + + s.transfer(contracAddr, amountToTransfer, addr, bobAddr) + + bobBalanceUint := s.queryBalance(contracAddr, bobAddr) + assert.Equal(s.T, bobAmount+amountToTransfer, bobBalanceUint) +} + +func (s *IntegrationTestSuite) InitiateUmeeCosmwasm() { + // umee cosmwasm contract upload + codeID := s.cw20StoreCode(addr2, umeeArtifact) + assert.Equal(s.T, uint64(2), codeID) + s.codeID = codeID + + // initiate the umee cw contract + intantiateResp, err := s.cw20InitiateCode(addr2, new(InstantiateMsg)) + assert.NilError(s.T, err) + s.contractAddr = intantiateResp.Address +} + +func (s *IntegrationTestSuite) genCustomQuery(umeeQuery wq.UmeeQuery) []byte { + cq, err := json.Marshal(UmeeCwCustomQuery(umeeQuery)) + assert.NilError(s.T, err) + return cq +} + +func (s *IntegrationTestSuite) genCustomTx(msg wm.UmeeMsg) []byte { + cq, err := json.Marshal(UmeeCwCustomTx(msg)) + assert.NilError(s.T, err) + return cq +} diff --git a/app/wasm/test/umee_cw_test.go b/app/wasm/test/umee_cw_test.go new file mode 100644 index 0000000000..c4a63e82ae --- /dev/null +++ b/app/wasm/test/umee_cw_test.go @@ -0,0 +1,225 @@ +package wasm_test + +import ( + "encoding/json" + "testing" + + wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" + sdk "github.com/cosmos/cosmos-sdk/types" + minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" + appparams "github.com/umee-network/umee/v4/app/params" + wm "github.com/umee-network/umee/v4/app/wasm/msg" + wq "github.com/umee-network/umee/v4/app/wasm/query" + lvtypes "github.com/umee-network/umee/v4/x/leverage/types" + "github.com/umee-network/umee/v4/x/oracle/types" + "gotest.tools/v3/assert" +) + +func (s *IntegrationTestSuite) TestLeverageQueries() { + tests := []struct { + Name string + CQ []byte + ResponseCheck func(data []byte) + }{ + { + Name: "leverage query params", + CQ: s.genCustomQuery(wq.UmeeQuery{ + LeverageParameters: &lvtypes.QueryParams{}, + }), + ResponseCheck: func(data []byte) { + var rr lvtypes.QueryParamsResponse + err := json.Unmarshal(data, &rr) + assert.NilError(s.T, err) + assert.DeepEqual(s.T, rr.Params, lvtypes.DefaultParams()) + }, + }, + { + Name: "query all registered tokens", + CQ: s.genCustomQuery(wq.UmeeQuery{ + RegisteredTokens: &lvtypes.QueryRegisteredTokens{}, + }), + ResponseCheck: func(data []byte) { + var rr lvtypes.QueryRegisteredTokensResponse + err := json.Unmarshal(data, &rr) + assert.NilError(s.T, err) + assert.Equal(s.T, true, len(rr.Registry) > 0) + }, + }, + { + Name: "query registered token", + CQ: s.genCustomQuery(wq.UmeeQuery{ + RegisteredTokens: &lvtypes.QueryRegisteredTokens{ + BaseDenom: appparams.BondDenom, + }, + }), + ResponseCheck: func(data []byte) { + var rr lvtypes.QueryRegisteredTokensResponse + err := json.Unmarshal(data, &rr) + assert.NilError(s.T, err) + assert.Equal(s.T, true, len(rr.Registry) > 0) + assert.Equal(s.T, appparams.BondDenom, rr.Registry[0].BaseDenom) + }, + }, + { + Name: "market summary", + CQ: s.genCustomQuery(wq.UmeeQuery{ + MarketSummary: &lvtypes.QueryMarketSummary{ + Denom: appparams.BondDenom, + }, + }), + ResponseCheck: func(data []byte) { + var rr lvtypes.QueryMarketSummaryResponse + err := json.Unmarshal(data, &rr) + assert.NilError(s.T, err) + assert.Equal(s.T, "UMEE", rr.SymbolDenom) + }, + }, + { + Name: "query bad debts", + CQ: s.genCustomQuery(wq.UmeeQuery{ + BadDebts: &lvtypes.QueryBadDebts{}, + }), + ResponseCheck: func(data []byte) { + var rr lvtypes.QueryBadDebtsResponse + err := json.Unmarshal(data, &rr) + assert.NilError(s.T, err) + assert.Equal(s.T, true, len(rr.Targets) == 0) + }, + }, + { + Name: "query max withdraw (zero)", + CQ: s.genCustomQuery(wq.UmeeQuery{ + MaxWithdraw: &lvtypes.QueryMaxWithdraw{ + Address: addr.String(), + Denom: appparams.BondDenom, + }, + }), + ResponseCheck: func(data []byte) { + var rr lvtypes.QueryMaxWithdrawResponse + err := json.Unmarshal(data, &rr) + assert.NilError(s.T, err) + assert.Equal(s.T, true, len(rr.Tokens) == 0) + assert.Equal(s.T, true, len(rr.UTokens) == 0) + }, + }, + { + Name: "query max borrow (zero)", + CQ: s.genCustomQuery(wq.UmeeQuery{ + MaxBorrow: &lvtypes.QueryMaxBorrow{ + Address: addr.String(), + Denom: appparams.BondDenom, + }, + }), + ResponseCheck: func(data []byte) { + var rr lvtypes.QueryMaxBorrowResponse + err := json.Unmarshal(data, &rr) + assert.NilError(s.T, err) + assert.Equal(s.T, true, len(rr.Tokens) == 0) + }, + }, + } + + for _, tc := range tests { + s.T.Run(tc.Name, func(t *testing.T) { + resp, err := s.wasmQueryClient.SmartContractState(sdk.WrapSDKContext(s.ctx), &wasmtypes.QuerySmartContractStateRequest{ + Address: s.contractAddr, QueryData: tc.CQ, + }) + assert.NilError(s.T, err) + tc.ResponseCheck(resp.Data) + }) + } +} + +func (s *IntegrationTestSuite) TestOracleQueries() { + tests := []struct { + Name string + CQ []byte + ResponseCheck func(data []byte) + }{ + { + Name: "oracle query params", + CQ: s.genCustomQuery(wq.UmeeQuery{ + OracleParams: &types.QueryParams{}, + }), + ResponseCheck: func(data []byte) { + var rr types.QueryParamsResponse + err := json.Unmarshal(data, &rr) + assert.NilError(s.T, err) + assert.DeepEqual(s.T, rr.Params, types.DefaultParams()) + }, + }, + { + Name: "oracle slash window", + CQ: s.genCustomQuery(wq.UmeeQuery{ + SlashWindow: &types.QuerySlashWindow{}, + }), + ResponseCheck: func(data []byte) { + var rr types.QuerySlashWindowResponse + err := json.Unmarshal(data, &rr) + assert.NilError(s.T, err) + }, + }, + } + + for _, tc := range tests { + s.T.Run(tc.Name, func(t *testing.T) { + resp := s.queryContract(tc.CQ) + tc.ResponseCheck(resp.Data) + }) + } +} + +func (s *IntegrationTestSuite) TestLeverageTxs() { + accAddr := sdk.MustAccAddressFromBech32(s.contractAddr) + err := s.app.BankKeeper.SendCoinsFromModuleToAccount(s.ctx, minttypes.ModuleName, accAddr, sdk.NewCoins(sdk.NewCoin(appparams.BondDenom, sdk.NewInt(100000)))) + assert.NilError(s.T, err) + txTests := []struct { + Name string + Msg []byte + }{ + { + Name: "supply", + Msg: s.genCustomTx(wm.UmeeMsg{ + Supply: &lvtypes.MsgSupply{ + Supplier: s.contractAddr, + Asset: sdk.NewCoin(appparams.BondDenom, sdk.NewInt(700)), + }, + }), + }, + { + Name: "add collateral", + Msg: s.genCustomTx(wm.UmeeMsg{ + Collateralize: &lvtypes.MsgCollateralize{ + Borrower: s.contractAddr, + Asset: sdk.NewCoin("u/uumee", sdk.NewInt(700)), + }, + }), + }, + // { + // Name: "borrow", + // Msg: s.genCustomTx(wm.UmeeMsg{ + // Borrow: &lvtypes.MsgBorrow{ + // Borrower: addr2.String(), + // Asset: sdk.NewCoin(appparams.BondDenom, sdk.NewInt(150)), + // }, + // }), + // }, + } + + for _, tc := range txTests { + s.T.Run(tc.Name, func(t *testing.T) { + s.execContract(addr2, tc.Msg) + }) + } + + query := s.genCustomQuery(wq.UmeeQuery{ + AccountSummary: &lvtypes.QueryAccountSummary{ + Address: addr2.String(), + }, + }) + + resp := s.queryContract(query) + var rr lvtypes.QueryAccountSummaryResponse + err = json.Unmarshal(resp.Data, &rr) + assert.NilError(s.T, err) +} diff --git a/tests/artifacts/cw20_base.wasm b/tests/artifacts/cw20_base.wasm new file mode 100644 index 0000000000..bb3eeaa0cf Binary files /dev/null and b/tests/artifacts/cw20_base.wasm differ diff --git a/tests/artifacts/umee_cosmwasm-aarch64.wasm b/tests/artifacts/umee_cosmwasm-aarch64.wasm new file mode 100644 index 0000000000..3b07737024 Binary files /dev/null and b/tests/artifacts/umee_cosmwasm-aarch64.wasm differ