Skip to content

Commit

Permalink
Merge pull request #58 from Polymarket/feat/market-orders
Browse files Browse the repository at this point in the history
Feat/market orders
  • Loading branch information
poly-rodr authored Nov 28, 2022
2 parents 9efe7fc + 49d9b23 commit 1ca193e
Show file tree
Hide file tree
Showing 10 changed files with 953 additions and 172 deletions.
37 changes: 37 additions & 0 deletions examples/marketOrder.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { ethers } from "ethers";
import { config as dotenvConfig } from "dotenv";
import { resolve } from "path";
import { ApiKeyCreds, Chain, ClobClient, OrderType } from "../src";

dotenvConfig({ path: resolve(__dirname, "../.env") });

async function main() {
const wallet = new ethers.Wallet(`${process.env.PK}`);
const chainId = parseInt(`${process.env.CHAIN_ID || Chain.MUMBAI}`) as Chain;
console.log(`Address: ${await wallet.getAddress()}, chainId: ${chainId}`);

const host = process.env.CLOB_API_URL || "http://localhost:8080";
const creds: ApiKeyCreds = {
key: `${process.env.CLOB_API_KEY}`,
secret: `${process.env.CLOB_SECRET}`,
passphrase: `${process.env.CLOB_PASS_PHRASE}`,
};
const clobClient = new ClobClient(host, chainId, wallet, creds);

// Create a YES market buy order for the equivalent of 100 USDC for the market price
const YES = "1343197538147866997676250008839231694243646439454152539053893078719042421992";
const marketOrder = await clobClient.createMarketBuyOrder({
tokenID: YES,
amount: 100,
feeRateBps: 0,
nonce: 0,
price: 0.5,
});
console.log("Created Market Order", marketOrder);

// Send it to the server
const resp = await clobClient.postOrder(marketOrder, OrderType.FOK);
console.log(resp);
}

