Skip to content
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
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@ test:
tox

bump:
poetry version patch
poetry version minor
22 changes: 22 additions & 0 deletions examples/cases/vault/deposit_to_vault.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import logging
from asyncio import run
from decimal import Decimal

from examples.utils import create_trading_client

LOGGER = logging.getLogger()


async def run_example():
trading_client = create_trading_client()
collateral_amount = Decimal("5")

LOGGER.info("Creating deposit of %s USDC to vault...", collateral_amount)

await trading_client.vault.deposit_to_vault(collateral_amount)

LOGGER.info("Deposit created")


if __name__ == "__main__":
run(main=run_example())
22 changes: 22 additions & 0 deletions examples/cases/vault/withdraw_from_vault.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import logging
from asyncio import run
from decimal import Decimal

from examples.utils import create_trading_client

LOGGER = logging.getLogger()


async def run_example():
trading_client = create_trading_client()
shares_amount = Decimal("5")

LOGGER.info("Creating withdrawal of %s shares from vault...", shares_amount)

await trading_client.vault.withdraw_from_vault(shares_amount)

LOGGER.info("Withdrawal created")


if __name__ == "__main__":
run(main=run_example())
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ build-backend = "poetry.core.masonry.api"

[tool.poetry]
name = "x10-python-trading-starknet"
version = "1.2.0"
version = "1.3.0"
description = "Python client for X10 API"
authors = ["X10 <tech@ex10.org>"]
repository = "https://github.com/x10xchange/python_sdk"
Expand Down
14 changes: 14 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,20 @@ def create_account_update_unknown_message():
return _create_account_update_unknown_message


@pytest.fixture
def get_asset_usd():
from tests.fixtures.assets import get_asset_usd as _get_asset_usd

return _get_asset_usd


@pytest.fixture
def get_asset_xvs():
from tests.fixtures.assets import get_asset_xvs as _get_asset_xvs

return _get_asset_xvs


@pytest.fixture
def create_asset_operations():
from tests.fixtures.assets import (
Expand Down
34 changes: 33 additions & 1 deletion tests/fixtures/assets.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from decimal import Decimal

from x10.perpetual.assets import AssetOperationModel
from x10.perpetual.assets import AssetModel, AssetOperationModel


def create_asset_operations():
Expand All @@ -27,3 +27,35 @@ def create_asset_operations():
account_id=3004,
),
]


def get_asset_usd():
return AssetModel(
id=1,
name="USD",
symbol="USD",
precision=6,
is_active=True,
is_collateral=True,
starkex_id=0x1,
starkex_resolution=1000000,
l1_id="0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
l1_resolution=1000000,
version=3,
)


