Skip to content

Commit

Permalink
Merge branch 'master' into milad/update-cosmovisor-1.6
Browse files Browse the repository at this point in the history
  • Loading branch information
miladz68 authored Dec 10, 2024
2 parents a1ee1d6 + 7e66c81 commit 6ed9925
Show file tree
Hide file tree
Showing 9 changed files with 157 additions and 62 deletions.
34 changes: 12 additions & 22 deletions x/asset/ft/keeper/test-contracts/asset-extension/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,18 @@ use crate::msg::{
use crate::state::{DENOM, EXTRA_DATA};
use coreum_wasm_sdk::deprecated::core::{CoreumMsg, CoreumResult};
use coreum_wasm_sdk::types::coreum::asset::ft::v1::{
MsgBurn, MsgMint, QueryFrozenBalanceRequest, QueryFrozenBalanceResponse, QueryTokenRequest,
QueryTokenResponse, Token,
MsgBurn, MsgMint, QueryTokenRequest, QueryTokenResponse, Token,
};
use coreum_wasm_sdk::types::cosmos::bank::v1beta1::{
MsgSend, QueryBalanceRequest, QueryBalanceResponse,
MsgSend,
};
use coreum_wasm_sdk::types::cosmos::base::v1beta1::Coin;
use cosmwasm_std::{entry_point, to_json_binary, CosmosMsg, StdError};
use cosmwasm_std::{Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult, Uint128};
use cw2::set_contract_version;
use std::ops::Div;
use std::string::ToString;
use cosmwasm_schema::schemars::_serde_json::to_string;

// version info for migration info
const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME");
Expand Down Expand Up @@ -189,7 +189,15 @@ pub fn sudo_extension_place_order(
{
return Err(ContractError::DEXOrderPlacementError {});
}
Ok(Response::new().add_attribute("method", "extension_place_order"))

let order_data = to_string(&order).
map_err(|_| ContractError::Std(StdError::generic_err("failed to serialize order to json string")))?;

Ok(
Response::new()
.add_attribute("method", "extension_place_order")
.add_attribute("order_data", order_data)
)
}

#[cfg_attr(not(feature = "library"), entry_point)]
Expand Down Expand Up @@ -376,24 +384,6 @@ fn assert_burn_rate(
.add_message(CosmosMsg::Any(burn_message.to_any())))
}

fn query_frozen_balance(deps: Deps, account: &str, denom: &str) -> StdResult<Coin> {
let request = QueryFrozenBalanceRequest {
account: account.to_string(),
denom: denom.to_string(),
};
let frozen_balance: QueryFrozenBalanceResponse = request.query(&deps.querier)?;
Ok(frozen_balance.balance.unwrap_or_default())
}

fn query_bank_balance(deps: Deps, account: &str, denom: &str) -> StdResult<Coin> {
let request = QueryBalanceRequest {
address: account.to_string(),
denom: denom.to_string(),
};
let bank_balance: QueryBalanceResponse = request.query(&deps.querier)?;
Ok(bank_balance.balance.unwrap_or_default())
}

