Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Qol improvements #35

Merged
merged 8 commits into from
Aug 1, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -144,3 +144,4 @@ cython_debug/
.vscode
.chain_cookie
.exchange_cookie
*.ipynb
198 changes: 198 additions & 0 deletions docs/source/dex.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
Concepts
========

The ``x/dex`` module is responsible for creating, joining, and
exiting liquidity pools that are dictated by an AMM for swaps.

Pool
----

Creation of Pool
~~~~~~~~~~~~~~~~

When a pool is created, a fixed amount of 100 LP shares is minted and
sent to the pool creator. The base pool share denom is in the format of
``nibiru/pool/{poolId}`` and is displayed in the format of
``NIBIRU-POOL-{poolId}`` to the user. One ``NIBIRU-POOL-{poolId}`` token is
equivalent to 10^18 ``nibiru/pool/{poolId}`` tokens.

Pool assets are sorted in alphabetical order by default.

You can create a pool with the nibiru-py package with:

.. code:: python

import nibiru as nib

tx_config = nib.TxConfig(tx_type=TxType.BLOCK)
trader = nib.Sdk.authorize(MNEMONIC).with_config(tx_config)

trader.tx.dex.create_pool(
creator=trader.address,
swap_fee=0.02,
exit_fee=0.1,
assets=[
nib.PoolAsset(
token=nib.Coin(
denom="unibi",
amount=1000,
),
weight=50
),
nib.PoolAsset(
token=nib.Coin(
denom="unusd",
amount=10000,
),
weight=50
),
]
)

You can then query the pools with the dex queries:

.. code:: python

trader.query.dex.pools()


Joining Pool
~~~~~~~~~~~~

When joining a pool, users provide the tokens they are willing to
deposit. The application will try to deposit as many tokens as it can
while maintaining equal weight ratios across the pool’s assets. Usually
this means one asset acts as a limiting factor and all other tokens are
deposited in proportion to the limited token.

For example, assume there is a 50/50 pool with 100 ``tokenA`` and 100
``tokenB``. A user wishes to LP 10 ``tokenA`` and 5 ``tokenB`` into the
pool. Because ``tokenB`` is the limiting factor, all of ``tokenB`` will
be deposited and only 5 of ``tokenA`` will be deposited. The user will
be left with 5 ``tokenA`` and receive LP shares for the liquidity they
provided.

.. code:: python

trader.tx.dex.join_pool(
sender=trader.address,
pool_id=4,
tokens=[
nib.Coin(
denom="unibi",
amount=10000,
),
nib.Coin(
denom="unusd",
amount=10000,
)
]
)

trader.query.get_bank_balance(
trader.address,
denom="nibiru/pool/4"
)

"""
balance {
denom: "nibiru/pool/4"
amount: "200000000000000000000"
}
"""


Exiting Pool
~~~~~~~~~~~~

When exiting the pool, the user also provides the number of LP shares
they are returning to the pool, and will receive assets in proportion to
the LP shares returned. However, unlike joining a pool, exiting a pool
requires the user to pay the exit fee, which is set as the param of the
pool. The share of the user gets burnt.

For example, assume there is a 50/50 pool with 50 ``tokenA`` and 150
``tokenB`` and 200 total LP shares minted. A user wishes to return 20 LP
shares to the pool and withdraw their liquidity. Because 20/200 = 10%,
the user will receive 5 ``tokenA`` and 15 ``tokenB`` from the pool,
minus exit fees.

.. code:: python

trader.tx.dex.exit_pool(
sender=trader.address,
pool_id=4,
pool_shares=nib.Coin(denom="nibiru/pool/4",amount=50000000000000000000)
)

trader.query.get_bank_balance(trader.address, denom="nibiru/pool/4")

"""
balance {
denom: "nibiru/pool/4"
amount: "150000000000000000000"
}
"""

Swap
----

During the process of swapping a specific asset, the token user is
putting into the pool is justified as ``tokenIn``, while the token that
would be omitted after the swap is justified as ``tokenOut`` throughout
the module.

Given a tokenIn, the following calculations are done to calculate how
much tokens are to be swapped and ommitted from the pool.

- ``tokenBalanceOut * [ 1 - { tokenBalanceIn / (tokenBalanceIn+(1-swapFee) * tokenAmountIn)}^(tokenWeightIn/tokenWeightOut)]``

The whole process is also able vice versa, the case where user provides
tokenOut. The calculation for the amount of token that the user should
be putting in is done through the following formula.

- ``tokenBalanceIn * [{tokenBalanceOut / (tokenBalanceOut - tokenAmountOut)}^(tokenWeightOut/tokenWeightIn)-1] / tokenAmountIn``


.. code:: python

trader.tx.dex.swap_assets(
sender=trader.address,
pool_id=4,
token_in=nib.Coin(denom="unusd",amount=1000000000),
token_out_denom="unibi"
)

The queries in the dex query module can give estimate of the output of this command
with the current reserves of the pool:

.. code:: python

trader.query.dex.estimate_swap_exact_amount_in(
pool_id=4,
token_in=nib.Coin(denom="unibi", amount=10000),
token_out_denom="unusd"
)


Spot Price
~~~~~~~~~~

Meanwhile, calculation of the spot price with a swap fee is done using
the following formula

- ``spotPrice / (1-swapFee)``

where spotPrice is

- ``(tokenBalanceIn / tokenWeightIn) / (tokenBalanceOut / tokenWeightOut)``

