cosmwasmpool

package
v15.9.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 1, 2023 License: Apache-2.0 Imports: 10 Imported by: 0

README

CosmWasm Pool

Overview

The CosmWasm Pool Module is an extension for the Osmosis pools, aiming to create a custom module that allows users to create and manage liquidity pools backed by CosmWasm smart contracts. The feature enables developers to build and deploy custom smart contracts that can be integrated with the rest of the pool types on the Osmosis chain.

The module is built on top of the CosmWasm smart contracting platform, which provides a secure and efficient way to develop and execute WebAssembly (Wasm) smart contracts on the Cosmos SDK.

Having pools in CosmWasm provides several benefits, one of which is avoiding the need for chain upgrades when introducing new functionalities or modifying existing ones related to liquidity pools. This advantage is particularly important in the context of speed of development and iteration.

An example of a CosmWasm pool type:

Key Components

  • Keeper: The module's keeper is responsible for managing the state of the CosmWasm pools, including creating and initializing pools, querying pool data, and executing privileged operations such as swaps using the CosmWasm sudo message.

    • InitializePool: Initializes a new CosmWasm pool by instantiating a Wasm contract and storing the pool model in the keeper.
    • Swap operations: Swap operations like SwapExactAmountIn and SwapExactAmountOut are implemented, allowing users to perform swaps within the CosmWasm pools.
    • Swap estimation: Functions like CalcOutAmtGivenIn, and CalcInAmtGivenOut are provided to calculate prices and amounts for swap operations.
    • Pool information: Functions like CalculateSpotPrice, GetPool, GetPoolAssets, GetPoolBalances, GetPoolTotalShares allow for querying the state of the CosmWasm pools.
  • Query and Sudo functions: The module includes generic functions to query CosmWasm smart contracts and execute sudo messages. The Query and Sudo functions are used to interact with the smart contracts, while MustQuery and MustSudo variants panic if an error occurs during the query or sudo call, respectively.

  • poolmanager.PoolI Interface: The CosmWasm Pool Model implements the PoolI interface from the Pool Manager Module to enable the creation and management of liquidity pools backed by CosmWasm smart contracts. By implementing the PoolI interface, the model ensures compatibility with the existing Pool Manager Module's structure and functionalities and integrates seamlessly with other modules such as x/concentrated-liquidity and x/gamm.

  • poolmanager.PoolModule Interface: To integrate the CosmWasm Pool Module with the existing Pool Manager Module, the module's keeper has to implement the PoolModule interface from x/poolmanager Module. By implementing the PoolModule interface, the CosmWasm Pool Keeper can register itself as an extension to the existing Pool Manager Module and handle the creation and management of CosmWasm-backed liquidity pools as well as receive swaps propagated from the x/poolmanager.

Creating new CosmWasm Pool

To create new CosmWasm Pool, there are 3 modules involved: x/cosmwasmpool, x/wasm, and x/poolmanager. Here is an overview of the process:

graph TD;
  Sender((Sender))

  Sender -- create poool --> x/cosmwasmpool
  x/cosmwasmpool -- get next & set pool id --> x/poolmanager
  x/cosmwasmpool -- instantiate contract --> x/wasm

The CosmWasm contract that is to be instanitiated needs to implement CosmWasm Pool Contract Interface and store it on chain first. Then new pool can be created by sending MsgCreateCosmWasmPool.

MsgCreateCosmWasmPool contains InstantiateMsg, which is a message that will be passed to the CosmWasm contract when it is instantiated. The structure of the message is defined by the contract developer, and can contain any information that the contract needs to be instantiated. JSON format is used for InstantiateMsg.

