-
Notifications
You must be signed in to change notification settings - Fork 58
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
[TS] [CLOB-895] short term place orders for TS #39
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,9 @@ | ||
import { Order_TimeInForce } from '@dydxprotocol/v4-proto/src/codegen/dydxprotocol/clob/order'; | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead of modifying composite_examples.ts, can you copy it to a separate composite_short_term_examples.ts with the new code? We still need this composite_examples.ts to test regular placeOrder for FE. |
||
import { BECH32_PREFIX } from '../src'; | ||
import { CompositeClient } from '../src/clients/composite-client'; | ||
import { | ||
Network, OrderExecution, OrderSide, OrderTimeInForce, OrderType, | ||
Network, OrderSide, | ||
} from '../src/clients/constants'; | ||
import LocalWallet from '../src/clients/modules/local-wallet'; | ||
import { Subaccount } from '../src/clients/subaccount'; | ||
|
@@ -23,25 +25,24 @@ async function test(): Promise<void> { | |
const subaccount = new Subaccount(wallet, 0); | ||
for (const orderParams of ordersParams) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ordersParams come from |
||
try { | ||
const type = OrderType[orderParams.type as keyof typeof OrderType]; | ||
const side = OrderSide[orderParams.side as keyof typeof OrderSide]; | ||
const timeInForceString = orderParams.timeInForce ?? 'GTT'; | ||
const timeInForce = OrderTimeInForce[timeInForceString as keyof typeof OrderTimeInForce]; | ||
const price = orderParams.price ?? 1350; | ||
const timeInForceSeconds = (timeInForce === OrderTimeInForce.GTT) ? 60 : 0; | ||
const postOnly = orderParams.postOnly ?? false; | ||
const tx = await client.placeOrder( | ||
|
||
const currentBlock = await client.validatorClient.get.latestBlockHeight(); | ||
const nextValidBlockHeight = currentBlock + 1; | ||
// Note, you can change this to any number between `next_valid_block_height` | ||
// to `next_valid_block_height + SHORT_BLOCK_WINDOW` | ||
const goodTilBlock = nextValidBlockHeight + 3; | ||
|
||
const tx = await client.placeShortTermOrder( | ||
subaccount, | ||
'ETH-USD', | ||
type, | ||
side, | ||
price, | ||
0.01, | ||
randomInt(100_000_000), | ||
timeInForce, | ||
timeInForceSeconds, | ||
OrderExecution.DEFAULT, | ||
postOnly, | ||
Order_TimeInForce.TIME_IN_FORCE_UNSPECIFIED, | ||
goodTilBlock, | ||
false, | ||
); | ||
console.log('**Order Tx**'); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,15 @@ | ||
import { EncodeObject } from '@cosmjs/proto-signing'; | ||
import { GasPrice, IndexedTx, StdFee } from '@cosmjs/stargate'; | ||
import { BroadcastTxAsyncResponse, BroadcastTxSyncResponse } from '@cosmjs/tendermint-rpc/build/tendermint37'; | ||
import { Order_ConditionType, Order_TimeInForce } from '@dydxprotocol/v4-proto/src/codegen/dydxprotocol/clob/order'; | ||
import Long from 'long'; | ||
import protobuf from 'protobufjs'; | ||
|
||
import { OrderFlags } from '../types'; | ||
import { | ||
DYDX_DENOM, | ||
GAS_PRICE, | ||
Network, OrderExecution, OrderSide, OrderTimeInForce, OrderType, | ||
Network, OrderExecution, OrderSide, OrderTimeInForce, OrderType, SHORT_BLOCK_WINDOW, | ||
} from './constants'; | ||
import { | ||
calculateQuantums, | ||
|
@@ -21,6 +22,7 @@ import { | |
calculateConditionalOrderTriggerSubticks, | ||
} from './helpers/chain-helpers'; | ||
import { IndexerClient } from './indexer-client'; | ||
import { UserError } from './lib/errors'; | ||
import LocalWallet from './modules/local-wallet'; | ||
import { Subaccount } from './subaccount'; | ||
import { ValidatorClient } from './validator-client'; | ||
|
@@ -157,6 +159,27 @@ export class CompositeClient { | |
return height + 3; | ||
} | ||
|
||
/** | ||
* @description Calculate the goodTilBlock value for a SHORT_TERM order | ||
* | ||
* @param goodTilBlock Number of blocks from the current block height the order will | ||
* be valid for. | ||
* | ||
* @throws UnexpectedClientError if a malformed response is returned with no GRPC error | ||
* at any point. | ||
*/ | ||
private async validateGoodTilBlock(goodTilBlock: number): Promise<void> { | ||
const height = await this.validatorClient.get.latestBlockHeight(); | ||
const nextValidBlockHeight = height + 1; | ||
const lowerBound = nextValidBlockHeight; | ||
const upperBound = nextValidBlockHeight + SHORT_BLOCK_WINDOW; | ||
if (goodTilBlock < lowerBound || goodTilBlock > upperBound) { | ||
throw new UserError(`Invalid Short-Term order GoodTilBlock. | ||
Should be greater-than-or-equal-to ${lowerBound} and less-than-or-equal-to ${upperBound}. | ||
Provided good til block: ${goodTilBlock}`); | ||
} | ||
} | ||
|
||
/** | ||
* @description Calculate the goodTilBlockTime value for a LONG_TERM order | ||
* the calling function is responsible for creating the messages. | ||
|
@@ -175,6 +198,60 @@ export class CompositeClient { | |
return Math.round(future.getTime() / 1000); | ||
} | ||
|
||
/** | ||
* @description Place a short term order with human readable input. | ||
* | ||
* Use human readable form of input, including price and size | ||
* The quantum and subticks are calculated and submitted | ||
* | ||
* @param subaccount The subaccount to place the order under | ||
* @param marketId The market to place the order on | ||
* @param side The side of the order to place | ||
* @param price The price of the order to place | ||
* @param size The size of the order to place | ||
* @param clientId The client id of the order to place | ||
* @param timeInForce The time in force of the order to place | ||
* @param goodTilBlock The goodTilBlock of the order to place | ||
* @param reduceOnly The reduceOnly of the order to place | ||
* | ||
* | ||
* @throws UnexpectedClientError if a malformed response is returned with no GRPC error | ||
* at any point. | ||
* @returns The transaction hash. | ||
*/ | ||
async placeShortTermOrder( | ||
subaccount: Subaccount, | ||
marketId: string, | ||
side: OrderSide, | ||
price: number, | ||
size: number, | ||
clientId: number, | ||
goodTilBlock: number, | ||
timeInForce: Order_TimeInForce, | ||
reduceOnly: boolean, | ||
): Promise<BroadcastTxAsyncResponse | BroadcastTxSyncResponse | IndexedTx> { | ||
const msgs: Promise<EncodeObject[]> = new Promise((resolve) => { | ||
const msg = this.placeShortTermOrderMessage( | ||
subaccount, | ||
marketId, | ||
side, | ||
price, | ||
size, | ||
clientId, | ||
timeInForce, | ||
goodTilBlock, | ||
reduceOnly, | ||
); | ||
msg.then((it) => resolve([it])).catch((err) => { | ||
console.log(err); | ||
}); | ||
}); | ||
return this.send( | ||
subaccount.wallet, | ||
() => msgs, | ||
true); | ||
} | ||
|
||
/** | ||
* @description Place an order with human readable input. | ||
* | ||
|
@@ -341,6 +418,79 @@ export class CompositeClient { | |
); | ||
} | ||
|
||
/** | ||
* @description Calculate and create the short term place order message | ||
* | ||
* Use human readable form of input, including price and size | ||
* The quantum and subticks are calculated and submitted | ||
* | ||
* @param subaccount The subaccount to place the order under | ||
* @param marketId The market to place the order on | ||
* @param side The side of the order to place | ||
* @param price The price of the order to place | ||
* @param size The size of the order to place | ||
* @param clientId The client id of the order to place | ||
* @param timeInForce The time in force of the order to place | ||
* @param goodTilBlock The goodTilBlock of the order to place | ||
* @param reduceOnly The reduceOnly of the order to place | ||
* | ||
* | ||
* @throws UnexpectedClientError if a malformed response is returned with no GRPC error | ||
* at any point. | ||
* @returns The message to be passed into the protocol | ||
*/ | ||
private async placeShortTermOrderMessage( | ||
subaccount: Subaccount, | ||
marketId: string, | ||
side: OrderSide, | ||
price: number, | ||
size: number, | ||
clientId: number, | ||
goodTilBlock: number, | ||
timeInForce: Order_TimeInForce, | ||
reduceOnly: boolean, | ||
): Promise<EncodeObject> { | ||
await this.validateGoodTilBlock(goodTilBlock); | ||
|
||
const marketsResponse = await this.indexerClient.markets.getPerpetualMarkets(marketId); | ||
const market = marketsResponse.markets[marketId]; | ||
const clobPairId = market.clobPairId; | ||
const atomicResolution = market.atomicResolution; | ||
const stepBaseQuantums = market.stepBaseQuantums; | ||
const quantumConversionExponent = market.quantumConversionExponent; | ||
const subticksPerTick = market.subticksPerTick; | ||
const orderSide = calculateSide(side); | ||
const quantums = calculateQuantums( | ||
size, | ||
atomicResolution, | ||
stepBaseQuantums, | ||
); | ||
const subticks = calculateSubticks( | ||
price, | ||
atomicResolution, | ||
quantumConversionExponent, | ||
subticksPerTick, | ||
); | ||
const orderFlags = OrderFlags.SHORT_TERM; | ||
return this.validatorClient.post.composer.composeMsgPlaceOrder( | ||
subaccount.address, | ||
subaccount.subaccountNumber, | ||
clientId, | ||
clobPairId, | ||
orderFlags, | ||
goodTilBlock, | ||
0, // Short term orders use goodTilBlock. | ||
orderSide, | ||
quantums, | ||
subticks, | ||
timeInForce, | ||
reduceOnly, | ||
0, // Client metadata is 0 for short term orders. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note: client metadata can be non-zero for short-term and stateful orders |
||
Order_ConditionType.CONDITION_TYPE_UNSPECIFIED, // Short term orders cannot be conditional. | ||
Long.fromInt(0), // Short term orders cannot be conditional. | ||
); | ||
} | ||
|
||
/** | ||
* @description Cancel an order with human readable input. | ||
* | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this the correct
Order_TIF
we want to be using as a a parameter? Note that I took out everything related to OrderExecution tooThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a translation layer in example to translate
orderExecution
string to Order_TimeInForce