You can query the spot price with:

.. code:: python

trader.query.dex.spot_price(
pool_id=4,
token_in_denom="unibi",
token_out_denom="unusd"
)
1 change: 1 addition & 0 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Contents

overview
perpetual
dex
general
constants

Expand Down
4 changes: 2 additions & 2 deletions nibiru/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from .client import Client # noqa
from .common import Direction, PoolAsset, Side, TxConfig # noqa
from .common import Direction, PoolAsset, Side, TxConfig, Coin # noqa
from .composer import Composer # noqa
from .network import Network # noqa
from .sdk import Sdk # noqa
from .transaction import Transaction # noqa
from .wallet import Address, PrivateKey, PublicKey # noqa
from .wallet import Address, PrivateKey, PublicKey # noqa
94 changes: 90 additions & 4 deletions nibiru/clients/perp.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

from nibiru.proto.perp.v1 import query_pb2 as perp_type
from nibiru.proto.perp.v1 import query_pb2_grpc as perp_query
from google.protobuf.json_format import MessageToDict
from nibiru.utils import from_sdk_dec, from_sdk_dec_24

from .util import deserialize

Expand All @@ -15,12 +17,96 @@ def __init__(self, channel: Channel):
self.api = perp_query.QueryStub(channel)

def params(self):
req = perp_type.QueryParamsRequest()
return deserialize(self.api.Params(req))
"""
Get the parameters of the perp module.

def trader_position(self, token_pair: str, trader: str):
Output sample:

{
"maintenanceMarginRatio": 0.0625,
"feePoolFeeRatio": 0.001,
"ecosystemFundFeeRatio": 0.001,
"liquidationFeeRatio": 0.025,
"partialLiquidationRatio": 0.25,
"epochIdentifier": "30 min",
"twapLookbackWindow": "900s"
}

Returns:
dict: The current parameters for the perpetual module
"""
proto_output: perp_type.QueryParamsResponse = self.api.Params(perp_type.QueryParamsRequest())
output = MessageToDict(proto_output)["params"]

sdk_dec_fields = [
"maintenanceMarginRatio",
"feePoolFeeRatio",
"ecosystemFundFeeRatio",
"liquidationFeeRatio",
"partialLiquidationRatio",
]

for field in sdk_dec_fields:
output[field] = from_sdk_dec(output[field])

return output

def trader_position(self, token_pair: str, trader: str) -> dict:
"""
Get the trader position. Returns information about position notional, margin ratio
unrealized pnl, size of the position etc.

Args:
token_pair (str): The token pair
trader (str): The trader address

Sample output:
{
"position": {
"traderAddress": "nibi1zaavvzxez0elund",
"pair": {
"token0": "axlwbtc",
"token1": "unusd"
},
"size": 11.241446725317692,
"margin": 45999.99999999999,
"openNotional": 230000.0,
"lastUpdateCumulativePremiumFraction": "0",
"blockNumber": "278"
},
"positionNotional": 230000.0,
"unrealizedPnl": 1.024e-20,
"marginRatio": 0.2
}

Returns:
dict: The output of the query
"""
req = perp_type.QueryTraderPositionRequest(
token_pair=token_pair,
trader=trader,
)
return deserialize(self.api.TraderPosition(req))

proto_output: perp_type.QueryTraderPositionResponse = self.api.TraderPosition(req)
output = MessageToDict(proto_output)

position_sdk_dec_fields = [
"size",
"margin",
"openNotional",
]

sdk_dec_fields= [
"positionNotional",
"unrealizedPnl",
]
for field in position_sdk_dec_fields:
output["position"][field] = from_sdk_dec_24(output["position"][field])


for field in sdk_dec_fields:
output[field] = from_sdk_dec_24(output[field])

output["marginRatio"] = from_sdk_dec(output["marginRatio"])

return output
13 changes: 10 additions & 3 deletions nibiru/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from enum import Enum

from .proto.cosmos.base.v1beta1 import coin_pb2 as coin_pb

from .proto.cosmos.base.v1beta1 import coin_pb2 as cosmos_base_coin_pb

class TxType(Enum):
SYNC = 1
Expand All @@ -19,11 +19,18 @@ class Direction(Enum):
ADD = 1
REMOVE = 2

@dataclass
class Coin:
amount: float
denom: str

def _generate_proto_object(self):
return cosmos_base_coin_pb.Coin(amount=str(self.amount), denom=self.denom)

@dataclass
class PoolAsset:
token: coin_pb.Coin
weight: int
token: Coin
weight: float


class TxConfig:
Expand Down
4 changes: 1 addition & 3 deletions nibiru/composer.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from google.protobuf import any_pb2

from .composers import Dex, Perp, Pricefeed
from .composers import Pricefeed
from .proto.cosmos.authz.v1beta1 import tx_pb2 as cosmos_authz_tx_pb
from .proto.cosmos.bank.v1beta1 import tx_pb2 as cosmos_bank_tx_pb
from .proto.cosmos.base.v1beta1 import coin_pb2 as cosmos_base_coin_pb
Expand All @@ -11,8 +11,6 @@


class Composer:
dex = Dex
perp = Perp
pricefeed = Pricefeed

@staticmethod
Expand Down
2 changes: 0 additions & 2 deletions nibiru/composers/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1 @@
from .dex import Dex # noqa
from .perp import Perp # noqa
from .pricefeed import Pricefeed # noqa
Loading