sequenceDiagram
    participant Sender
    participant x/cosmwasmpool
    participant x/poolmanager
    participant x/wasm

    Sender ->> x/cosmwasmpool: MsgCreateCosmWasmPool {CodeId, InstantiateMsg, Sender}

    Note over x/wasm: Given there is a pool contract with CodeId

    x/poolmanager ->> x/cosmwasmpool: Call GetNextPoolId()
    x/cosmwasmpool ->> x/poolmanager: Call SetNextPoolId(poolId)

    x/cosmwasmpool ->> x/wasm: Call InstantiateContract(CodeId, InstantiateMsg)
    x/wasm -->> x/cosmwasmpool: ContractAddress

    Note over x/cosmwasmpool: Store CodeId, ContractAddress, and PoolId

    x/cosmwasmpool -->>  Sender: MsgCreateCosmWasmPoolResponse {PoolId}

Providing / Withdrawing Liquidity

Currently, all existing pool types have their own way of providing liquidity and shares calculation. CosmWasm pool aims to be flexible that regards and let the contract define the way of providing liquidity. So there is no restriction here, and the contract developer can define the way of providing liquidity as they wish, potentially with execute endpoint since MsgExecuteContract triggers state mutating endpoint and can also attach funds to it.

Common interface and later be defined for the contract to implement as spec and/or create a separated crate for that purpose.

It's important to note that the contract itselfs hold tokens that are provided by users.

Swap

One of the main reason why CosmWasm pool is implemented as a module + contract rather than a contract only is that it allows us to use the existing pool manager module to handle swap, which means things like swap routing, cross chain swap, and other functionality that depends on existing pool interface works out of the box.

graph TD;
  Sender((Sender))
  Sender -- swap --> x/poolmanager
  x/poolmanager -- route msg to --> x/cosmwasmpool
  x/cosmwasmpool -- sudo execute contract --> x/wasm
  x/wasm -- sudo --> wasm/pool

  x/cosmwasmpool -- send token_in from sender to wasm/pool --> x/bank
  wasm/pool -- send token_out to sender --> x/bank

Pool contract's sudo endpoint expect the following message variant:

/// SwapExactAmountIn swaps an exact amount of tokens in for as many tokens out as possible.
/// The amount of tokens out is determined by the current exchange rate and the swap fee.
/// The user specifies a minimum amount of tokens out, and the transaction will revert if that amount of tokens
/// is not received.
SwapExactAmountIn {
    sender: String,
    token_in: Coin,
    token_out_denom: String,
    token_out_min_amount: Uint128,
    swap_fee: Decimal,
},
/// SwapExactAmountOut swaps as many tokens in as possible for an exact amount of tokens out.
/// The amount of tokens in is determined by the current exchange rate and the swap fee.
/// The user specifies a maximum amount of tokens in, and the transaction will revert if that amount of tokens
/// is exceeded.
SwapExactAmountOut {
    sender: String,
    token_in_denom: String,
    token_in_max_amount: Uint128,
    token_out: Coin,
    swap_fee: Decimal,
},

The reason why this needs to be sudo endpoint, which can only be called by the chain itself, is that the chain can provide correct information about swap_fee, which can be deviated from contract defined swap_fee in multihop scenario.

swap_fee in this context is intended to be fee that is collected by liquidity providers. If the contract provider wants to collect fee for itself, it should implement its own fee collection mechanism.

And because sudo message can't attach funds like execute message, chain-side is required to perform sending token to the contract and ensure that token_in and token_in_max_amount is exactly the same amount of token that gets sent to the contract.

Deactivating

On contract's sudo enpoint, SetActive can be called to deactivate the pool. This will prevent the pool from being used for swap, and also prevent users from providing liquidity to the pool. Contract needs to check if the pool is active before performing any state mutating operation except SetActive.

SetActive {
    is_active: bool,
}

(TBD) On how to handle the deactivation operationally.

CosmWasm Pool Contract Interface

The contract interface is defined so that cosmwasmpool can delegate PoolI and PoolModuleI calls to contract.

The following are the messages that the contract needs to implement. (If you have trouble interpreting this, please read Rust de/serialization)

Query
#[cw_serde]
#[derive(QueryResponses)]
enum QueryMessage {
    /// GetSwapFee returns the pool's swap fee, based on the current state.
    /// Pools may choose to make their swap fees dependent upon state
    /// (prior TWAPs, network downtime, other pool states, etc.)
    /// This is intended to be fee that is collected by liquidity providers.
    /// If the contract provider wants to collect fee for itself, it should implement its own fee collection mechanism.
    #[returns(GetSwapFeeResponse)]
    GetSwapFee {},