fn query_token(deps: Deps, denom: &str) -> StdResult<Token> {
let request = QueryTokenRequest {
denom: denom.to_string(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ pub struct DEXOrder {
#[serde(rename = "type")]
pub order_type: String,
pub id: String,
pub sequence: u64,
pub base_denom: String,
pub quote_denom: String,
pub price: Option<String>,
Expand Down
13 changes: 7 additions & 6 deletions x/asset/ft/spec/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -193,8 +193,6 @@ Same rules apply to receiving tokens over IBC transfer protocol if IBC is enable

When token is created, admin decides if users may send and receive it over IBC transfer protocol.
If IBC feature is disabled token can never leave the Coreum chain.
The IBC feature is incompatible with the extension feature. The reason for it is that extension can override
the full functionality of ibc, so allowing the two features together can lead to confusion.

### Clawback

Expand Down Expand Up @@ -265,13 +263,16 @@ The fields are:
- `ibc_purpose`: if it is an ibc transfer, indicates whether it's an outgoing, incoming, acknowledged or timed-out
transfer

_**Note**: The extension feature is not compatible with ibc and block smart contract feature. It will error out if you
try
to enable those features at the same time._
_**Note**: The extension feature is not compatible with block smart contract feature. It will error out if you
try to enable those features at the same time._

There is a sample implementation of extension in `x/asset/ft/keeper/test-contracts/asset-extension` which can be used to
take inspiration from, when implementing other extensions.

#### DEX extension

The `extension` is also integrate with the DEX check [DEX spec](../../../dex/spec/README.md#Extension) for more details.

### DEX unified ref amount.

The `unified_ref_amount` DEX setting can be updated by the token admin or gov.
Expand Down Expand Up @@ -300,7 +301,7 @@ the `dex_whitelisted_denoms` feature is not enabled or not the `whitelisted_deno
<tr>
<th rowspan="3"></th>
<th colspan="14">Features</th>
<th colspan="4">Extensions</th>
<th colspan="4">Extension</th>
</tr>
<tr>
<th colspan="2">Default</th>
Expand Down
1 change: 1 addition & 0 deletions x/asset/ft/types/dex.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ type DEXOrder struct {
Creator sdk.AccAddress `json:"creator"`
Type string `json:"type"`
ID string `json:"id"`
Sequence uint64 `json:"sequence"`
BaseDenom string `json:"base_denom"`
QuoteDenom string `json:"quote_denom"`
Price *string `json:"price,omitempty"` // might be nil
Expand Down
61 changes: 37 additions & 24 deletions x/dex/keeper/keeper_ft_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package keeper_test

import (
"encoding/json"
"fmt"
"testing"
"time"
Expand All @@ -18,14 +19,19 @@ import (
"github.com/samber/lo"
"github.com/stretchr/testify/require"

"github.com/CoreumFoundation/coreum/v5/testutil/event"
"github.com/CoreumFoundation/coreum/v5/testutil/simapp"
testcontracts "github.com/CoreumFoundation/coreum/v5/x/asset/ft/keeper/test-contracts"
assetfttypes "github.com/CoreumFoundation/coreum/v5/x/asset/ft/types"
"github.com/CoreumFoundation/coreum/v5/x/dex/types"
)

var (
const (
ExtensionOrderDataWASMAttribute = "order_data"
IDDEXOrderSuffixTrigger = "blocked"
)

var (
AmountDEXExpectToSpendTrigger = sdkmath.NewInt(103)
AmountDEXExpectToReceiveTrigger = sdkmath.NewInt(104)
)
Expand Down Expand Up @@ -84,7 +90,7 @@ func TestKeeper_PlaceOrderWithExtension(t *testing.T) {
wantDEXErr: false,
},
{
name: "sell_dex_error_spend_amount",
name: "sell_dex_error",
order: types.Order{
Creator: func() string {
creator, _ := testApp.GenAccount(sdkCtx)
Expand All @@ -101,24 +107,6 @@ func TestKeeper_PlaceOrderWithExtension(t *testing.T) {
},
wantDEXErr: true,
},
{
name: "sell_dex_error_order_id",
order: types.Order{
Creator: func() string {
creator, _ := testApp.GenAccount(sdkCtx)
return creator.String()
}(),
Type: types.ORDER_TYPE_LIMIT,
ID: uuid.Generate().String()[:10] + IDDEXOrderSuffixTrigger,
BaseDenom: denomWithExtension,
QuoteDenom: denom2,
Price: lo.ToPtr(types.MustNewPriceFromString("1")),
Quantity: sdkmath.NewInt(10),
Side: types.SIDE_SELL,
TimeInForce: types.TIME_IN_FORCE_GTC,
},
wantDEXErr: true,
},
{
name: "buy_positive",
order: types.Order{
Expand All @@ -138,7 +126,7 @@ func TestKeeper_PlaceOrderWithExtension(t *testing.T) {
wantDEXErr: false,
},
{
name: "buy_dex_error_receive_amount",
name: "buy_dex_error",
order: types.Order{
Creator: func() string {
creator, _ := testApp.GenAccount(sdkCtx)
Expand All @@ -164,13 +152,38 @@ func TestKeeper_PlaceOrderWithExtension(t *testing.T) {
testApp.MintAndSendCoin(t, sdkCtx, creator, sdk.NewCoins(lockedBalance))
fundOrderReserve(t, testApp, sdkCtx, creator)
if !tt.wantDEXErr {
sdkCtx = sdkCtx.WithEventManager(sdk.NewEventManager())
require.NoError(t, testApp.DEXKeeper.PlaceOrder(sdkCtx, tt.order))

// decode wasm events
orderStr, err := event.FindStringEventAttribute(
sdkCtx.EventManager().Events().ToABCIEvents(),
wasmtypes.WasmModuleEventType,
ExtensionOrderDataWASMAttribute,
)
require.NoError(t, err)

extensionOrderData := assetfttypes.DEXOrder{}
require.NoError(t, json.Unmarshal([]byte(orderStr), &extensionOrderData))

order, err := testApp.DEXKeeper.GetOrderByAddressAndID(sdkCtx, creator, tt.order.ID)
require.NoError(t, err)

require.Equal(t, assetfttypes.DEXOrder{
Creator: sdk.MustAccAddressFromBech32(order.Creator),
Type: order.Type.String(),
ID: order.ID,
Sequence: order.Sequence,
BaseDenom: order.BaseDenom,
QuoteDenom: order.QuoteDenom,
Price: lo.ToPtr(order.Price.String()),
Quantity: order.Quantity,
Side: order.Side.String(),
}, extensionOrderData)
} else {
err := testApp.DEXKeeper.PlaceOrder(sdkCtx, tt.order)
require.ErrorIs(t, err, assetfttypes.ErrExtensionCallFailed)
require.ErrorContains(
t,
err,
testApp.DEXKeeper.PlaceOrder(simapp.CopyContextWithMultiStore(sdkCtx), tt.order),
"wasm error: DEX order placement is failed",
)
}
Expand Down
1 change: 1 addition & 0 deletions x/dex/keeper/keeper_matching_result.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ func NewMatchingResult(order types.Order) (*MatchingResult, error) {
Creator: takerAddress,
Type: order.Type.String(),
ID: order.ID,
Sequence: order.Sequence,
BaseDenom: order.BaseDenom,
QuoteDenom: order.QuoteDenom,
Price: orderStrPrice,
Expand Down
97 changes: 94 additions & 3 deletions x/dex/spec/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,98 @@ This feature introduces an enhancement to the asset FT (`restrict_dex` feature),
This adds a layer of control over trading pairs, ensuring that denom(asset FT) can only be exchanged with certain
denoms/currencies or assets, as specified by the admin.

### Extensions
### Extension

The `extension` feature integrates the DEX with the smart contract extension capability of asset FT. This integration
enables asset FT extension contracts to define custom extension functions that the DEX invokes before executing an
order. The extension functions can leverage order details, including the amounts expected to be spent and received
post-execution, to implement custom rules. For example, they can validate an order against specific business logic or
constraints.

#### Asset FT features handling

If the extension feature is enabled for a token but no contract extension function is implemented, all order
placements will fail. When multiple asset FT features are enabled, the rules of the asset FT features are validated
first, followed by invoking the extension.

#### Example of WASM Call Implementation in an Extension Smart Contract

Below is an example showcasing how to implement the extension functionality in a smart contract:

##### Code Example

```rust
#[cw_serde]
pub struct DEXOrder {
pub creator: String,
#[serde(rename = "type")]
pub order_type: String,
pub id: String,
pub base_denom: String,
pub quote_denom: String,
pub price: Option<String>,
pub quantity: Uint128,
pub side: String,
}

pub enum SudoMsg {
ExtensionTransfer {
recipient: String,
sender: String,
transfer_amount: Uint128,
commission_amount: Uint128,
burn_amount: Uint128,
context: TransferContext,
},
ExtensionPlaceOrder {
order: DEXOrder,
expected_to_spend: Coin,
expected_to_receive: Coin,
},
}

#[entry_point]
pub fn sudo(deps: DepsMut, env: Env, msg: SudoMsg) -> CoreumResult<ContractError> {
match msg {
SudoMsg::ExtensionTransfer {
recipient,
sender,
transfer_amount,
commission_amount,
burn_amount,
context,
} => sudo_extension_transfer(
deps,
env,
recipient,
sender,
transfer_amount,
commission_amount,
burn_amount,
context,
),
SudoMsg::ExtensionPlaceOrder {
order,
expected_to_spend,
expected_to_receive,
} => sudo_extension_place_order(
deps,
env,
order,
expected_to_spend,
expected_to_receive,
),
}
}
```

**`SudoMsg` Enum**:

- Defines the types of messages the extension supports, such as `ExtensionTransfer` and `ExtensionPlaceOrder`.
- Each variant carries the necessary information to execute specific logic.

**`sudo` Entry Point**:

- Acts as the main function that handles incoming messages.
- Delegates to specific helper functions based on the message type.

The current version of the DEX doesn't support the extensions. It means if a user places an order with the asset FT
extension token, such an order will be rejected.
5 changes: 0 additions & 5 deletions x/wasm/handler/query_grpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package handler

import (
"fmt"
"sync"

msgv1 "cosmossdk.io/api/cosmos/msg/v1"
queryv1 "cosmossdk.io/api/cosmos/query/v1"
Expand All @@ -25,7 +24,6 @@ type GRPCQuerier struct {
codec codec.Codec
// map[query proto URL]proto response type
acceptedQueries map[string]func() gogoproto.Message
mu sync.Mutex
}

// NewGRPCQuerier returns a new instance of GRPCQuerier.
Expand All @@ -40,7 +38,6 @@ func NewGRPCQuerier(gRPCQueryRouter *baseapp.GRPCQueryRouter, codec codec.Codec)
gRPCQueryRouter: gRPCQueryRouter,
codec: codec,
acceptedQueries: acceptedQueries,
mu: sync.Mutex{},
}
}

Expand All @@ -59,12 +56,10 @@ func (q *GRPCQuerier) Query(ctx sdk.Context, request *wasmvmtypes.GrpcQuery) (go
return nil, wasmvmtypes.UnsupportedRequest{Kind: fmt.Sprintf("No route to query '%s'", request.Path)}
}

q.mu.Lock()
res, err := handler(ctx, &abci.RequestQuery{
Data: request.Data,
Path: request.Path,
})
q.mu.Unlock()
if err != nil {
return nil, err
}
Expand Down
Loading

0 comments on commit 6ed9925

Please sign in to comment.