Skip to content

Commit

Permalink
Merge branch 'main' into fix-historical-pnl-indexer-query-params
Browse files Browse the repository at this point in the history
  • Loading branch information
ruixhuang authored Jul 25, 2024
2 parents 6d21a92 + 8996a3a commit 6752bd8
Show file tree
Hide file tree
Showing 15 changed files with 264 additions and 41 deletions.
3 changes: 3 additions & 0 deletions v4-client-js/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,6 @@ npm-debug.log

# IDE
.idea

# Packed tgz for local dev
*.tgz
57 changes: 52 additions & 5 deletions v4-client-js/__native__/__ios__/v4-native-client.js

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions v4-client-js/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion v4-client-js/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@dydxprotocol/v4-client-js",
"version": "1.1.28",
"version": "1.1.30",
"description": "General client library for the new dYdX system (v4 decentralized)",
"main": "build/src/index.js",
"scripts": {
Expand Down
55 changes: 54 additions & 1 deletion v4-client-js/src/clients/native.ts
Original file line number Diff line number Diff line change
Expand Up @@ -968,7 +968,26 @@ export async function getDelegatorDelegations(payload: string): Promise<string>
}
}

export async function getDelegatorUnbondingDelegations(payload: string): Promise<string> {
export async function getStakingRewards(payload: string): Promise<string> {
try {
const client = globalThis.client;
if (client === undefined) {
throw new UserError('client is not connected. Call connectClient() first');
}
const json = JSON.parse(payload);
const address = json.address;
if (address === undefined) {
throw new UserError('address is not set');
}
const delegations =
await globalThis.client?.validatorClient.get.getDelegationTotalRewards(address);
return encodeJson(delegations);
} catch (e) {
return wrappedError(e);
}
}

export async function getCurrentUnstaking(payload: string): Promise<string> {
try {
const client = globalThis.client;
if (client === undefined) {
Expand Down Expand Up @@ -1138,6 +1157,40 @@ export async function cctpWithdraw(squidPayload: string): Promise<String> {
}
}


export async function cctpMultiMsgWithdraw(cosmosPayload: string): Promise<string> {
try {
const client = globalThis.nobleClient;
const messages: { typeUrl:string, value: { amount: string } }[] = JSON.parse(cosmosPayload)
if (client === undefined || !client.isConnected) {
throw new UserError('client is not connected.');
}
const ibcMsgs = messages.map(({ typeUrl, value }) => ({
typeUrl, // '/circle.cctp.v1.MsgDepositForBurnWithCaller', '/cosmos.bank.v1beta1.MsgSend'
value,
}));

const fee = await client.simulateTransaction(ibcMsgs);

// take out fee from amount before sweeping
const amount =
parseInt(ibcMsgs[0].value.amount, 10) -
Math.floor(parseInt(fee.amount[0].amount, 10) * GAS_MULTIPLIER);

if (amount <= 0) {
throw new Error('noble balance does not cover fees');
}

ibcMsgs[0].value.amount = amount.toString();

const tx = await client.send(ibcMsgs);

return encodeJson(tx);
} catch (error) {
return wrappedError(error)
}
}

export async function getWithdrawalCapacityByDenom(payload: string): Promise<string> {
try {
const client = globalThis.client;
Expand Down
16 changes: 4 additions & 12 deletions v4-client-py-v2/dydx_v4_client/indexer/rest/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,10 @@
DELAYMSG_MODULE_ADDRESS = "dydx1mkkvp26dngu6n8rmalaxyp3gwkjuzztq5zx6tr"


# Market Statistic Day Types
class MarketStatisticDay:
ONE = "1"
SEVEN = "7"
THIRTY = "30"
# Order Side
class OrderSide:
BUY = "BUY"
SELL = "SELL"


# Order Types
Expand All @@ -20,20 +19,13 @@ class OrderType:
TAKE_PROFIT_MARKET = "TAKE_PROFIT_MARKET"


# Order Side
class OrderSide:
BUY = "BUY"
SELL = "SELL"


# Order TimeInForce
class OrderTimeInForce:
GTT = "TIME_IN_FORCE_UNSPECIFIED"
IOC = "TIME_IN_FORCE_IOC"
FOK = "TIME_IN_FORCE_FILL_OR_KILL"


# Order Execution
class OrderExecution:
DEFAULT = "DEFAULT"
IOC = "IOC"
Expand Down
4 changes: 2 additions & 2 deletions v4-client-py-v2/dydx_v4_client/indexer/socket/websocket.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,10 @@ class Candles(Channel):
channel = "v4_candles"

def subscribe(self, id, resolution: CandlesResolution, batched=True) -> Self:
return super().subscribe(id=f"{id}/{resolution}", batched=batched)
return super().subscribe(id=f"{id}/{resolution.value}", batched=batched)

def unsubscribe(self, id, resolution: CandlesResolution):
return super().unsubscribe(id=f"{id}/{resolution}")
return super().unsubscribe(id=f"{id}/{resolution.value}")


class Subaccounts(Channel):
Expand Down
64 changes: 64 additions & 0 deletions v4-client-py-v2/dydx_v4_client/node/chain_helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
from dydx_v4_client.indexer.rest.constants import (
OrderType,
OrderExecution,
OrderTimeInForce,
)
from v4_proto.dydxprotocol.clob.order_pb2 import Order


class OrderHelper:
@staticmethod
def calculate_time_in_force(
order_type: OrderType,
time_in_force: Order.TimeInForce,
post_only: bool = False,
execution: OrderExecution = OrderExecution.DEFAULT,
) -> Order.TimeInForce:
if order_type == OrderType.MARKET:
return Order.TimeInForce.TIME_IN_FORCE_IOC
elif order_type == OrderType.LIMIT:
if post_only:
return Order.TimeInForce.TIME_IN_FORCE_POST_ONLY
else:
return time_in_force
elif order_type in [OrderType.STOP_LIMIT, OrderType.TAKE_PROFIT_LIMIT]:
if execution == OrderExecution.DEFAULT:
return Order.TimeInForce.TIME_IN_FORCE_UNSPECIFIED
elif execution == OrderExecution.POST_ONLY:
return Order.TimeInForce.TIME_IN_FORCE_POST_ONLY
elif execution == OrderExecution.FOK:
return Order.TimeInForce.TIME_IN_FORCE_FILL_OR_KILL
elif execution == OrderExecution.IOC:
return Order.TimeInForce.TIME_IN_FORCE_IOC
elif order_type in [OrderType.STOP_MARKET, OrderType.TAKE_PROFIT_MARKET]:
if execution in [OrderExecution.DEFAULT, OrderExecution.POST_ONLY]:
raise ValueError(
f"Execution value {execution.value} not supported for {order_type.value}"
)
elif execution == OrderExecution.FOK:
return Order.TimeInForce.TIME_IN_FORCE_FILL_OR_KILL
elif execution == OrderExecution.IOC:
return Order.TimeInForce.TIME_IN_FORCE_IOC
raise ValueError(
"Invalid combination of order type, time in force, and execution"
)

@staticmethod
def calculate_client_metadata(order_type: OrderType) -> int:
return (
1
if order_type
in [OrderType.MARKET, OrderType.STOP_MARKET, OrderType.TAKE_PROFIT_MARKET]
else 0
)

@staticmethod
def calculate_condition_type(order_type: OrderType) -> Order.ConditionType:
if order_type in [OrderType.LIMIT, OrderType.MARKET]:
return Order.ConditionType.CONDITION_TYPE_UNSPECIFIED
elif order_type in [OrderType.STOP_LIMIT, OrderType.STOP_MARKET]:
return Order.ConditionType.CONDITION_TYPE_STOP_LOSS
elif order_type in [OrderType.TAKE_PROFIT_LIMIT, OrderType.TAKE_PROFIT_MARKET]:
return Order.ConditionType.CONDITION_TYPE_TAKE_PROFIT
else:
raise ValueError("Invalid order type")
37 changes: 23 additions & 14 deletions v4-client-py-v2/dydx_v4_client/node/market.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

from v4_proto.dydxprotocol.clob.order_pb2 import Order, OrderId

from dydx_v4_client.indexer.rest.constants import OrderType, OrderExecution
from dydx_v4_client.node.chain_helpers import OrderHelper
from dydx_v4_client.node.message import order, order_id


Expand Down Expand Up @@ -51,28 +53,35 @@ def order_id(

def order(
self,
order_id: Order,
order_id: OrderId,
order_type: OrderType,
side: Order.Side,
size: float,
price: int,
time_in_force: Order.TimeInForce,
reduce_only: bool,
post_only: bool = False,
good_til_block: int = None,
good_til_block_time: int = None,
client_metadata: int = 0,
condition_type: Order.ConditionType = Order.ConditionType.CONDITION_TYPE_UNSPECIFIED,
execution: OrderExecution = OrderExecution.DEFAULT,
conditional_order_trigger_subticks: int = 0,
) -> Order:
order_time_in_force = OrderHelper.calculate_time_in_force(
order_type, time_in_force, post_only, execution
)
client_metadata = OrderHelper.calculate_client_metadata(order_type)
condition_type = OrderHelper.calculate_condition_type(order_type)

return order(
order_id,
side,
self.calculate_quantums(size),
self.calculate_subticks(price),
time_in_force,
reduce_only,
good_til_block,
good_til_block_time,
client_metadata,
condition_type,
conditional_order_trigger_subticks,
order_id=order_id,
side=side,
quantums=self.calculate_quantums(size),
subticks=self.calculate_subticks(price),
time_in_force=order_time_in_force,
reduce_only=reduce_only,
good_til_block=good_til_block,
good_til_block_time=good_til_block_time,
client_metadata=client_metadata,
condition_type=condition_type,
conditional_order_trigger_subticks=conditional_order_trigger_subticks,
)
2 changes: 1 addition & 1 deletion v4-client-py-v2/dydx_v4_client/node/message.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ def place_order(order: Order):


def cancel_order(
order_id,
order_id: OrderId,
good_til_block: int = None,
good_til_block_time: int = None,
):
Expand Down
51 changes: 51 additions & 0 deletions v4-client-py-v2/examples/market_order_example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import asyncio
import random

from dydx_v4_client import MAX_CLIENT_ID, NodeClient, OrderFlags, Wallet
from v4_proto.dydxprotocol.clob.order_pb2 import Order

from dydx_v4_client.indexer.rest.constants import OrderType
from dydx_v4_client.indexer.rest.indexer_client import IndexerClient
from dydx_v4_client.network import TESTNET
from dydx_v4_client.node.market import Market
from tests.conftest import DYDX_TEST_MNEMONIC, TEST_ADDRESS

MARKET_ID = "ETH-USD"


async def place_market_order(size: float):
node = await NodeClient.connect(TESTNET.node)
indexer = IndexerClient(TESTNET.rest_indexer)

market = Market(
(await indexer.markets.get_perpetual_markets(MARKET_ID))["markets"][MARKET_ID]
)
wallet = await Wallet.from_mnemonic(node, DYDX_TEST_MNEMONIC, TEST_ADDRESS)

order_id = market.order_id(
TEST_ADDRESS, 0, random.randint(0, MAX_CLIENT_ID), OrderFlags.SHORT_TERM
)

current_block = await node.latest_block_height()

new_order = market.order(
order_id=order_id,
order_type=OrderType.MARKET,
side=Order.Side.SIDE_SELL,
size=size,
price=0, # Set to 0 for market orders
time_in_force=Order.TimeInForce.TIME_IN_FORCE_UNSPECIFIED,
reduce_only=False,
good_til_block=current_block + 10,
)

transaction = await node.place_order(
wallet=wallet,
order=new_order,
)

print(transaction)
wallet.sequence += 1


asyncio.run(place_market_order(0.00001))
4 changes: 3 additions & 1 deletion v4-client-py-v2/examples/short_term_order_cancel_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import time

from dydx_v4_client import MAX_CLIENT_ID, NodeClient, Order, OrderFlags, Wallet
from dydx_v4_client.indexer.rest.constants import OrderType
from dydx_v4_client.indexer.rest.indexer_client import IndexerClient
from dydx_v4_client.network import TESTNET
from dydx_v4_client.node.market import Market
Expand Down Expand Up @@ -32,10 +33,11 @@ async def test():
wallet,
market.order(
order_id,
OrderType.LIMIT,
Order.Side.SIDE_SELL,
size=0.01,
price=40000,
time_in_force=Order.TIME_IN_FORCE_UNSPECIFIED,
time_in_force=Order.TimeInForce.TIME_IN_FORCE_UNSPECIFIED,
reduce_only=False,
good_til_block=good_til_block,
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from pathlib import Path

from dydx_v4_client import MAX_CLIENT_ID, NodeClient, Order, OrderFlags, Wallet
from dydx_v4_client.indexer.rest.constants import OrderType
from dydx_v4_client.indexer.rest.indexer_client import IndexerClient
from dydx_v4_client.network import TESTNET
from dydx_v4_client.node.market import Market
Expand Down Expand Up @@ -48,6 +49,7 @@ async def test():
wallet,
market.order(
order_id,
OrderType.LIMIT,
to_order_side[order["side"]],
size=0.01,
price=order.get("price", 1350),
Expand Down
2 changes: 1 addition & 1 deletion v4-client-py-v2/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "dydx-v4-client"
version = "0.1.0"
version = "0.1.1"
description = ""
authors = ["Piotr Piwoński <piwonskp@gmail.com>"]
readme = "README.md"
Expand Down
2 changes: 1 addition & 1 deletion v4-client-py-v2/tests/test_mutating_node_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ async def test_order(
# codespace: "sdk"\n code: 32\n raw_log: "account sequence mismatch, expected 1460, got 1459: incorrect account sequence"
# If the time is too long the result is:
# codespace: "clob"\n code:...hj67cghhf9jypslcf9sh2n5k6art Number:0} ClientId:13850897 OrderFlags:64 ClobPairId:0}: Stateful order does not exist"
time.sleep(2)
time.sleep(1.5)

wallet = await get_wallet(node_client, private_key, test_address)

Expand Down

0 comments on commit 6752bd8

Please sign in to comment.