diff --git a/x/perpetual/spec/01_concepts.md b/x/perpetual/spec/01_concepts.md new file mode 100644 index 000000000..d3ae93052 --- /dev/null +++ b/x/perpetual/spec/01_concepts.md @@ -0,0 +1,75 @@ + + +# Concepts + +The `perpetual` module in the Elys Network facilitates perpetual trading, allowing users to open and close positions on various assets. This guide provides an overview of the key concepts and mechanisms involved in the perpetual module. + +## Key Concepts + +### Perpetual Trading + +Perpetual trading allows users to trade assets with leverage without the need for expiry dates on their positions. This type of trading is popular in the cryptocurrency market as it offers flexibility and continuous trading opportunities. + +### Positions + +A position in perpetual trading represents an open contract on an asset. Positions can be either long (betting that the asset price will rise) or short (betting that the asset price will fall). The size of the position is determined by the leverage applied and the amount of collateral provided. + +### Leverage + +Leverage allows traders to open positions larger than their collateral by borrowing funds. For example, with 5x leverage, a trader can open a position five times the size of their collateral. While leverage amplifies potential profits, it also increases potential losses. + +### Collateral + +Collateral is the amount of assets locked to open a leveraged position. It acts as a security deposit to cover potential losses. If the value of the position falls below the maintenance margin, the position may be liquidated to prevent further losses. + +### Safety Factor + +The safety factor is the threshold to keep a position open. If the position health value falls below this threshold, the position may be subject to liquidation. + +### Position Health + +Position health is a metric that indicates the risk level of a position. It is calculated based on the position's value, leverage, and collateral. Monitoring position health helps prevent excessive risk-taking and ensures the stability of the perpetual trading system. + +### Liquidation + +Liquidation occurs when a position's value drops below the safety factor. The position is forcibly closed to prevent further losses. This mechanism ensures the stability of the perpetual trading system and protects the platform from significant losses. + +### Funding Rate + +The funding rate is a periodic payment exchanged between long and short positions. It ensures that the perpetual contract price closely tracks the underlying asset price. When the funding rate is positive, longs pay shorts, and when it is negative, shorts pay longs. + +### Whitelisting + +Whitelisting is a mechanism to control access to the perpetual module. Only addresses that are whitelisted can participate in trading. This helps maintain the security and integrity of the platform by allowing only trusted participants. + +### Parameters + +The perpetual module has several configurable parameters that govern its operation. These include leverage limits, interest rates, maintenance margins, and more. These parameters can be updated through governance proposals to adapt to changing market conditions and ensure the module's efficiency and security. + +## Transaction Commands + +The perpetual module supports various transaction commands for managing positions and parameters. Key commands include: + +- `open`: Opens a new perpetual position. +- `close`: Closes an existing perpetual position. +- `whitelist`: Adds an address to the whitelist. +- `dewhitelist`: Removes an address from the whitelist. +- `update-params`: Updates the module parameters through a governance proposal. + +## Query Commands + +Users can query various aspects of the perpetual module using the following commands: + +- `params`: Retrieves the current parameters of the module. +- `get-positions`: Lists all open positions. +- `get-positions-by-pool`: Lists positions for a specific pool. +- `get-positions-for-address`: Lists positions for a specific address. +- `get-status`: Retrieves the current status of the module. +- `get-whitelist`: Lists all whitelisted addresses. +- `is-whitelisted`: Checks if a specific address is whitelisted. +- `list-pool`: Lists all available pools. +- `show-pool`: Retrieves details of a specific pool. +- `get-mtp`: Retrieves details of a specific margin trading position (MTP). +- `open-estimation`: Provides an estimation for opening a position. diff --git a/x/perpetual/spec/02_mechanism.md b/x/perpetual/spec/02_mechanism.md new file mode 100644 index 000000000..e96e02edc --- /dev/null +++ b/x/perpetual/spec/02_mechanism.md @@ -0,0 +1,135 @@ + + +# Mechanism + +The following section provides a detailed technical explanation of the perpetual trading mechanism as depicted in the Elys Perpetual Trading Design diagram. + +![Elys Perpetual Trading Design](elys-perpetual-trading-design-long-y-asset.png) + +## Overview + +The Elys Perpetual Trading system facilitates leveraged trading on assets without the need for expiry dates on positions. This design ensures continuous trading opportunities and flexibility, which are essential features in the cryptocurrency market. The system is designed to manage the complex interactions between various entities and components, ensuring the stability and security of the trading platform. + +The module allows participants to leverage their trading positions by borrowing either the traded asset liquidity or USDC, depending on the direction of the trade (long or short). For long trades, participants borrow either the traded asset liquidity or USDC to increase their exposure to the traded asset, while for short trades, they borrow the traded asset itself to bet against its price. This mechanism ensures that the borrowed assets are appropriately managed and tracked within the Perpetual module, providing a flexible and efficient trading experience. + +## Components + +### Symbols and Definitions + +- **$X_A, Y_A$**: Pool Assets +- **$\Delta x$**: Collateral +- **$n$**: Leverage +- **$\Delta x_L$**: Synthetic Liability +- **$X_L^P$**: Pool Synthetic Liability +- **$\Delta x_L^{TP}$**: Take Profit Liability +- **$X_L^{TP}$**: Pool Take Profit Liability +- **$\Delta y_C$**: Custody Asset +- **$Y_C$**: Pool Custody Asset +- **$\Delta y_C^{TP}$**: Take Profit Custody +- **$Y_C^{TP}$**: Pool Take Profit Custody + +### Key Entities + +- **Perp Trader**: The trader participating in perpetual trading. +- **Perp Position**: The open position taken by the trader. +- **Swap Logic**: The mechanism for swapping assets within the pool. +- **Pool Health $H(X)$**: A metric indicating the health of the pool, influenced by the assets and liabilities. + +## Workflow + +### 1. Borrow and Create Synthetic Liability + +1. The Perp Trader deposits collateral $\Delta x$. +2. The collateral $\Delta x$ is added to the Pool Assets $X_A$. +3. A synthetic liability $\Delta x_L$ is created based on the leverage $n$, calculated as: + $\Delta x_L = (n - 1) \cdot \Delta x$ +4. The synthetic liability $\Delta x_L$ is recorded as Pool Synthetic Liability $X_L^P$. + +### 2. Position Opening + +1. The Perp Position is updated with the new synthetic liability $\Delta x_L$. +2. The take profit liability $\Delta x_L^{TP}$ is calculated and added to the Pool Take Profit Liability $X_L^{TP}$. + +### 3. Custody and Asset Management + +1. The equivalent custody asset $\Delta y_C$ is determined and taken into custody. +2. This custody asset $\Delta y_C$ is added to the Pool Custody Asset $Y_C$. +3. The take profit custody $\Delta y_C^{TP}$ is also calculated and added to the Pool Take Profit Custody $Y_C^{TP}$. + +### 4. Swap Logic + +The system employs swap logic to facilitate asset exchanges within the pool, ensuring the appropriate custody and liabilities are maintained. + +### 5. Pool Health Management + +The overall health of the pool $H(X)$ is continually monitored, ensuring that the pool remains solvent and capable of covering all positions and liabilities. + +### Summary of Flow + +The flow diagram illustrates the interaction between the Perp Trader, the Perp Position, and the various pool components. The process involves depositing collateral, creating synthetic liabilities, managing custody assets, and using swap logic to maintain pool health and facilitate perpetual trading. + +### Note on Short Positions + +The diagram illustrates the long position scenario. The mechanism for short positions is essentially the same, but with the roles of $X$ and $Y$ reversed. This means that for short positions, the Perp Trader deposits $Y$ as collateral, and the system manages the corresponding synthetic liabilities, custody assets, and swaps accordingly. + +## Technical Specification + +The perpetual trading mechanism is designed to ensure that: + +- Traders can open leveraged positions without expiry dates. +- The system dynamically manages collateral, synthetic liabilities, and custody assets. +- Pool health is continuously monitored to prevent insolvency. +- Swaps and asset exchanges within the pool are efficiently handled to maintain liquidity and stability. + +The implementation ensures that all participants can trade with confidence, knowing that the system is robust and capable of handling the complexities of perpetual trading. + +## Dependencies + +The Perpetual module depends on two other modules: + +1. **AccountedPool**: Manages the accounting of the pools usage between the AMM and the Perpetual modules. +2. **AMM**: Facilitates liquidity provision and trading. + +## Key Equations + +To maintain the stability and health of the pool, the following equations are used: + +1. **Pool Health** + + The Pool Health formula is designed to evaluate the stability and risk associated with the assets in a trading pool. It takes into account the balances of the assets, the synthetic liabilities for both long and short positions, and the take profit liabilities for these positions. The formula is given by: + + $$\text{Pool Health} = \prod_\text{Asset}{\frac{\text{Asset Balance}}{\text{Asset Balance} + \text{Pool Synthetic Liabilities}\_\text{long} - \text{Pool Take Profit Liabilities}\_\text{long}}} \times \prod_\text{Asset}{\frac{\text{Asset Balance}}{\text{Asset Balance} + \text{Pool Synthetic Liabilities}\_\text{short} - \text{Pool Take Profit Liabilities}\_\text{short}}}$$ + + **Components of the Formula** + + - **Asset Balance**: The current amount of the asset available in the pool. + - **Pool Synthetic Liabilities (long)**: The total synthetic liabilities created from long positions for the asset. + - **Pool Take Profit Liabilities (long)**: The liabilities associated with take profit levels for long positions on the asset. + - **Pool Synthetic Liabilities (short)**: The total synthetic liabilities created from short positions for the asset. + - **Pool Take Profit Liabilities (short)**: The liabilities associated with take profit levels for short positions on the asset. + + **Interpretation** + + - **Long Position Component**: The term $$\frac{\text{Asset Balance}}{\text{Asset Balance} + \text{Pool Synthetic Liabilities}\_\text{long} - \text{Pool Take Profit Liabilities}\_\text{long}}$$ calculates the ratio of the asset balance to the net synthetic liabilities (considering take profit liabilities) for long positions. This ratio indicates how well the pool's asset balance can cover the synthetic liabilities of long positions. A higher ratio implies a healthier pool. + + - **Short Position Component**: Similarly, the term $$\frac{\text{Asset Balance}}{\text{Asset Balance} + \text{Pool Synthetic Liabilities}\_\text{short} - \text{Pool Take Profit Liabilities}\_\text{short}}$$ calculates the ratio for short positions. It shows the pool's ability to cover synthetic liabilities for short positions. + + - **Product of Ratios**: The overall pool health is determined by taking the product of these ratios across all assets. This comprehensive measure ensures that both long and short synthetic liabilities are considered for each asset in the pool. + + **Purpose** + + The Pool Health metric is monitored to ensure it does not fall below the Safety Factor threshold, which is set at 5%. Lower Pool Health indicates higher risk of leveraged positions being liquidated. + +2. **Position Health and Liquidation Mechanism**: + + The Position Health formula is used to evaluate the risk level of an individual trading position by comparing the amount of assets held in custody to the total liabilities and unpaid borrow interest. The formula is given by: + + $$\text{Position Health} = \frac{\text{Custody Amount}}{\text{Liabilities} + \text{Borrow Interest Unpaid}}$$ + + - **Custody Amount**: The total value of assets currently held in custody for the position. This represents the collateral or assets secured to back the position. + - **Liabilities**: The total amount of liabilities associated with the position. This includes any borrowed amounts that the trader is responsible for repaying. + - **Borrow Interest Unpaid**: The total amount of unpaid interest accrued on the borrowed liabilities. This includes any interest that has not yet been paid by the trader. + + If Position Health falls below the Safety Factor threshold (5%), the perpetual position position is automatically liquidated to protect the underlying liquidity pool. diff --git a/x/perpetual/spec/03_usage.md b/x/perpetual/spec/03_usage.md new file mode 100644 index 000000000..031d216fd --- /dev/null +++ b/x/perpetual/spec/03_usage.md @@ -0,0 +1,247 @@ + + +# Usage + +## Transaction Commands + +### Open a Position + +#### Infinite Profitability + +To open a long position with infinite profitability: + +```shell +elysd tx perpetual open long 5 uatom 100000000uusdc --from=treasury --keyring-backend=test --chain-id=elystestnet-1 --yes --gas=1000000 +``` + +#### Finite Profitability + +To open a short position with a specified take profit level: + +```shell +elysd tx perpetual open short 5 uatom 100000000uusdc --take-profit 100 --from=treasury --keyring-backend=test --chain-id=elystestnet-1 --yes --gas=1000000 +``` + +### Close a Position + +To close an existing position: + +```shell +elysd tx perpetual close 1 10000000 --from=treasury --keyring-backend=test --chain-id=elystestnet-1 --yes --gas=1000000 +``` + +### Manage Whitelist + +#### Whitelist an Address + +To whitelist an address for trading in the perpetual module: + +```shell +elysd tx perpetual whitelist ADDRESS --from=treasury --keyring-backend=test --chain-id=elystestnet-1 --yes --gas=1000000 +``` + +#### Dewhitelist an Address + +To remove an address from the whitelist: + +```shell +elysd tx perpetual dewhitelist ADDRESS --from=treasury --keyring-backend=test --chain-id=elystestnet-1 --yes --gas=1000000 +``` + +### Update Module Parameters + +To update the parameters of the perpetual module: + +```shell +elysd tx perpetual update-params [OPTIONS] --from=treasury --keyring-backend=test --chain-id=elystestnet-1 --yes --gas=1000000 +``` + +## Query Commands + +### Query Module Parameters + +To query the current parameters of the perpetual module: + +```shell +elysd query perpetual params +``` + +### Query All Positions + +To query all positions: + +```shell +elysd query perpetual get-positions +``` + +### Query Positions by Pool + +To query positions for a specific pool: + +```shell +elysd query perpetual get-positions-by-pool [amm_pool_id] +``` + +### Query Positions for an Address + +To query positions for a specific address: + +```shell +elysd query perpetual get-positions-for-address [address] +``` + +### Query Module Status + +To query the current status of the perpetual module: + +```shell +elysd query perpetual get-status +``` + +### Query Whitelisted Addresses + +To query all whitelisted addresses: + +```shell +elysd query perpetual get-whitelist +``` + +### Check if Address is Whitelisted + +To check if a specific address is whitelisted: + +```shell +elysd query perpetual is-whitelisted [address] +``` + +### Query Pool List + +To query all pools: + +```shell +elysd query perpetual list-pool +``` + +### Query Specific Pool + +To query details of a specific pool: + +```shell +elysd query perpetual show-pool [index] +``` + +### Query MTP + +To query a specific MTP (margin trading position): + +```shell +elysd query perpetual get-mtp [address] [id] +``` + +### Open Estimation + +To query an open estimation: + +```shell +elysd query perpetual open-estimation [position] [leverage] [trading-asset] [collateral] +``` + +## Examples + +### Open a Long Position + +```shell +elysd tx perpetual open long 5 uatom 100000000uusdc --from=treasury --keyring-backend=test --chain-id=elystestnet-1 --yes --gas=1000000 +``` + +### Open a Short Position with Take Profit + +```shell +elysd tx perpetual open short 5 uatom 100000000uusdc --take-profit 100 --from=treasury --keyring-backend=test --chain-id=elystestnet-1 --yes --gas=1000000 +``` + +### Close a Position + +```shell +elysd tx perpetual close 1 10000000 --from=treasury --keyring-backend=test --chain-id=elystestnet-1 --yes --gas=1000000 +``` + +### Whitelist an Address + +```shell +elysd tx perpetual whitelist elys1qv4k64xr6nhcxgnzq0l8t8wy9s9d4s9d6w93lz --from=treasury --keyring-backend=test --chain-id=elystestnet-1 --yes --gas=1000000 +``` + +### Dewhitelist an Address + +```shell +elysd tx perpetual dewhitelist elys1qv4k64xr6nhcxgnzq0l8t8wy9s9d4s9d6w93lz --from=treasury --keyring-backend=test --chain-id=elystestnet-1 --yes --gas=1000000 +``` + +### Update Parameters + +```shell +elysd tx perpetual update-params --maxLeverage=10 --maintenanceMarginRatio=0.05 --fundingRateInterval=60 --from=treasury --keyring-backend=test --chain-id=elystestnet-1 --yes --gas=1000000 +``` + +### Query a Specific Position + +```shell +elysd query perpetual get-mtp elys1qv4k64xr6nhcxgnzq0l8t8wy9s9d4s9d6w93lz 1 +``` + +### Query All Positions + +```shell +elysd query perpetual get-positions +``` + +### Query Positions by Pool + +```shell +elysd query perpetual get-positions-by-pool 1 +``` + +### Query Positions for an Address + +```shell +elysd query perpetual get-positions-for-address elys1qv4k64xr6nhcxgnzq0l8t8wy9s9d4s9d6w93lz +``` + +### Query Module Status + +```shell +elysd query perpetual get-status +``` + +### Query Whitelisted Addresses + +```shell +elysd query perpetual get-whitelist +``` + +### Check if Address is Whitelisted + +```shell +elysd query perpetual is-whitelisted elys1qv4k64xr6nhcxgnzq0l8t8wy9s9d4s9d6w93lz +``` + +### Query All Pools + +```shell +elysd query perpetual list-pool +``` + +### Query Specific Pool + +```shell +elysd query perpetual show-pool 1 +``` + +### Open Estimation + +```shell +elysd query perpetual open-estimation long 5 uatom 100000000uusdc +``` diff --git a/x/perpetual/spec/04_keeper.md b/x/perpetual/spec/04_keeper.md new file mode 100644 index 000000000..24818113d --- /dev/null +++ b/x/perpetual/spec/04_keeper.md @@ -0,0 +1,179 @@ + + +# Keeper + +The `Keeper` module is a core component of the Elys Network's perpetual trading system. It encapsulates the logic for managing the state of perpetual contracts, handling transactions, and interfacing with other modules. This guide provides a detailed overview of the key functions and their roles within the module. + +## Block Initialization + +### Epoch and Interest Rate Initialization + +The `BeginBlocker` function is triggered at the beginning of each block. It performs various checks and updates, including: + +- **Epoch Check**: Determines if a new epoch has begun. +- **Interest Rate Computation**: Computes borrow interest rates for each pool. +- **Position Updates**: Updates the health of margin trading positions (MTPs) and processes funding fees. + +### Processing Margin Trading Positions + +`BeginBlockerProcessMTP` is responsible for updating individual MTPs, including: + +- Calculating take profit liabilities and borrow rates. +- Handling borrow interest payments and funding fee collection. +- Checking MTP health and determining if force closure is necessary. + +## Collateral and Position Calculations + +### Minimum Collateral Calculation + +`CalcMinCollateral` calculates the minimum collateral required to open a position based on leverage and the trading asset's price. + +### Consolidating Collateral + +`CalcMTPConsolidateCollateral` consolidates an MTP's collateral into the base currency, ensuring accurate position valuation. + +### Borrow Interest Liabilities Calculation + +`CalcMTPBorrowInterestLiabilities` computes the interest liabilities for an MTP, including unpaid borrow interest and new interest accrued within the current epoch. + +### Take Profit Borrow Rate Calculation + +`CalcMTPTakeProfitBorrowRate` calculates the borrow rate for an MTP's take profit custody, ensuring the rate meets minimum requirements. + +### Take Profit Liabilities Calculation + +`CalcMTPTakeProfitLiability` calculates the liabilities associated with an MTP's take profit custody in the base currency. + +## Position Management Checks + +### Maximum Open Positions Check + +`CheckMaxOpenPositions` ensures the number of open MTPs does not exceed the maximum allowed limit. + +### Pool Health Verification + +`CheckPoolHealth` verifies the health of a pool, ensuring it is enabled and not closed, and that its health is above the minimum threshold. + +### Same Asset Position Check + +`CheckSameAssetPosition` checks if a user already has an open position with the same assets, allowing for consolidation. + +### User Authorization Check + +`CheckUserAuthorization` ensures a user is authorized to open a position, based on whitelisting if enabled. + +## Position Closure + +### Closing a Margin Trading Position + +`Close` handles the closure of an MTP, repaying liabilities and returning any remaining collateral to the user. + +### Closing a Long Position + +`CloseLong` processes the closure of a long position, including handling borrow interest and estimating repayment amounts. + +### Closing a Short Position + +`CloseShort` processes the closure of a short position, similar to `CloseLong`, but adjusted for short positions. + +### Emitting Closure Events + +`EmitCloseEvent` emits an event when an MTP is closed, providing details about the closure. + +## Position Opening + +### Opening a New Margin Trading Position + +`Open` processes the opening of a new MTP, determining the appropriate position type and validating assets. + +### Consolidating Positions on Open + +`OpenConsolidate` handles the consolidation of existing and new MTPs when the user already has an open position with the same assets. + +### Opening a Long Position + +`OpenLong` processes the opening of a new long position, including leverage calculation and collateral handling. + +### Detailed Long Position Processing + +`ProcessOpenLong` performs the detailed steps required to open a long position, including borrowing assets and updating pool health. + +### Opening a Short Position + +`OpenShort` processes the opening of a new short position, including leverage calculation and collateral handling. + +### Detailed Short Position Processing + +`ProcessOpenShort` performs the detailed steps required to open a short position, including borrowing assets and updating pool health. + +## Position and Pool Handling + +### Estimating and Repaying Liabilities + +`EstimateAndRepay` estimates the repayment amount for a position and handles the repayment process. + +### Estimating Swaps + +`EstimateSwap` estimates the result of a token swap using the AMM's `CalcOutAmtGivenIn` function. + +### Event Emissions + +Defines various event emission functions used throughout the module, such as `EmitFundPayment`, `EmitForceClose`, and `EmitFundingFeePayment`. + +### Forcibly Closing Positions + +`ForceCloseLong` and `ForceCloseShort` handle the forced closure of positions when necessary, typically due to health or take profit conditions. + +### Pool Management + +`GetAmmPool`, `GetBestPool`, `SetPool`, and other functions handle the retrieval, initialization, and management of AMM pools. + +### Borrow Interest Handling + +`HandleBorrowInterest` and `HandleBorrowInterestPayment` manage the computation and payment of borrow interest for MTPs. + +### Funding Fee Handling + +`HandleFundingFeeCollection` and `HandleFundingFeeDistribution` manage the collection and distribution of funding fees. + +### Health Updates + +`UpdateMTPHealth` and `UpdatePoolHealth` ensure the health of MTPs and pools are calculated and maintained. + +### Utility Functions + +Various utility functions such as `GetMarketAssetPrice`, `PositionEstimation`, and `SwapAssets` provide necessary support for position and pool management. + +## Governance and Whitelisting + +### Message Server Implementation + +Defines the `MsgServer` struct and its implementation, handling various message types such as opening and closing positions, whitelisting, and parameter updates. + +### Governance Proposals + +`UpdateParams` handles updating the module's parameters through a governance proposal. + +### Whitelisting Management + +`Whitelist` and `Dewhitelist` manage the addition and removal of addresses from the whitelist, ensuring only authorized users can open positions. + +## Queries + +### Querying Positions + +`GetPositions`, `GetPositionsByPool`, and `GetPositionsForAddress` provide endpoints to retrieve open positions. + +### Querying Pool and Module Status + +`GetStatus`, `Pools`, `Pool`, and `Params` provide endpoints to retrieve the status of the module, including pool details and module parameters. + +### Querying Whitelist + +`GetWhitelist` and `IsWhitelisted` provide endpoints to retrieve and check whitelisted addresses. + +### Position Opening Estimation + +`OpenEstimation` provides an estimation for opening a position, including collateral requirements, estimated PnL, and liquidation prices. diff --git a/x/perpetual/spec/05_protobuf_definitions.md b/x/perpetual/spec/05_protobuf_definitions.md new file mode 100644 index 000000000..43f541430 --- /dev/null +++ b/x/perpetual/spec/05_protobuf_definitions.md @@ -0,0 +1,925 @@ + + +# Protobuf Definitions + +The following sections provide detailed protobuf definitions used in the Elys Network's Perpetual trading module. + +## Genesis State Definition + +The `GenesisState` message defines the initial state of the perpetual module at genesis. + +### GenesisState + +- **Params params**: Parameters for the module. +- **repeated Pool pool_list**: List of pools. +- **repeated MTP mtp_list**: List of MTPs. +- **repeated string address_whitelist**: List of whitelisted addresses. + +```proto +message GenesisState { + Params params = 1 [(gogoproto.nullable) = false]; + repeated Pool pool_list = 2 [(gogoproto.nullable) = false]; + repeated MTP mtp_list = 3 [(gogoproto.nullable) = false]; + repeated string address_whitelist = 4; +} +``` + +## Module Parameters + +The `Params` message defines the configuration parameters for the perpetual module. + +### Params + +- **string leverage_max**: Maximum leverage allowed. +- **string borrow_interest_rate_max**: Maximum borrow interest rate. +- **string borrow_interest_rate_min**: Minimum borrow interest rate. +- **string borrow_interest_rate_increase**: Borrow interest rate increase factor. +- **string borrow_interest_rate_decrease**: Borrow interest rate decrease factor. +- **string health_gain_factor**: Health gain factor. +- **int64 epoch_length**: Length of an epoch. +- **int64 max_open_positions**: Maximum number of open positions. +- **string pool_open_threshold**: Pool open threshold. +- **string force_close_fund_percentage**: Percentage of force close fund. +- **string force_close_fund_address**: Address of the force close fund. +- **string incremental_borrow_interest_payment_fund_percentage**: Percentage for incremental borrow interest payment fund. +- **string incremental_borrow_interest_payment_fund_address**: Address for incremental borrow interest payment fund. +- **string safety_factor**: Safety factor. +- **bool incremental_borrow_interest_payment_enabled**: Flag to enable incremental borrow interest payment. +- **bool whitelisting_enabled**: Flag to enable whitelisting. +- **string invariant_check_epoch**: Epoch for invariant check. +- **string take_profit_borrow_interest_rate_min**: Minimum take profit borrow interest rate. +- **string funding_fee_base_rate**: Base rate for funding fee. +- **string funding_fee_max_rate**: Maximum rate for funding fee. +- **string funding_fee_min_rate**: Minimum rate for funding fee. +- **string funding_fee_collection_address**: Address for funding fee collection. +- **string swap_fee**: Swap fee. + +```proto +message Params { + option (gogoproto.goproto_stringer) = false; + string leverage_max = 1 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; + string borrow_interest_rate_max = 2 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; + string borrow_interest_rate_min = 3 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; + string borrow_interest_rate_increase = 4 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; + string borrow_interest_rate_decrease = 5 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; + string health_gain_factor = 6 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; + int64 epoch_length = 7; + int64 max_open_positions = 8; + string pool_open_threshold = 9 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; + string force_close_fund_percentage = 10 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; + string force_close_fund_address = 11; + string incremental_borrow_interest_payment_fund_percentage = 12 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; + string incremental_borrow_interest_payment_fund_address = 13; + string safety_factor = 14 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; + bool incremental_borrow_interest_payment_enabled = 15; + bool whitelisting_enabled = 16; + string invariant_check_epoch = 17; + string take_profit_borrow_interest_rate_min = 18 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; + string funding_fee_base_rate = 19 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; + string funding_fee_max_rate = 20 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; + string funding_fee_min_rate = 21 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; + string funding_fee_collection_address = 22; + string swap_fee = 23 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; +} +``` + +## Pool Definitions + +The `Pool` and `PoolAsset` messages define the structure of pools and their assets in the perpetual module. + +### PoolAsset + +- **string liabilities**: Liabilities of the asset. +- **string custody**: Custody of the asset. +- **string take_profit_liabilities**: Take profit liabilities of the asset. +- **string take_profit_custody**: Take profit custody of the asset. +- **string asset_balance**: Balance of the asset. +- **string block_borrow_interest**: Block borrow interest. +- **string asset_denom**: Denomination of the asset. + +```proto +message PoolAsset { + string liabilities = 1 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", + (gogoproto.nullable) = false + ]; + string custody = 2 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", + (gogoproto.nullable) = false + ]; + string take_profit_liabilities = 3 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", + (gogoproto.nullable) = false + ]; + string take_profit_custody = 4 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", + (gogoproto.nullable) = false + ]; + string asset_balance = 5 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", + (gogoproto.nullable) = false + ]; + string block_borrow_interest = 6 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", + (gogoproto.nullable) = false + ]; + string asset_denom = 7; +} +``` + +### Pool + +- **uint64 amm_pool_id**: AMM pool ID. +- **string health**: Health of the pool. +- **bool enabled**: Flag indicating if the pool is enabled. +- **bool closed**: Flag indicating if the pool is closed. +- **string borrow_interest_rate**: Borrow interest rate. +- **repeated PoolAsset pool_assets_long**: List of long position assets. +- **repeated PoolAsset pool_assets_short**: List of short position assets. +- **int64 last_height_borrow_interest_rate_computed**: Last height at which borrow interest rate was computed. +- **string funding_rate**: Funding rate. + +```proto +message Pool { + uint64 amm_pool_id = 1; + string health = 2 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; + bool enabled = 3; + bool closed = 4; + string borrow_interest_rate = 5 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; + repeated PoolAsset pool_assets_long = 6 [(gogoproto.nullable) = false]; + repeated + + PoolAsset pool_assets_short = 7 [(gogoproto.nullable) = false]; + int64 last_height_borrow_interest_rate_computed = 8; + string funding_rate = 9 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; +} +``` + +## Query Service Definitions + +The `Query` service defines the gRPC querier service for the perpetual module. + +### Query Service + +- **Params**: Retrieves the parameters of the module. +- **GetPositions**: Retrieves a list of positions. +- **GetPositionsByPool**: Retrieves a list of MTP positions by pool. +- **GetStatus**: Retrieves the total number of open and lifetime MTPs. +- **GetPositionsForAddress**: Retrieves a list of MTP positions for a given address. +- **GetWhitelist**: Retrieves a list of whitelisted addresses. +- **IsWhitelisted**: Checks if an address is whitelisted. +- **Pool**: Retrieves a single pool given its index. +- **Pools**: Retrieves a list of all pools. +- **MTP**: Retrieves a single MTP position given its address and ID. +- **OpenEstimation**: Provides an estimation of a new open position. + +### ParamsRequest + +**Request**: None + +**Response**: + +- **Params params**: The parameters of the module. + +```proto +message ParamsRequest {} + +message ParamsResponse { + Params params = 1 [(gogoproto.nullable) = false]; +} +``` + +### PositionsRequest + +**Request**: + +- **cosmos.base.query.v1beta1.PageRequest pagination**: Pagination for the request. + +**Response**: + +- **repeated MTP mtps**: List of MTPs. +- **cosmos.base.query.v1beta1.PageResponse pagination**: Pagination for the response. + +```proto +message PositionsRequest { + cosmos.base.query.v1beta1.PageRequest pagination = 1; +} + +message PositionsResponse { + repeated MTP mtps = 1; + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} +``` + +### PositionsByPoolRequest + +**Request**: + +- **uint64 amm_pool_id**: AMM pool ID. +- **cosmos.base.query.v1beta1.PageRequest pagination**: Pagination for the request. + +**Response**: + +- **repeated MTP mtps**: List of MTPs by pool. +- **cosmos.base.query.v1beta1.PageResponse pagination**: Pagination for the response. + +```proto +message PositionsByPoolRequest { + uint64 amm_pool_id = 1; + cosmos.base.query.v1beta1.PageRequest pagination = 2; +} + +message PositionsByPoolResponse { + repeated MTP mtps = 1; + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} +``` + +### StatusRequest + +**Request**: None + +**Response**: + +- **uint64 open_mtp_count**: Number of open MTPs. +- **uint64 lifetime_mtp_count**: Number of lifetime MTPs. + +```proto +message StatusRequest {} + +message StatusResponse { + uint64 open_mtp_count = 1; + uint64 lifetime_mtp_count = 2; +} +``` + +### PositionsForAddressRequest + +**Request**: + +- **string address**: Address to query positions for. +- **cosmos.base.query.v1beta1.PageRequest pagination**: Pagination for the request. + +**Response**: + +- **repeated MTP mtps**: List of MTPs for the address. +- **cosmos.base.query.v1beta1.PageResponse pagination**: Pagination for the response. + +```proto +message PositionsForAddressRequest { + string address = 1; + cosmos.base.query.v1beta1.PageRequest pagination = 2; +} + +message PositionsForAddressResponse { + repeated MTP mtps = 1; + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} +``` + +### WhitelistRequest + +**Request**: + +- **cosmos.base.query.v1beta1.PageRequest pagination**: Pagination for the request. + +**Response**: + +- **repeated string whitelist**: List of whitelisted addresses. +- **cosmos.base.query.v1beta1.PageResponse pagination**: Pagination for the response. + +```proto +message WhitelistRequest { + cosmos.base.query.v1beta1.PageRequest pagination = 1; +} + +message WhitelistResponse { + repeated string whitelist = 1; + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} +``` + +### IsWhitelistedRequest + +**Request**: + +- **string address**: Address to check. + +**Response**: + +- **string address**: Address checked. +- **bool is_whitelisted**: Whether the address is whitelisted. + +```proto +message IsWhitelistedRequest { + string address = 1; +} + +message IsWhitelistedResponse { + string address = 1; + bool is_whitelisted = 2; +} +``` + +### QueryGetPoolRequest + +**Request**: + +- **uint64 index**: Index of the pool to retrieve. + +**Response**: + +- **Pool pool**: The pool retrieved. + +```proto +message QueryGetPoolRequest { + uint64 index = 1; +} + +message QueryGetPoolResponse { + Pool pool = 1 [(gogoproto.nullable) = false]; +} +``` + +### QueryAllPoolRequest + +**Request**: + +- **cosmos.base.query.v1beta1.PageRequest pagination**: Pagination for the request. + +**Response**: + +- **repeated Pool pool**: List of all pools. +- **cosmos.base.query.v1beta1.PageResponse pagination**: Pagination for the response. + +```proto +message QueryAllPoolRequest { + cosmos.base.query.v1beta1.PageRequest pagination = 1; +} + +message QueryAllPoolResponse { + repeated Pool pool = 1 [(gogoproto.nullable) = false]; + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} +``` + +### MTPRequest + +**Request**: + +- **string address**: Address associated with the MTP. +- **uint64 id**: ID of the MTP. + +**Response**: + +- **MTP mtp**: The MTP retrieved. + +```proto +message MTPRequest { + string address = 1; + uint64 id = 2; +} + +message MTPResponse { + MTP mtp = 1; +} +``` + +### QueryOpenEstimationRequest + +**Request**: + +- **Position position**: Position to estimate. +- **string leverage**: Leverage for the position. +- **string trading_asset**: Trading asset. +- **cosmos.base.v1beta1.Coin collateral**: Collateral. +- **string discount**: Discount for the position. +- **string take_profit_price**: Take profit price. + +**Response**: + +- **Position position**: Estimated position. +- **string leverage**: Leverage for the position. +- **string trading_asset**: Trading asset. +- **cosmos.base.v1beta1.Coin collateral**: Collateral. +- **cosmos.base.v1beta1.Coin min_collateral**: Minimum collateral. +- **bool valid_collateral**: Whether the collateral is valid. +- **cosmos.base.v1beta1.Coin position_size**: Position size. +- **string swap_fee**: Swap fee. +- **string discount**: Discount for the position. +- **string open_price**: Open price. +- **string take_profit_price**: Take profit price. +- **string liquidation_price**: Liquidation price. +- **string estimated_pnl**: Estimated PnL. +- **string estimated_pnl_denom**: Denomination of the estimated PnL. +- **cosmos.base.v1beta1.Coin available_liquidity**: Available liquidity. +- **string slippage**: Slippage. +- **string weight_balance_ratio**: Weight balance ratio. +- **string borrow_interest_rate**: Borrow interest rate. +- **string funding_rate**: Funding rate. +- **string price_impact**: Price impact. + +```proto +message QueryOpenEstimationRequest { + Position position = 1; + string leverage = 2 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; + string trading_asset = 3; + cosmos.base.v1beta1.Coin collateral = 4 [ + (gogoproto.nullable) = false + ]; + string discount = 5 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; + string take_profit_price = 6 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; +} + +message QueryOpenEstimationResponse { + Position position = 1; + string leverage = 2 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; + string trading_asset = 3; + cosmos.base.v1beta1.Coin collateral = 4 [ + (gogoproto.nullable) = false + ]; + cosmos.base.v1beta1.Coin min_collateral = 5 [ + (gogoproto.nullable) = false + ]; + bool valid_collateral = 6; + cosmos.base.v1beta1.Coin position_size = 7 [ + (gogoproto.null + +able) = false + ]; + string swap_fee = 8 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; + string discount = 9 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; + string open_price = 10 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; + string take_profit_price = 11 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; + string liquidation_price = 12 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; + string estimated_pnl = 13 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", + (gogoproto.nullable) = false + ]; + string estimated_pnl_denom = 14; + cosmos.base.v1beta1.Coin available_liquidity = 15 [ + (gogoproto.nullable) = false + ]; + string slippage = 16 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; + string weight_balance_ratio = 17 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; + string borrow_interest_rate = 18 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; + string funding_rate = 19 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; + string price_impact = 20 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; +} +``` + +## Transaction Service Definitions + +The `Msg` service defines the gRPC transaction service for the perpetual module. + +### Msg Service + +- **Open**: Opens a new position. +- **BrokerOpen**: Broker opens a new position. +- **Close**: Closes an existing position. +- **BrokerClose**: Broker closes an existing position. +- **UpdateParams**: Updates the module parameters. +- **Whitelist**: Adds an address to the whitelist. +- **Dewhitelist**: Removes an address from the whitelist. + +### MsgOpen + +**Request**: + +- **string creator**: Creator of the message. +- **Position position**: Position to open. +- **string leverage**: Leverage for the position. +- **string trading_asset**: Trading asset. +- **cosmos.base.v1beta1.Coin collateral**: Collateral. +- **string take_profit_price**: Take profit price. + +**Response**: + +- **uint64 id**: ID of the opened position. + +```proto +message MsgOpen { + string creator = 1; + Position position = 2; + string leverage = 3 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; + string trading_asset = 4; + cosmos.base.v1beta1.Coin collateral = 5 [ + (gogoproto.nullable) = false + ]; + string take_profit_price = 6 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; +} + +message MsgOpenResponse { + uint64 id = 1; +} +``` + +### MsgBrokerOpen + +**Request**: + +- **string creator**: Creator of the message. +- **Position position**: Position to open. +- **string leverage**: Leverage for the position. +- **string trading_asset**: Trading asset. +- **cosmos.base.v1beta1.Coin collateral**: Collateral. +- **string take_profit_price**: Take profit price. +- **string owner**: Owner of the position. + +**Response**: + +- **uint64 id**: ID of the opened position. + +```proto +message MsgBrokerOpen { + string creator = 1; + Position position = 2; + string leverage = 3 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; + string trading_asset = 4; + cosmos.base.v1beta1.Coin collateral = 5 [ + (gogoproto.nullable) = false + ]; + string take_profit_price = 6 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; + string owner = 7; +} + +message MsgOpenResponse { + uint64 id = 1; +} +``` + +### MsgClose + +**Request**: + +- **string creator**: Creator of the message. +- **uint64 id**: ID of the position to close. +- **string amount**: Amount to close. + +**Response**: + +- **uint64 id**: ID of the closed position. +- **string amount**: Closed amount. + +```proto +message MsgClose { + string creator = 1; + uint64 id = 2; + string amount = 3 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", + (gogoproto.nullable) = false + ]; +} + +message MsgCloseResponse { + uint64 id = 1; + string amount = 2 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", + (gogoproto.nullable) = false + ]; +} +``` + +### MsgBrokerClose + +**Request**: + +- **string creator**: Creator of the message. +- **uint64 id**: ID of the position to close. +- **string amount**: Amount to close. +- **string owner**: Owner of the position. + +**Response**: + +- **uint64 id**: ID of the closed position. +- **string amount**: Closed amount. + +```proto +message MsgBrokerClose { + string creator = 1; + uint64 id = 2; + string amount = 3 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", + (gogoproto.nullable) = false + ]; + string owner = 4; +} + +message MsgCloseResponse { + uint64 id = 1; + string amount = 2 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", + (gogoproto.nullable) = false + ]; +} +``` + +### MsgUpdateParams + +**Request**: + +- **string authority**: Authority to update the parameters. +- **Params params**: New parameters. + +**Response**: None + +```proto +message MsgUpdateParams { + string authority = 1; + Params params = 2; +} + +message MsgUpdateParamsResponse {} +``` + +### MsgWhitelist + +**Request**: + +- **string authority**: Authority to whitelist an address. +- **string whitelisted_address**: Address to be whitelisted. + +**Response**: None + +```proto +message MsgWhitelist { + string authority = 1; + string whitelisted_address = 2; +} + +message MsgWhitelistResponse {} +``` + +### MsgDewhitelist + +**Request**: + +- **string authority**: Authority to dewhitelist an address. +- **string whitelisted_address**: Address to be dewhitelisted. + +**Response**: None + +```proto +message MsgDewhitelist { + string authority = 1; + string whitelisted_address = 2; +} + +message MsgDewhitelistResponse {} +``` + +## Type Definitions + +The `MTP` and `Position` messages define the structure of positions and MTPs in the perpetual module. + +### MTP + +- **string address**: Address associated with the MTP. +- **string collateral_asset**: Collateral asset. +- **string trading_asset**: Trading asset. +- **string liabilities_asset**: Liabilities asset. +- **string custody_asset**: Custody asset. +- **string collateral**: Collateral amount. +- **string liabilities**: Liabilities amount. +- **string borrow_interest_paid_collateral**: Borrow interest paid in collateral. +- **string borrow_interest_paid_custody**: Borrow interest paid in custody. +- **string borrow_interest_unpaid_collateral**: Unpaid borrow interest in collateral. +- **string custody**: Custody amount. +- **string take_profit_liabilities**: Take profit liabilities amount. +- **string take_profit_custody**: Take profit custody amount. +- **string leverage**: Leverage for the MTP. +- **string mtp_health**: Health of the MTP. +- **Position position**: Position type (LONG or SHORT). +- **uint64 id**: ID of the MTP. +- **uint64 amm_pool_id**: AMM pool ID. + +- **string consolidate_leverage**: Consolidated leverage. +- **string sum_collateral**: Sum of collateral. +- **string take_profit_price**: Take profit price. +- **string take_profit_borrow_rate**: Take profit borrow rate. +- **string funding_fee_paid_collateral**: Funding fee paid in collateral. +- **string funding_fee_paid_custody**: Funding fee paid in custody. +- **string funding_fee_received_collateral**: Funding fee received in collateral. +- **string funding_fee_received_custody**: Funding fee received in custody. +- **string open_price**: Open price. + +```proto +message MTP { + string address = 1; + string collateral_asset = 2; + string trading_asset = 3; + string liabilities_asset = 4; + string custody_asset = 5; + string collateral = 6 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", + (gogoproto.nullable) = false + ]; + string liabilities = 7 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", + (gogoproto.nullable) = false + ]; + string borrow_interest_paid_collateral = 8 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", + (gogoproto.nullable) = false + ]; + string borrow_interest_paid_custody = 9 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", + (gogoproto.nullable) = false + ]; + string borrow_interest_unpaid_collateral = 10 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", + (gogoproto.nullable) = false + ]; + string custody = 11 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", + (gogoproto.nullable) = false + ]; + string take_profit_liabilities = 12 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", + (gogoproto.nullable) = false + ]; + string take_profit_custody = 13 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", + (gogoproto.nullable) = false + ]; + string leverage = 14 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; + string mtp_health = 15 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; + Position position = 16; + uint64 id = 17; + uint64 amm_pool_id = 18; + string consolidate_leverage = 19 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; + string sum_collateral = 20 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", + (gogoproto.nullable) = false + ]; + string take_profit_price = 21 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; + string take_profit_borrow_rate = 22 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; + string funding_fee_paid_collateral = 23 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", + (gogoproto.nullable) = false + ]; + string funding_fee_paid_custody = 24 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", + (gogoproto.nullable) = false + ]; + string funding_fee_received_collateral = 25 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", + (gogoproto.nullable) = false + ]; + string funding_fee_received_custody = 26 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", + (gogoproto.nullable) = false + ]; + string open_price = 27 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; +} +``` + +### Position + +The `Position` enum defines the possible position types in the perpetual module. + +```proto +enum Position { + UNSPECIFIED = 0; + LONG = 1; + SHORT = 2; +} +``` + +### WhiteList + +- **repeated string validator_list**: List of validators. + +```proto +message WhiteList { + repeated string validator_list = 1; +} +``` diff --git a/x/perpetual/spec/06_functions.md b/x/perpetual/spec/06_functions.md new file mode 100644 index 000000000..a8dce99cb --- /dev/null +++ b/x/perpetual/spec/06_functions.md @@ -0,0 +1,447 @@ + + +# Functions + +## BeginBlocker + +### BeginBlocker + +The `BeginBlocker` function checks if the epoch has passed and executes necessary operations for each pool. + +```go +func (k Keeper) BeginBlocker(ctx sdk.Context) { + // check if epoch has passed then execute + epochLength := k.GetEpochLength(ctx) + epochPosition := k.GetEpochPosition(ctx, epochLength) + + // if epoch has not passed + if epochPosition != 0 { + return + } + + // if epoch has passed + entry, found := k.assetProfileKeeper.GetEntry(ctx, ptypes.BaseCurrency) + if !found { + ctx.Logger().Error(errorsmod.Wrapf(assetprofiletypes.ErrAssetProfileNotFound, "asset %s not found", ptypes.BaseCurrency).Error()) + } + baseCurrency := entry.Denom + baseCurrencyDecimal := entry.Decimals + + currentHeight := ctx.BlockHeight() + pools := k.GetAllPools(ctx) + for _, pool := range pools { + ammPool, err := k.GetAmmPool(ctx, pool.AmmPoolId, "") + if err != nil { + ctx.Logger().Error(errorsmod.Wrap(err, fmt.Sprintf("error getting amm pool: %d", pool.AmmPoolId)).Error()) + continue + } + if k.IsPoolEnabled(ctx, pool.AmmPoolId) { + rate, err := k.BorrowInterestRateComputation(ctx, pool) + if err != nil { + ctx.Logger().Error(err.Error()) + continue + } + pool.BorrowInterestRate = rate + pool.LastHeightBorrowInterestRateComputed = currentHeight + err = k.UpdatePoolHealth(ctx, &pool) + if err != nil { + ctx.Logger().Error(err.Error()) + } + err = k.UpdateFundingRate(ctx, &pool) + if err != nil { + ctx.Logger().Error(err.Error()) + } + + mtps, _, _ := k.GetMTPsForPool(ctx, pool.AmmPoolId, nil) + for _, mtp := range mtps { + err := BeginBlockerProcessMTP(ctx, k, mtp, pool, ammPool, baseCurrency, baseCurrencyDecimal) + if err != nil { + ctx.Logger().Error(err.Error()) + continue + } + } + err = k.HandleFundingFeeDistribution(ctx, mtps, &pool, ammPool, baseCurrency) + if err != nil { + ctx.Logger().Error(err.Error()) + } + } + k.SetPool(ctx, pool) + } +} +``` + +## Params + +### GetParams + +The `GetParams` function retrieves the current parameters of the `perpetual` module. + +```go +func (k Keeper) GetParams(ctx sdk.Context) (params types.Params) { + store := ctx.KVStore(k.storeKey) + bz := store.Get(types.KeyPrefix(types.ParamsKey)) + if bz == nil { + return params + } + + k.cdc.MustUnmarshal(bz, ¶ms) + return params +} +``` + +### SetParams + +The `SetParams` function sets the parameters of the `perpetual` module. + +```go +func (k Keeper) SetParams(ctx sdk.Context, params *types.Params) error { + if err := params.Validate(); err != nil { + return err + } + + store := ctx.KVStore(k.storeKey) + bz, err := k.cdc.Marshal(params) + if err != nil { + return err + } + store.Set(types.KeyPrefix(types.ParamsKey), bz) + + return nil +} +``` + +## MTP + +### GetMTP + +The `GetMTP` function retrieves an MTP by address and ID. + +```go +func (k Keeper) GetMTP(ctx sdk.Context, mtpAddress string, id uint64) (types.MTP, error) { + var mtp types.MTP + key := types.GetMTPKey(mtpAddress, id) + store := ctx.KVStore(k.storeKey) + if !store.Has(key) { + return mtp, types.ErrMTPDoesNotExist + } + bz := store.Get(key) + k.cdc.MustUnmarshal(bz, &mtp) + return mtp, nil +} +``` + +### SetMTP + +The `SetMTP` function sets an MTP in the store. + +```go +func (k Keeper) SetMTP(ctx sdk.Context, mtp *types.MTP) error { + store := ctx.KVStore(k.storeKey) + count := k.GetMTPCount(ctx) + openCount := k.GetOpenMTPCount(ctx) + + if mtp.Id == 0 { + // increment global id count + count++ + mtp.Id = count + k.SetMTPCount(ctx, count) + // increment open mtp count + openCount++ + k.SetOpenMTPCount(ctx, openCount) + } + + if err := mtp.Validate(); err != nil { + return err + } + key := types.GetMTPKey(mtp.Address, mtp.Id) + store.Set(key, k.cdc.MustMarshal(mtp)) + return nil +} +``` + +## Pool + +### GetPool + +The `GetPool` function returns a pool by its ID. + +```go +func (k Keeper) GetPool(ctx sdk.Context, poolId uint64) (val types.Pool, found bool) { + store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefix(types.PoolKeyPrefix)) + + b := store.Get(types.PoolKey(poolId)) + if b == nil { + return val, false + } + + k.cdc.MustUnmarshal(b, &val) + return val, true +} +``` + +### SetPool + +The `SetPool` function sets a specific pool in the store by its index. + +```go +func (k Keeper) SetPool(ctx sdk.Context, pool types.Pool) { + store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefix(types.PoolKeyPrefix)) + b := k.cdc.MustMarshal(&pool) + store.Set(types.PoolKey(pool.AmmPoolId), b) +} +``` + +## Hooks + +### SetHooks + +The `SetHooks` function sets the perpetual hooks. + +```go +func (k *Keeper) SetHooks(gh types.PerpetualHooks) *Keeper { + if k.hooks != nil { + panic("cannot set perpetual hooks twice") + } + + k.hooks = gh + + return k +} +``` + +## Query + +### GetPositions + +The `GetPositions` function retrieves MTPs with pagination. + +```go +func (k Keeper) GetPositions(goCtx context.Context, req *types.PositionsRequest) (*types.PositionsResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + + if req.Pagination != nil && req.Pagination.Limit > types.MaxPageLimit { + return nil, status.Error(codes.InvalidArgument, fmt.Sprintf("page size greater than max %d", types.MaxPageLimit)) + } + + ctx := sdk.UnwrapSDKContext(goCtx) + mtps, page, err := k.GetMTPs(ctx, req.Pagination) + if err != nil { + return nil, err + } + + return &types.PositionsResponse{ + Mtps: mtps, + Pagination: page, + }, nil +} +``` + +### GetPositionsByPool + +The `GetPositionsByPool` function retrieves MTPs for a specific pool with pagination. + +```go +func (k Keeper) GetPositionsByPool(goCtx context.Context, req *types.PositionsByPoolRequest) (*types.PositionsByPoolResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + + ctx := sdk.UnwrapSDKContext(goCtx) + mtps, pageRes, err := k.GetMTPsForPool(ctx, req.AmmPoolId, req.Pagination) + if err != nil { + return nil, err + } + + return &types.PositionsByPoolResponse{ + Mtps: mtps, + Pagination: pageRes, + }, nil +} +``` + +### GetPositionsForAddress + +The `GetPositionsForAddress` function retrieves MTPs for a specific address with pagination. + +```go +func (k Keeper) GetPositionsForAddress(goCtx context.Context, req *types.PositionsForAddressRequest) (*types.PositionsForAddressResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + + addr, err := sdk.AccAddressFromBech32(req.Address) + if err != nil { + return nil, err + } + + mtps, pageRes, err := k.GetMTPsForAddress(sdk.UnwrapSDKContext(goCtx), addr, req.Pagination) + if err != nil { + return nil, err + } + + return &types.PositionsForAddressResponse{Mtps: mtps, Pagination: pageRes}, nil +} +``` + +### GetStatus + +The `GetStatus` function retrieves the open and lifetime MTP count. + +```go +func (k Keeper) GetStatus(goCtx context.Context, req *types.StatusRequest) (*types.StatusResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + + ctx := sdk.UnwrapSDKContext(goCtx) + return &types.StatusResponse{ + OpenMtpCount: k.GetOpenMTPCount(ctx + +), + LifetimeMtpCount: k.GetMTPCount(ctx), + }, nil +} +``` + +### GetModuleBalance + +The `GetModuleBalance` function retrieves the balance of the module account. + +```go +func (k Keeper) GetModuleBalance(ctx sdk.Context, denom string) sdk.Coin { + moduleAccount := k.accountKeeper.GetModuleAccount(ctx, types.ModuleName) + balance := k.bankKeeper.GetBalance(ctx, moduleAccount.GetAddress(), denom) + return balance +} +``` + +### GetAllPools + +The `GetAllPools` function retrieves all pools from the store. + +```go +func (k Keeper) GetAllPools(ctx sdk.Context) (pools []types.Pool) { + store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, types.KeyPrefix(types.PoolKeyPrefix)) + + defer iterator.Close() + for ; iterator.Valid(); iterator.Next() { + var pool types.Pool + k.cdc.MustUnmarshal(iterator.Value(), &pool) + pools = append(pools, pool) + } + + return pools +} +``` + +## Epoch + +### GetEpochLength + +The `GetEpochLength` function retrieves the length of an epoch. + +```go +func (k Keeper) GetEpochLength(ctx sdk.Context) uint64 { + return k.GetParams(ctx).EpochLength +} +``` + +### GetEpochPosition + +The `GetEpochPosition` function calculates the current position within the epoch. + +```go +func (k Keeper) GetEpochPosition(ctx sdk.Context, epochLength uint64) uint64 { + return uint64(ctx.BlockHeight()) % epochLength +} +``` + +## MTP Handling + +### BeginBlockerProcessMTP + +The `BeginBlockerProcessMTP` function processes an MTP during the BeginBlocker. + +```go +func BeginBlockerProcessMTP( + ctx sdk.Context, + k Keeper, + mtp types.MTP, + pool types.Pool, + ammPool types.AmmPool, + baseCurrency string, + baseCurrencyDecimal uint8, +) error { + // Implementation of the processing logic + // ... + + return nil +} +``` + +### HandleFundingFeeDistribution + +The `HandleFundingFeeDistribution` function handles the distribution of funding fees. + +```go +func (k Keeper) HandleFundingFeeDistribution( + ctx sdk.Context, + mtps []types.MTP, + pool *types.Pool, + ammPool types.AmmPool, + baseCurrency string, +) error { + // Implementation of the fee distribution logic + // ... + + return nil +} +``` + +## Interest Rate + +### BorrowInterestRateComputation + +The `BorrowInterestRateComputation` function computes the borrow interest rate for a pool. + +```go +func (k Keeper) BorrowInterestRateComputation(ctx sdk.Context, pool types.Pool) (sdk.Dec, error) { + // Implementation of the interest rate computation + // ... + + return sdk.ZeroDec(), nil +} +``` + +## Pool Health + +### UpdatePoolHealth + +The `UpdatePoolHealth` function updates the health of a pool. + +```go +func (k Keeper) UpdatePoolHealth(ctx sdk.Context, pool *types.Pool) error { + // Implementation of the pool health update logic + // ... + + return nil +} +``` + +### UpdateFundingRate + +The `UpdateFundingRate` function updates the funding rate for a pool. + +```go +func (k Keeper) UpdateFundingRate(ctx sdk.Context, pool *types.Pool) error { + // Implementation of the funding rate update logic + // ... + + return nil +} +``` diff --git a/x/perpetual/spec/README.md b/x/perpetual/spec/README.md index 2ea5d86f7..90acb6074 100644 --- a/x/perpetual/spec/README.md +++ b/x/perpetual/spec/README.md @@ -1,40 +1,108 @@ -# Perpetual module + -Perpetual module is to support perpetual trading on molecule token pools. -At initial, isolated perpetual is to be supported and cross perpetual will be supported later. +# Concepts -## Pool +The `perpetual` module in the Elys Network facilitates perpetual trading, allowing users to open and close positions on various assets. This guide provides an overview of the key concepts and mechanisms involved in the perpetual module. -Perpetual will use amm pool and will have position records. When the trader closes the position or liquidated, pool tokens will be transferred to the perpetual trader for P&L. +## Key Concepts -## Perpetual limit +### Perpetual Trading -Based on pool size perpetual should be limited. +Perpetual trading allows users to trade assets with leverage without the need for expiry dates on their positions. This type of trading is popular in the cryptocurrency market as it offers flexibility and continuous trading opportunities. -## Race condition between amm & perpetual +### Positions -Pool could have lack of balance. Therefore why we have to keep a healthy buffer when setting perpetual position. We should not allow more than 50% of the pool to be borrowed for perpetual. +A position in perpetual trading represents an open contract on an asset. Positions can be either long (betting that the asset price will rise) or short (betting that the asset price will fall). The size of the position is determined by the leverage applied and the amount of collateral provided. -The position should be auto-closed before the pool becomes insufficient to cover. +### Leverage -Any action which affects the health of the position shud trigger position closing. +Leverage allows traders to open positions larger than their collateral by borrowing funds. For example, with 5x leverage, a trader can open a position five times the size of their collateral. While leverage amplifies potential profits, it also increases potential losses. -## Risks +### Collateral -We are not going to offer perpetual on shallow pools. +Collateral is the amount of assets locked to open a leveraged position. It acts as a security deposit to cover potential losses. If the value of the position falls below the maintenance margin, the position may be liquidated to prevent further losses. -## Oracle +### Safety Factor -Oracle should use average price to prevent too big liquidations sudden dump for a short time. +The safety factor is the threshold to keep a position open. If the position health value falls below this threshold, the position may be subject to liquidation. -If we have 3 oracle price sources for example and one experiences a massive candle anomaly, We will need to add exceptions for this case. +### Position Health -## Reference codebases for perpetual +Position health is a metric that indicates the risk level of a position. It is calculated based on the position's value, leverage, and collateral. Monitoring position health helps prevent excessive risk-taking and ensures the stability of the perpetual trading system. -- TBD +### Liquidation -## Notes +Liquidation occurs when a position's value drops below the safety factor. The position is forcibly closed to prevent further losses. This mechanism ensures the stability of the perpetual trading system and protects the platform from significant losses. -- Perpetual code to be the base code for LP leveraging - Only real difference being that for perpetual the borrow is from the pool liqudity itself while for LP leveraging the borrow is from base currency deposit +### Funding Rate -- Ultimately when we have cross perpetual, that’s when perpetual positions and LP positions can interact +The funding rate is a periodic payment exchanged between long and short positions. It ensures that the perpetual contract price closely tracks the underlying asset price. When the funding rate is positive, longs pay shorts, and when it is negative, shorts pay longs. + +### Whitelisting + +Whitelisting is a mechanism to control access to the perpetual module. Only addresses that are whitelisted can participate in trading. This helps maintain the security and integrity of the platform by allowing only trusted participants. + +### Parameters + +The perpetual module has several configurable parameters that govern its operation. These include leverage limits, interest rates, maintenance margins, and more. These parameters can be updated through governance proposals to adapt to changing market conditions and ensure the module's efficiency and security. + +## Perpetual Module Overview + +The perpetual module supports perpetual trading on molecule token pools. Initially, isolated perpetual trading is supported, with plans to include cross perpetual trading in the future. + +### Pools + +Perpetual trading utilizes AMM (Automated Market Maker) pools and maintains position records. When a trader closes a position or it is liquidated, pool tokens are transferred to the perpetual trader for profit and loss (P&L). + +### Perpetual Limits + +Perpetual trading limits are based on the pool size. To maintain pool health, perpetual positions are restricted to ensure that no more than 50% of the pool's assets are borrowed. + +### Race Condition Between AMM and Perpetual + +Due to the possibility of pool balance insufficiency, a healthy buffer must be maintained when setting perpetual positions. The position should be auto-closed before the pool becomes insufficient to cover it. Any action affecting the position's health should trigger position closing. + +### Risks + +Perpetual trading will not be offered on shallow pools to mitigate risk. + +### Oracle + +The oracle should use average prices to prevent large liquidations caused by sudden, short-term price dumps. In cases where multiple oracle price sources are used, exceptions may need to be made for anomalies such as massive candle spikes from one source. + +### Reference Codebases for Perpetual + +- To Be Determined (TBD) + +### Notes + +- The perpetual code will serve as the base for LP leveraging, with the primary difference being that perpetual trading borrows from the pool liquidity itself, while LP leveraging borrows from base currency deposits. +- Cross perpetual trading will enable interactions between perpetual positions and LP positions once implemented. + +## Transaction Commands + +The perpetual module supports various transaction commands for managing positions and parameters. Key commands include: + +- `open`: Opens a new perpetual position. +- `close`: Closes an existing perpetual position. +- `whitelist`: Adds an address to the whitelist. +- `dewhitelist`: Removes an address from the whitelist. +- `update-params`: Updates the module parameters through a governance proposal. + +## Query Commands + +Users can query various aspects of the perpetual module using the following commands: + +- `params`: Retrieves the current parameters of the module. +- `get-positions`: Lists all open positions. +- `get-positions-by-pool`: Lists positions for a specific pool. +- `get-positions-for-address`: Lists positions for a specific address. +- `get-status`: Retrieves the current status of the module. +- `get-whitelist`: Lists all whitelisted addresses. +- `is-whitelisted`: Checks if a specific address is whitelisted. +- `list-pool`: Lists all available pools. +- `show-pool`: Retrieves details of a specific pool. +- `get-mtp`: Retrieves details of a specific margin trading position (MTP). +- `open-estimation`: Provides an estimation for opening a position.