    /// Returns whether the pool has swaps enabled at the moment
    #[returns(IsActiveResponse)]
    IsActive {},

    /// GetTotalShares returns the total number of LP shares in the pool

    /// GetTotalPoolLiquidity returns the coins in the pool owned by all LPs
    #[returns(TotalPoolLiquidityResponse)]
    GetTotalPoolLiquidity {},

    /// Returns the spot price of the 'base asset' in terms of the 'quote asset' in the pool,
    /// errors if either baseAssetDenom, or quoteAssetDenom does not exist.
    /// For example, if this was a UniV2 50-50 pool, with 2 ETH, and 8000 UST
    /// pool.SpotPrice(ctx, "eth", "ust") = 4000.00
    #[returns(SpotPriceResponse)]
    SpotPrice {
        quote_asset_denom: String,
        base_asset_denom: String,
    },

    /// CalcOutAmtGivenIn calculates the amount of tokenOut given tokenIn and the pool's current state.
    /// Returns error if the given pool is not a CFMM pool. Returns error on internal calculations.
    #[returns(CalcOutAmtGivenInResponse)]
    CalcOutAmtGivenIn {
        token_in: Coin,
        token_out_denom: String,
        swap_fee: Decimal,
    },

    /// CalcInAmtGivenOut calculates the amount of tokenIn given tokenOut and the pool's current state.
    /// Returns error if the given pool is not a CFMM pool. Returns error on internal calculations.
    #[returns(CalcInAmtGivenOutResponse)]
    CalcInAmtGivenOut {
        token_out: Coin,
        token_in_denom: String,
        swap_fee: Decimal,
    },
}
#[cw_serde]
pub struct GetSwapFeeResponse {
    pub swap_fee: Decimal,
}

#[cw_serde]
pub struct IsActiveResponse {
    pub is_active: bool,
}

#[cw_serde]
pub struct TotalPoolLiquidityResponse {
    pub total_pool_liquidity: Vec<Coin>,
}

#[cw_serde]
pub struct SpotPriceResponse {
    pub spot_price: Decimal,
}

#[cw_serde]
pub struct CalcOutAmtGivenInResponse {
    pub token_out: Coin,
}

#[cw_serde]
pub struct CalcInAmtGivenOutResponse {
    pub token_in: Coin,
}
Sudo
#[cw_serde]
pub enum SudoMessage {
    /// SetActive sets the active status of the pool.
    SetActive {
        is_active: bool,
    },
    /// SwapExactAmountIn swaps an exact amount of tokens in for as many tokens out as possible.
    /// The amount of tokens out is determined by the current exchange rate and the swap fee.
    /// The user specifies a minimum amount of tokens out, and the transaction will revert if that amount of tokens
    /// is not received.
    SwapExactAmountIn {
        sender: String,
        token_in: Coin,
        token_out_denom: String,
        token_out_min_amount: Uint128,
        swap_fee: Decimal,
    },
    /// SwapExactAmountOut swaps as many tokens in as possible for an exact amount of tokens out.
    /// The amount of tokens in is determined by the current exchange rate and the swap fee.
    /// The user specifies a maximum amount of tokens in, and the transaction will revert if that amount of tokens
    /// is exceeded.
    SwapExactAmountOut {
        sender: String,
        token_in_denom: String,
        token_in_max_amount: Uint128,
        token_out: Coin,
        swap_fee: Decimal,
    },
}

Incentives and Shares

In order to allow CosmWasm pool to work with the incentives module (or being composable in general), the contract needs to be able to create share tokens.

We handle this by utilizing the x/tokenfactory module. Each pool has share denom with this pattern: factory/{contract_address}/cw-pool/{custom-name}.

The contract address uniquely identifies a pool. We also use cw-pool to make these denoms distinguishable from other tokenfactory denoms and provide contracts the ability to customize the {custom-name}.