def get_asset_xvs():
return AssetModel(
id=94,
name="XVS",
symbol="XVS",
precision=6,
is_active=True,
is_collateral=False,
starkex_id=0x07DB365513DF1EE2EB8FC2D157D4D1CBA3D4A2EF59B44DD3D61124C88B4F6084,
starkex_resolution=1000000,
l1_id="0x07DB365513Df1eE2Eb8fC2d157d4D1cBA3d4a2eF59b44Dd3D61124C88b4f6084",
l1_resolution=1000000,
version=6,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
from decimal import Decimal

import pytest
from freezegun import freeze_time
from hamcrest import assert_that, equal_to
from pytest_mock import MockerFixture

FROZEN_NONCE = 1473459052


@freeze_time("2024-01-05 01:08:56.860694")
@pytest.mark.asyncio
async def test_create_buy_limit_order_settlement_data(
mocker: MockerFixture, create_trading_account, get_asset_usd, get_asset_xvs
):
mocker.patch("x10.utils.nonce.generate_nonce", return_value=FROZEN_NONCE)

from x10.perpetual.configuration import MAINNET_CONFIG
from x10.perpetual.limit_order_object_settlement import create_order_settlement_data

trading_account = create_trading_account()
collateral_asset = get_asset_usd()
vault_asset = get_asset_xvs()

settlement, quote_amount_human, base_amount_human = create_order_settlement_data(
quote_amount=Decimal("10"),
base_amount=Decimal("7"),
position_id=trading_account.vault,
quote_asset_model=collateral_asset,
base_asset_model=vault_asset,
starknet_account=trading_account,
starknet_domain=MAINNET_CONFIG.starknet_domain,
is_buy=True,
)

assert_that(quote_amount_human.value, equal_to(Decimal("-10")))
assert_that(base_amount_human.value, equal_to(Decimal("7")))
assert_that(
settlement.to_api_request_json(),
equal_to(
{
"baseAmount": 7000000,
"quoteAmount": -10000000,
"feeAmount": 0,
"baseAssetId": "0x7db365513df1ee2eb8fc2d157d4d1cba3d4a2ef59b44dd3d61124c88b4f6084",
"quoteAssetId": "0x1",
"feeAssetId": "0x1",
"expirationTimestamp": 1705630137,
"nonce": 1473459052,
"receiverPositionId": 10002,
"senderPositionId": 10002,
"signature": {
"r": "0x77c1a73e45bf4d7934f7deb04fbd4a3c9d3261baf2007e5f25b0fc681dd3183",
"s": "0x26f4fbcf7b3dbb2bf14c438bed1c482eb3a65dc9f73b305e56345c8a3e393cd",
},
}
),
)
72 changes: 72 additions & 0 deletions x10/perpetual/limit_order_object_settlement.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import decimal
from datetime import timedelta

from x10.perpetual.accounts import StarkPerpetualAccount
from x10.perpetual.amounts import HumanReadableAmount, StarkAmount
from x10.perpetual.assets import Asset, AssetModel
from x10.perpetual.configuration import StarknetDomain
from x10.perpetual.order_object_settlement import (
calculate_order_settlement_expiration,
hash_limit_order,
)
from x10.perpetual.orders import LimitOrderSettlementModel
from x10.utils.date import utc_now
from x10.utils.model import SettlementSignatureModel
from x10.utils.nonce import generate_nonce


def create_order_settlement_data(
*,
quote_amount,
base_amount,
position_id,
quote_asset_model: AssetModel,
base_asset_model: AssetModel,
starknet_account: StarkPerpetualAccount,
starknet_domain: StarknetDomain,
is_buy: bool,
):
quote_asset = Asset.from_model(quote_asset_model)
base_asset = Asset.from_model(base_asset_model)

quote_amount_human = HumanReadableAmount(
asset=quote_asset,
value=-quote_amount if is_buy else quote_amount,
)
base_amount_human = HumanReadableAmount(
asset=base_asset,
value=base_amount if is_buy else -base_amount,
)

quote_amount_stark = quote_amount_human.to_stark_amount(decimal.Context(rounding=decimal.ROUND_UP))
base_amount_stark = base_amount_human.to_stark_amount(decimal.Context(rounding=decimal.ROUND_UP))

nonce = generate_nonce()
expire_time = utc_now() + timedelta(hours=1)
order_hash = hash_limit_order(
amount_base=base_amount_stark,
amount_quote=quote_amount_stark,
max_fee=StarkAmount(0, quote_asset),
nonce=nonce,
position_id=position_id,
expiration_timestamp=expire_time,
public_key=starknet_account.public_key,
starknet_domain=starknet_domain,
)
order_signature = starknet_account.sign(order_hash)

settlement = LimitOrderSettlementModel(
base_amount=base_amount_stark.value,
quote_amount=quote_amount_stark.value,
fee_amount=0,
base_asset_id=int(base_asset.settlement_external_id, 16),
quote_asset_id=int(quote_asset.settlement_external_id, 16),
fee_asset_id=int(quote_asset.settlement_external_id, 16),
expiration_timestamp=calculate_order_settlement_expiration(expire_time),
nonce=nonce,
receiver_position_id=position_id,
sender_position_id=position_id,
signature=SettlementSignatureModel(r=order_signature[0], s=order_signature[1]),
)

return settlement, quote_amount_human, base_amount_human
Loading
Loading