main();
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@polymarket/clob-client",
"description": "Typescript client for Polymarket's CLOB",
"version": "1.1.7",
"version": "1.1.8",
"contributors": [
{
"name": "Jonathan Amenechi",
Expand Down
23 changes: 21 additions & 2 deletions src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,11 @@ import {
OptionalParams,
Order,
OrderPayload,
OrderType,
Side,
Trade,
TradeParams,
UserMarketOrder,
UserOrder,
} from "./types";
import { createL1Headers, createL2Headers } from "./headers";
Expand Down Expand Up @@ -260,6 +263,18 @@ export class ClobClient {
return this.orderBuilder.buildOrder(userOrder);
}

public async createMarketBuyOrder(userMarketOrder: UserMarketOrder): Promise<SignedOrder> {
this.canL1Auth();

if (!userMarketOrder.price) {
const { tokenID } = userMarketOrder;
const marketPrice = await this.getPrice(tokenID, Side.BUY);

userMarketOrder.price = parseFloat(marketPrice);
}
return this.orderBuilder.buildMarketOrder(userMarketOrder);
}

public async getOpenOrders(params?: OpenOrderParams): Promise<OpenOrdersResponse> {
this.canL2Auth();
const endpoint = GET_OPEN_ORDERS;
Expand All @@ -278,10 +293,14 @@ export class ClobClient {
return get(url, headers);
}

public async postOrder(order: SignedOrder, optionalParams?: OptionalParams): Promise<any> {
public async postOrder(
order: SignedOrder,
orderType: OrderType = OrderType.GTC,
optionalParams?: OptionalParams,
): Promise<any> {
this.canL2Auth();
const endpoint = POST_ORDER;
const orderPayload = orderToJson(order);
const orderPayload = orderToJson(order, this.creds?.key || "", orderType);

const l2HeaderArgs = {
method: POST,
Expand Down
9 changes: 6 additions & 3 deletions src/http-helpers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,15 @@ export const request = async (
return response;
} catch (err) {
if (axios.isAxiosError(err)) {
console.error(err.response?.status);
console.error(err.response?.statusText);
console.error(err.response?.data);
console.error("request error", {
status: err.response?.status,
statusText: err.response?.statusText,
data: err.response?.data,
});
return { error: err.response?.data };
}
console.error(err);

return { error: err };
}
};
Expand Down
17 changes: 15 additions & 2 deletions src/order-builder/builder.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Wallet } from "@ethersproject/wallet";
import { JsonRpcSigner } from "@ethersproject/providers";
import { SignedOrder, SignatureType } from "@polymarket/order-utils";
import { createOrder } from "./helpers";
import { Chain, UserOrder } from "../types";
import { createMarketBuyOrder, createOrder } from "./helpers";
import { Chain, UserMarketOrder, UserOrder } from "../types";

export class OrderBuilder {
readonly signer: Wallet | JsonRpcSigner;
Expand Down Expand Up @@ -41,4 +41,17 @@ export class OrderBuilder {
userOrder,
);
}

/**
* Generate and sign a market order
*/
public async buildMarketOrder(userMarketOrder: UserMarketOrder): Promise<SignedOrder> {
return createMarketBuyOrder(
this.signer,
this.chainId,
this.signatureType,
this.funderAddress,
userMarketOrder,
);
}
}
126 changes: 100 additions & 26 deletions src/order-builder/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,28 @@ import {
COLLATERAL_TOKEN_DECIMALS,
CONDITIONAL_TOKEN_DECIMALS,
} from "@polymarket/order-utils";
import { UserOrder, Side, Chain } from "../types";
import { UserOrder, Side, Chain, UserMarketOrder } from "../types";
import { roundDown } from "../utilities";

/**
* Generate and sign a order
*
* @param signer
* @param contractAddress ctf exchange contract address
* @param chainId
* @param OrderData
* @returns SignedOrder
*/
export const buildOrder = async (
signer: Wallet | JsonRpcSigner,
contractAddress: string,
chainId: number,
orderData: OrderData,
): Promise<SignedOrder> => {
const cTFExchangeOrderBuilder = new ExchangeOrderBuilder(contractAddress, chainId, signer);
return cTFExchangeOrderBuilder.buildSignedOrder(orderData);
};

/**
* Translate simple user order to args used to generate Orders
*/
Expand All @@ -32,18 +51,18 @@ export const buildOrderCreationArgs = async (
side = UtilsSide.BUY;

// force 2 decimals places
const rawTakerAmt = roundDown(userOrder.size, 2);
const rawPrice = roundDown(userOrder.price, 2);
const rawMakerAmt = roundDown(rawTakerAmt * rawPrice, 4);
const rawTakerAmt = roundDown(userOrder.size, 4);
const rawPrice = roundDown(userOrder.price, 4);
const rawMakerAmt = roundDown(rawTakerAmt * rawPrice, 6);

makerAmount = parseUnits(rawMakerAmt.toString(), COLLATERAL_TOKEN_DECIMALS).toString();
takerAmount = parseUnits(rawTakerAmt.toString(), CONDITIONAL_TOKEN_DECIMALS).toString();
} else {
side = UtilsSide.SELL;

const rawMakerAmt = roundDown(userOrder.size, 2);
const rawPrice = roundDown(userOrder.price, 2);
const rawTakerAmt = roundDown(rawPrice * rawMakerAmt, 4);
const rawMakerAmt = roundDown(userOrder.size, 4);
const rawPrice = roundDown(userOrder.price, 4);
const rawTakerAmt = roundDown(rawPrice * rawMakerAmt, 6);

makerAmount = parseUnits(rawMakerAmt.toString(), CONDITIONAL_TOKEN_DECIMALS).toString();
takerAmount = parseUnits(rawTakerAmt.toString(), COLLATERAL_TOKEN_DECIMALS).toString();
Expand Down Expand Up @@ -85,44 +104,99 @@ export const buildOrderCreationArgs = async (
} as OrderData;
};

export const createOrder = async (
eoaSigner: Wallet | JsonRpcSigner,
chainId: Chain,
signatureType: SignatureType,
funderAddress: string | undefined,
userOrder: UserOrder,
): Promise<SignedOrder> => {
const eoaSignerAddress = await eoaSigner.getAddress();

// If funder address is not given, use the signer address
const maker = funderAddress === undefined ? eoaSignerAddress : funderAddress;
const clobContracts = getContracts(chainId);

const orderData = await buildOrderCreationArgs(
eoaSignerAddress,
maker,
signatureType,
userOrder,
);
return buildOrder(eoaSigner, clobContracts.Exchange, chainId, orderData);
};

/**
* Generate and sign a order
*
* @param signer
* @param contractAddress ctf exchange contract address
* @param chainId
* @param OrderData
* @returns SignedOrder
* Translate simple user market order to args used to generate Orders
*/
export const buildOrder = async (
signer: Wallet | JsonRpcSigner,
contractAddress: string,
chainId: number,
orderData: OrderData,
): Promise<SignedOrder> => {
const cTFExchangeOrderBuilder = new ExchangeOrderBuilder(contractAddress, chainId, signer);
export const buildMarketBuyOrderCreationArgs = async (
signer: string,
maker: string,
signatureType: SignatureType,
userMarketOrder: UserMarketOrder,
): Promise<OrderData> => {
// force 2 decimals places
const rawMakerAmt = roundDown(userMarketOrder.amount, 4);
const rawPrice = roundDown(userMarketOrder.price || 1, 4);
const rawTakerAmt = roundDown(rawMakerAmt / rawPrice, 6);

return cTFExchangeOrderBuilder.buildSignedOrder(orderData);
const makerAmount = parseUnits(rawMakerAmt.toString(), COLLATERAL_TOKEN_DECIMALS).toString();
const takerAmount = parseUnits(rawTakerAmt.toString(), CONDITIONAL_TOKEN_DECIMALS).toString();

let taker;
if (typeof userMarketOrder.taker !== "undefined" && userMarketOrder.taker) {
taker = userMarketOrder.taker;
} else {
taker = "0x0000000000000000000000000000000000000000";
}

let feeRateBps;
if (typeof userMarketOrder.feeRateBps !== "undefined" && userMarketOrder.feeRateBps) {
feeRateBps = userMarketOrder.feeRateBps.toString();
} else {
feeRateBps = "0";
}

let nonce;
if (typeof userMarketOrder.nonce !== "undefined" && userMarketOrder.nonce) {
nonce = userMarketOrder.nonce.toString();
} else {
nonce = "0";
}

return {
maker,
taker,
tokenId: userMarketOrder.tokenID,
makerAmount,
takerAmount,
side: UtilsSide.BUY,
feeRateBps,
nonce,
signer,
expiration: (userMarketOrder.expiration || 0).toString(),
signatureType,
} as OrderData;
};

export const createOrder = async (
export const createMarketBuyOrder = async (
eoaSigner: Wallet | JsonRpcSigner,
chainId: Chain,
signatureType: SignatureType,
funderAddress: string | undefined,
userOrder: UserOrder,
userMarketOrder: UserMarketOrder,
): Promise<SignedOrder> => {
const eoaSignerAddress = await eoaSigner.getAddress();

// If funder address is not given, use the signer address
const maker = funderAddress === undefined ? eoaSignerAddress : funderAddress;
const clobContracts = getContracts(chainId);

const orderData = await buildOrderCreationArgs(
const orderData = await buildMarketBuyOrderCreationArgs(
eoaSignerAddress,
maker,
signatureType,
userOrder,
userMarketOrder,
);
return buildOrder(eoaSigner, clobContracts.Exchange, chainId, orderData);
};
Loading

0 comments on commit 1ca193e

Please sign in to comment.