Each contract is responsible for minting and burning its token factory shares. The chain does no interaction with tokenfactory.

To integrate x/cosmwasmpool into the x/incentives module, it also needs to create gauges.

This can be done by setting after_pool_created on instantiate response.

Ok(Response::new()
    .add_attribute("method", "instantiate")
    .add_attribute("contract_name", CONTRACT_NAME)
    .add_attribute("contract_version", CONTRACT_VERSION)
    // set `after_pool_created` information on response
    // for `cosmwasmpool` module to process
    .set_data(to_binary(&after_pool_created)?))

after_pool_created has type:

#[cw_serde]
pub struct AfterPoolCreated {
    pub create_pool_guages: Option<CreatePoolGauges>,
}

#[cw_serde]
pub enum CreatePoolGauges {
    // This works exactly like `gamm`'s.
    DefaultLockableDurations {},
    // Custom guages can be created.
    Custom { msgs: Vec<MsgCreateGauge> },
}

Appendix

TWAP

x/twap is not implemented for cosmwasm pools but can be in the future if there is a need.

Rust de/serialization

Contract read these msg as JSON format. Here are some examples of how it is being de/serialized:

// Notice that enum variant is turned into snake case and becomes the key of the JSON object.
enum QueryMessage {
    // { "spot_price": { "quote_asset_denom": "denom1", "base_asset_denom": "denom2" } }
    SpotPrice {
        quote_asset_denom: String,
        base_asset_denom: String,
    },
}


// In case of struct, the struct name is not used as the key,
// since there is no need to distinguish between different structs.
struct SpotPriceResponse {
    // { "spot_price": "0.001" }
    pub spot_price: Decimal,
}

Decimal and Uint128 are represented as string in JSON.

Coin is:

pub struct Coin {
    pub denom: String,
    pub amount: Uint128,
}

Documentation

Overview

This file implements the poolmanagertypes.PoolModule interface

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func NewMsgServerImpl added in v15.8.0

func NewMsgServerImpl(keeper *Keeper) types.MsgServer

Types

type Keeper

type Keeper struct {
	// contains filtered or unexported fields
}

func NewKeeper

func NewKeeper(cdc codec.BinaryCodec, storeKey sdk.StoreKey, paramSpace paramtypes.Subspace, accountKeeper types.AccountKeeper, bankKeeper types.BankKeeper) *Keeper

func (Keeper) CalcInAmtGivenOut added in v15.8.0

func (k Keeper) CalcInAmtGivenOut(
	ctx sdk.Context,
	poolI poolmanagertypes.PoolI,
	tokenOut sdk.Coin,
	tokenInDenom string,
	swapFee sdk.Dec,
) (tokenIn sdk.Coin, err error)

CalcInAmtGivenOut calculates the input amount of a token required to get the desired output token amount in a CosmWasm-based liquidity pool.

Parameters: - ctx: The context of the operation. - poolI: The liquidity pool to perform the calculation on. - tokenOut: The desired output token (asset) amount. - tokenInDenom: The denom of the input token (asset) to be used in the calculation. - swapFee: The fee associated with the swap operation.

Returns: - sdk.Coin: The calculated input token amount. - error: An error if the calculation fails or if the pool conversion fails.

func (Keeper) CalcOutAmtGivenIn added in v15.8.0

func (k Keeper) CalcOutAmtGivenIn(
	ctx sdk.Context,
	poolI poolmanagertypes.PoolI,
	tokenIn sdk.Coin,
	tokenOutDenom string,
	swapFee sdk.Dec,
) (tokenOut sdk.Coin, err error)

CalcOutAmtGivenIn calculates the output amount of a token given the input token amount in a CosmWasm-based liquidity pool.

Parameters: - ctx: The context of the operation. - poolI: The liquidity pool to perform the calculation on. - tokenIn: The input token (asset) to be used in the calculation. - tokenOutDenom: The denom of the output token (asset) to be received. - swapFee: The fee associated with the swap operation.

Returns: - sdk.Coin: The calculated output token amount. - error: An error if the calculation fails or if the pool conversion fails.

func (Keeper) CalculateSpotPrice added in v15.8.0

func (k Keeper) CalculateSpotPrice(
	ctx sdk.Context,
	poolId uint64,
	quoteAssetDenom string,
	baseAssetDenom string,
) (price sdk.Dec, err error)

CalculateSpotPrice calculates the spot price of a pair of assets in a CosmWasm-based liquidity pool.

Parameters: - ctx: The context of the query request. - poolId: The unique identifier of the CosmWasm-based liquidity pool. - quoteAssetDenom: The denom of the quote asset in the trading pair. - baseAssetDenom: The denom of the base asset in the trading pair.

Returns: - price: The spot price of the trading pair in the specified liquidity pool. - err: An error if the pool cannot be found or if the spot price calculation fails.

func (Keeper) GetParams

func (k Keeper) GetParams(ctx sdk.Context) (params types.Params)

GetParams returns the total set of cosmwasmpool parameters.

func (Keeper) GetPool added in v15.8.0

func (k Keeper) GetPool(ctx sdk.Context, poolId uint64) (poolmanagertypes.PoolI, error)

GetPool retrieves a pool model with the specified pool ID from the store. The method returns the pool interface of the corresponding pool model if found, and an error if not found.

Parameters: - ctx: The SDK context. - poolId: The unique identifier of the pool.

Returns: - poolmanagertypes.PoolI: The pool interface of the corresponding pool model, if found. - error: An error if the pool model is not found; otherwise, nil.

func (Keeper) GetPoolById added in v15.8.0

func (k Keeper) GetPoolById(ctx sdk.Context, poolId uint64) (types.CosmWasmExtension, error)

GetPoolById returns a CosmWasmExtension that corresponds to the requested pool id. Returns error if pool id is not found.

func (Keeper) GetPoolDenoms added in v15.8.0

func (k Keeper) GetPoolDenoms(ctx sdk.Context, poolId uint64) (denoms []string, err error)

GetPoolDenoms retrieves the list of asset denoms in a CosmWasm-based liquidity pool given its ID.

Parameters: - ctx: The context of the query request. - poolId: The unique identifier of the CosmWasm-based liquidity pool.

Returns: - denoms: A slice of strings representing the asset denoms in the liquidity pool. - err: An error if the pool cannot be found or if the CosmWasm query fails.

func (Keeper) GetPools added in v15.8.0

func (k Keeper) GetPools(ctx sdk.Context) ([]poolmanagertypes.PoolI, error)

GetPools retrieves all pool objects stored in the keeper.

It fetches values from the store associated with the PoolsKey prefix. For each value retrieved, it attempts to unmarshal the value into a Pool object. If this operation succeeds, the Pool object is added to the returned slice. If an error occurs during unmarshalling, the function will return immediately with the encountered error.

Parameters: - ctx: The current SDK Context used to access the store.

Returns:

  • A slice of PoolI interfaces if the operation is successful. Each element in the slice represents a pool that was stored in the keeper.
  • An error if unmarshalling fails for any of the values fetched from the store. In this case, the slice of PoolI interfaces will be nil.

func (Keeper) GetTotalPoolLiquidity added in v15.8.0

func (k Keeper) GetTotalPoolLiquidity(ctx sdk.Context, poolId uint64) (sdk.Coins, error)

GetTotalPoolLiquidity retrieves the total liquidity of a specific pool identified by poolId.

Parameters: - ctx: The current SDK Context used for executing store operations. - poolId: The unique identifier of the pool whose total liquidity is to be fetched.

Returns:

  • the total liquidity of the specified pool, if the operations are successful.
  • An error if the pool retrieval operation fails. In this case, an empty sdk.Coins object will be returned.

func (Keeper) InitializePool added in v15.8.0

func (k Keeper) InitializePool(ctx sdk.Context, pool poolmanagertypes.PoolI, creatorAddress sdk.AccAddress) error

It converts the given pool to a CosmWasmPool, instantiates the Wasm contract using the contract keeper, and then sets the contract address in the CosmWasmPool model before storing it. The method returns an error if the pool conversion, contract instantiation, or storage process fails.

Parameters: - ctx: The SDK context. - pool: The pool interface to be initialized. - creatorAddress: The address of the creator of the pool.

Returns: - error: An error if the pool conversion, contract instantiation, or storage process fails; otherwise, nil.

func (*Keeper) SetContractKeeper added in v15.8.0

func (k *Keeper) SetContractKeeper(contractKeeper types.ContractKeeper)

Set the contract keeper.

func (Keeper) SetParams

func (k Keeper) SetParams(ctx sdk.Context, params types.Params)

SetParams sets the total set of cosmwasmpool parameters.

func (Keeper) SetPool added in v15.8.0

func (k Keeper) SetPool(ctx sdk.Context, pool types.CosmWasmExtension)

SetPool stores the given pool in state.

func (*Keeper) SetPoolManagerKeeper added in v15.8.0

func (k *Keeper) SetPoolManagerKeeper(poolmanagerKeeper types.PoolManagerKeeper)

Set the poolmanager keeper.

func (*Keeper) SetWasmKeeper added in v15.8.0

func (k *Keeper) SetWasmKeeper(wasmKeeper types.WasmKeeper)

Set the wasm keeper.

func (Keeper) SwapExactAmountIn added in v15.8.0

func (k Keeper) SwapExactAmountIn(
	ctx sdk.Context,
	sender sdk.AccAddress,
	pool poolmanagertypes.PoolI,
	tokenIn sdk.Coin,
	tokenOutDenom string,
	tokenOutMinAmount sdk.Int,
	swapFee sdk.Dec,
) (sdk.Int, error)

SwapExactAmountIn performs a swap operation with a specified input amount in a CosmWasm-based liquidity pool.

Parameters: - ctx: The context of the operation. - sender: The address of the account initiating the swap. - pool: The liquidity pool in which the swap occurs. - tokenIn: The input token (asset) to be swapped. - tokenOutDenom: The denom of the output token (asset) to be received. - tokenOutMinAmount: The minimum amount of the output token to be received. - swapFee: The fee associated with the swap operation.

Returns: - sdk.Int: The actual amount of the output token received after the swap. - error: An error if the swap operation fails or if the pool conversion fails.

func (Keeper) SwapExactAmountOut added in v15.8.0

func (k Keeper) SwapExactAmountOut(
	ctx sdk.Context,
	sender sdk.AccAddress,
	pool poolmanagertypes.PoolI,
	tokenInDenom string,
	tokenInMaxAmount sdk.Int,
	tokenOut sdk.Coin,
	swapFee sdk.Dec,
) (tokenInAmount sdk.Int, err error)

SwapExactAmountOut performs a swap operation with a specified output amount in a CosmWasm-based liquidity pool.

Parameters: - ctx: The context of the operation. - sender: The address of the account initiating the swap. - pool: The liquidity pool in which the swap occurs. - tokenInDenom: The denom of the input token (asset) to be swapped. - tokenInMaxAmount: The maximum amount of the input token allowed to be swapped. - tokenOut: The output token (asset) to be received. - swapFee: The fee associated with the swap operation.

Returns: - sdk.Int: The actual amount of the input token used in the swap. - error: An error if the swap operation fails or if the pool conversion fails.

func (Keeper) ValidatePermissionlessPoolCreationEnabled added in v15.8.0

func (k Keeper) ValidatePermissionlessPoolCreationEnabled(ctx sdk.Context) error

ValidatePermissionlessPoolCreationEnabled returns nil if permissionless pool creation in the module is enabled. Otherwise, returns an error.

Directories

Path Synopsis
queryproto
Package queryproto is a reverse proxy.
Package queryproto is a reverse proxy.
cosmwasm
msg
This file defines helpers for querying the cosmwasm pool contract from the cosmwasm pool module.
This file defines helpers for querying the cosmwasm pool contract from the cosmwasm pool module.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL