Market buy order
To perform a market buy. We useKuruSdk.IOC.placeMarket() Set isBuy to false to place a Market sell order.
marketBuy
Copy
Ask AI
// Example: place a market buy (IOC) using the Kuru SDK
// Fill placeholders (rpcUrl, marketAddress, size) and export PRIVATE_KEY before running.
import { ethers } from "ethers";
import * as KuruSdk from "@kuru-labs/kuru-sdk";
// Network RPC endpoint
const rpcUrl = ""; // TODO: e.g., http://localhost:8545
// Address of the market you want to trade on
const marketAddress = ""; // TODO: e.g., 0x...
// Order params (strings OK; SDK handles parsing):
// size → amount in quote asset (e.g., USDC) to spend
const size = ""; // TODO: e.g., "5.5" (USDC)
// Slippage tolerance expressed in basis points (100 = 1%). This is only used to
// derive the on-chain minAmountOut threshold; the contract enforces minAmountOut,
// not the slippage percentage itself.
const slippageToleranceBps = 100;
async function main() {
const provider = new ethers.providers.JsonRpcProvider(rpcUrl);
const privateKey = process.env.PRIVATE_KEY; // must be set in env
if (!privateKey) throw new Error("PRIVATE_KEY environment variable not set");
const signer = new ethers.Wallet(privateKey, provider);
try {
// Pull market parameters (precisions, fees, etc.)
const marketParams = await KuruSdk.ParamFetcher.getMarketParams(provider, marketAddress);
const quoteSize = parseFloat(size);
if (Number.isNaN(quoteSize)) {
throw new Error("size must be a numeric string (e.g., \"5.5\")");
}
// Estimate base asset output and gas using CostEstimator
const { output: estimatedBaseOut, estimatedGas } = await KuruSdk.CostEstimator.estimateMarketBuy(
provider,
marketAddress,
marketParams,
quoteSize
);
console.log("Estimated gas:", estimatedGas.toString());
console.log("Estimated base out:", estimatedBaseOut);
const baseDecimals = marketParams.baseAssetDecimals.toNumber();
const slippageMultiplier = (10000 - slippageToleranceBps) / 10000;
const minAmountOut = (estimatedBaseOut * slippageMultiplier).toFixed(baseDecimals);
console.log("Min amount out (post slippage):", minAmountOut);
// Immediate-Or-Cancel market buy
const receipt = await KuruSdk.IOC.placeMarket(signer, marketAddress, marketParams, {
approveTokens: true,
size,
isBuy: true,
minAmountOut,
isMargin: false,
fillOrKill: true,
});
console.log("Transaction hash:", receipt.transactionHash);
} catch (error) {
console.error("Error placing market buy order:", error);
}
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
Estimate buy
To estimate thebaseAsset received for a buy order with X amount of quoteAsset. We use KuruSdk.CostEstimator.estimateMarketBuy()
EstimateBuy
Copy
Ask AI
import { ethers } from "ethers";
import * as KuruSdk from "@kuru-labs/kuru-sdk";
// Example: estimate how much base you receive for a market buy (no tx sent)
// Fill placeholders (rpcUrl, marketAddress, amount) before running.
const rpcUrl = ""; // TODO: RPC endpoint (e.g., http://localhost:8545)
const marketAddress = ""; // TODO: Market address (e.g., 0x...)
const amount = ""; // TODO: Quote amount to spend (e.g., "100" USDC)
async function main() {
const provider = new ethers.providers.JsonRpcProvider(rpcUrl);
// Fetch market params (precisions, fees, etc.) required for estimation
const marketParams = await KuruSdk.ParamFetcher.getMarketParams(provider, marketAddress);
try {
// Validate and convert input amount (quote asset) to number
const amountNum = Number(amount);
if (Number.isNaN(amountNum)) {
throw new Error("Provide a numeric 'amount' (quote asset), e.g., \"100\"");
}
const estimate = await KuruSdk.CostEstimator.estimateMarketBuy(
provider,
marketAddress,
marketParams,
amountNum
);
console.log(estimate);
} catch (error) {
console.error("Error estimating market buy:", error);
}
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
Estimate base for sell
To estimate the requiredbaseAsset for a sell order to get X amount of quoteAsset. We use KuruSdk.CostEstimator.estimateRequiredBaseForSell()
EstimateSell
Copy
Ask AI
import { ethers } from "ethers";
import * as KuruSdk from "@kuru-labs/kuru-sdk";
import orderbookAbi from "@kuru-labs/kuru-sdk/abi/OrderBook.json";
// Example: estimate required BASE to sell a given QUOTE amount (no tx sent)
// Fill placeholders (rpcUrl, marketAddress, amount) before running.
const rpcUrl = ""; // TODO: RPC endpoint (e.g., http://localhost:8545)
const marketAddress = ""; // TODO: Market address (e.g., 0x...)
const amount = ""; // TODO: desired quote amount to receive (e.g., "100" USDC)
(async () => {
const provider = new ethers.providers.JsonRpcProvider(rpcUrl);
// Pull market params (precisions, fees, etc.)
const marketParams = await KuruSdk.ParamFetcher.getMarketParams(provider, marketAddress);
// Read orderbook state used by estimator
const orderbook = new ethers.Contract(marketAddress, orderbookAbi.abi, provider);
const l2Book = await orderbook.getL2Book();
const vaultParams = await orderbook.getVaultParams();
try {
// Validate numeric amount (quote)
const amountNum = Number(amount);
if (Number.isNaN(amountNum)) throw new Error("Provide numeric 'amount' (quote asset)");
const estimate = await KuruSdk.CostEstimator.estimateRequiredBaseForSell(
provider,
marketAddress,
marketParams,
amountNum,
l2Book,
vaultParams
);
console.log(estimate);
} catch (error) {
console.error("Error estimating required base for sell:", error);
}
})();
Deposit into margin account
MarginDeposit
Copy
Ask AI
// Example: deposit tokens into a margin account via Kuru SDK
// Fill placeholders (rpcUrl, marginAccountAddress, tokenAddress, amount, decimals)
// and export PRIVATE_KEY before running.
import { ethers } from "ethers";
import * as KuruSdk from "@kuru-labs/kuru-sdk";
import * as KuruConfig from "../config.json";
const {rpcUrl, marginAccountAddress} = KuruConfig;
const tokenAddress = ""; // TODO: ERC-20 token to deposit (use 0x000...000 for native if supported)
const amount = ""; // TODO: human amount as string (e.g., "1000")
const decimals = 18; // TODO: token decimals (e.g., 18 for ABC, 6 for USDC)
async function main() {
const provider = new ethers.providers.JsonRpcProvider(rpcUrl);
const privateKey = process.env.PRIVATE_KEY; // must be set in env
if (!privateKey) throw new Error("PRIVATE_KEY environment variable not set");
const signer = new ethers.Wallet(privateKey, provider);
try {
// Deposits on behalf of the signer address. Set a different receiver if desired
const receipt = await KuruSdk.MarginDeposit.deposit(
signer,
marginAccountAddress,
await signer.getAddress(),
tokenAddress,
amount,
decimals,
true // set to true to auto-approve ERC-20 if not already approved
);
console.log("Transaction hash:", receipt.transactionHash);
} catch (error) {
console.error("Error depositing:", error);
}
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
Place limit buy
To place a limit buy order. We useKuruSdk.GTC.placeLimit()
After sending, call parseEvents(receipt) to confirm the order was created and to read new order IDs.
Set isBuy to false to place a limit sell order.
parseEvents.ts
Copy
Ask AI
import { ethers, BigNumber } from 'ethers';
interface TradeInfo {
orderId: BigNumber;
filledSize: BigNumber;
price: BigNumber;
isBuy: boolean;
}
export async function parseEvents(receipt: ethers.ContractReceipt): Promise<{
newOrderIds: BigNumber[];
trades: TradeInfo[];
}> {
const newOrderIds: BigNumber[] = [];
const trades: TradeInfo[] = [];
receipt.logs.forEach((log) => {
if (log.topics[0] === ethers.utils.id("OrderCreated(uint40,address,uint96,uint32,bool)")) {
try {
const decodedLog = ethers.utils.defaultAbiCoder.decode(
['uint40', 'address', 'uint96', 'uint32', 'bool'],
log.data
);
const orderId = BigNumber.from(decodedLog[0]);
newOrderIds.push(orderId);
} catch (error) {
console.error("Error decoding OrderCreated event:", error);
}
} else if (log.topics[0] === ethers.utils.id("Trade(uint40,address,bool,uint256,uint96,address,address,uint96)")) {
try {
const decodedLog = ethers.utils.defaultAbiCoder.decode(
['uint40', 'address', 'bool', 'uint256', 'uint96', 'address', 'address', 'uint96'],
log.data
);
trades.push({
orderId: BigNumber.from(decodedLog[0]),
price: BigNumber.from(decodedLog[3]),
filledSize: BigNumber.from(decodedLog[7]),
isBuy: decodedLog[2]
});
} catch (error) {
console.error("Error decoding trade event:", error);
}
}
});
return { newOrderIds, trades };
}
placeLimitBuy.ts
Copy
Ask AI
// Example: place a limit buy order using the Kuru SDK
// Fill the placeholders (rpcUrl, marketAddress) and export PRIVATE_KEY before running.
import { ethers } from "ethers";
import * as KuruSdk from "@kuru-labs/kuru-sdk";
import { parseEvents } from "./ParseEvents";
// Address of the orderbook/market you want to trade on
const marketAddress = ""; // TODO: market address (e.g., 0x...)
// Network RPC endpoint to connect the signer/provider
const rpcUrl = ""; // TODO: RPC endpoint (e.g., http://localhost:8545)
// Order parameters (strings OK; SDK handles parsing/decimals):
// price → quoted in quote per base (e.g., USDC per ABC); must respect tick size
// size → amount of base to buy; must be ≥ min size
const price = ""; // TODO: e.g., "5" for 5 USDC per ABC
const size = ""; // TODO: e.g., "10" for 10 ABC
async function main() {
const provider = new ethers.providers.JsonRpcProvider(rpcUrl);
// PRIVATE_KEY should be exported in your shell before running
const privateKey = process.env.PRIVATE_KEY; // must be set in env
if (!privateKey) throw new Error("PRIVATE_KEY environment variable not set");
const signer = new ethers.Wallet(privateKey, provider);
// Fetch market parameters (precisions, tick size, fees)
const marketParams = await KuruSdk.ParamFetcher.getMarketParams(
provider,
marketAddress
);
const order = {
price,
size,
isBuy: true,
postOnly: true,
};
try {
const gasEstimate = await KuruSdk.GTC.estimateGas(
signer,
marketAddress,
marketParams,
order
);
const bufferedGasLimit = gasEstimate.mul(120).div(100);
console.log("Estimated gas:", gasEstimate.toString());
console.log("Buffered gas limit (120%):", bufferedGasLimit.toString());
// Place a Good-Till-Cancel limit buy with buffered gas limit
const receipt = await KuruSdk.GTC.placeLimit(
signer,
marketAddress,
marketParams,
{
...order,
txOptions: {
gasLimit: bufferedGasLimit,
},
}
);
console.log("Transaction hash:", receipt.transactionHash);
// Extract created order ids from events
const { newOrderIds } = await parseEvents(receipt);
console.log("OrderIds:", newOrderIds);
} catch (error) {
console.error("Error placing limit buy order:", error);
}
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
Cancel order
To cancel orders using the Kuru SDK. We useKuruSdk.OrderCanceler.cancelOrders()
cancelOrder
Copy
Ask AI
// Example: cancel one or more orders on an orderbook
// Fill placeholders (rpcUrl, marketAddress, orderIds) and export PRIVATE_KEY before running.
import { ethers, BigNumber } from "ethers";
import * as KuruSdk from "@kuru-labs/kuru-sdk";
// Network RPC endpoint
const rpcUrl = ""; // TODO: e.g., http://localhost:8545
// Address of the market where the orders exist
const marketAddress = ""; // TODO: market address (e.g., 0x...)
// Order IDs to cancel (as strings). These will be converted to BigNumber.
const orderIds = [""]; // TODO: e.g., ["123", "124"]
async function main() {
const provider = new ethers.providers.JsonRpcProvider(rpcUrl);
const privateKey = process.env.PRIVATE_KEY; // must be set in env
if (!privateKey) throw new Error("PRIVATE_KEY environment variable not set");
const signer = new ethers.Wallet(privateKey, provider);
const ids = orderIds.map((orderId) => BigNumber.from(orderId));
try {
const gasEstimate = await KuruSdk.OrderCanceler.estimateGas(
signer,
marketAddress,
ids
);
const bufferedGasLimit = gasEstimate.mul(120).div(100);
console.log("Estimated gas:", gasEstimate.toString());
console.log("Buffered gas limit (120%):", bufferedGasLimit.toString());
// Convert string IDs to BigNumber and send cancel transaction
const txReceipt = await KuruSdk.OrderCanceler.cancelOrders(
signer,
marketAddress,
ids,
{ gasLimit: bufferedGasLimit }
);
console.log("Transaction hash:", txReceipt.transactionHash);
} catch (err: any) {
console.error("Error:", err);
}
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
FlipOrders( Liquidity )
Flip orders place an order that, when filled, automatically creates an opposite-side order at a specified “flipped” price. They let you enter as a maker and immediately re-quote on the other side to maintain liquidity across both sides of the book. Using FlipOrders we could provide Liquidity in following ways.Spot
Even spread across a price band around best ask.placeSpotConcentratedLiquidity
Copy
Ask AI
import { ethers } from "ethers";
import * as KuruSdk from "@kuru-labs/kuru-sdk";
// Docs Template: Place concentrated liquidity around the current best ask
// 1) Fill all placeholders below
// 2) export PRIVATE_KEY=<your_key>
// 3) Run with: npx ts-node scripts/docs_scripts/placeConcentratedLiquidity.ts
const RPC_URL = ""; // TODO: RPC endpoint (e.g., https://rpc.your-node)
const MARKET_ADDRESS = ""; // TODO: Orderbook/market address
const MIN_FEES_BPS = 30n; // Minimum fees in bps (e.g., 30 for 0.3%)
const RANGE_PCT = 1n; // +/- range in percent around best ask (e.g., 1)
const TOTAL_QUOTE = ""; // TODO: Total quote liquidity to allocate (human string, e.g., "1000")
async function main() {
// Require configuration and PRIVATE_KEY in env
if (!RPC_URL || !MARKET_ADDRESS || !TOTAL_QUOTE) {
throw new Error("Fill RPC_URL, MARKET_ADDRESS, TOTAL_QUOTE before running");
}
const privateKey = process.env.PRIVATE_KEY;
if (!privateKey) throw new Error("Set PRIVATE_KEY in your environment before running");
// Provider/signer setup
const provider = new ethers.providers.JsonRpcProvider(RPC_URL);
const signer = new ethers.Wallet(privateKey, provider);
// Fetch market params and current orderbook
const marketParams = await KuruSdk.ParamFetcher.getMarketParams(provider, MARKET_ADDRESS);
const orderbook = await KuruSdk.OrderBook.getL2OrderBook(provider, MARKET_ADDRESS, marketParams);
if (!orderbook.asks || orderbook.asks.length === 0) {
throw new Error("No asks available to anchor price range");
}
// Derive best ask price in precision units (BigInt)
const pricePrecisionPow = BigInt(10) ** BigInt(KuruSdk.log10BigNumber(marketParams.pricePrecision));
const bestAskFloat = orderbook.asks[0][0]; // number
const bestAskPrice = BigInt(Math.floor(bestAskFloat * Number(pricePrecisionPow)));
// Compute start/end bounds around best ask by +/- RANGE_PCT
const startPrice = bestAskPrice - (bestAskPrice * RANGE_PCT) / 100n;
const endPrice = bestAskPrice + (bestAskPrice * RANGE_PCT) / 100n;
// Convert total quote to raw units using quote decimals
const quoteDecimals = Number(marketParams.quoteAssetDecimals.toString());
const totalQuoteWei = ethers.utils.parseUnits(TOTAL_QUOTE, quoteDecimals);
// Build batch LP details using PositionViewer (quote-liquidity driven)
const batchLPDetails = await KuruSdk.PositionViewer.getSpotBatchLPDetails(
MIN_FEES_BPS,
startPrice,
endPrice,
bestAskPrice,
BigInt(marketParams.pricePrecision.toString()),
BigInt(marketParams.sizePrecision.toString()),
BigInt(marketParams.quoteAssetDecimals.toString()),
BigInt(marketParams.baseAssetDecimals.toString()),
BigInt(marketParams.tickSize.toString()),
BigInt(marketParams.minSize.toString()),
BigInt(totalQuoteWei.toString()), // provide total quote liquidity
undefined // or provide base liquidity instead
);
console.log("Placing concentrated liquidity:", {
bids: batchLPDetails.bids.length,
asks: batchLPDetails.asks.length,
});
// Submit the provision transaction (calls batchProvisionLiquidity)
const receipt = await KuruSdk.PositionProvider.provisionLiquidity(
signer,
MARKET_ADDRESS,
batchLPDetails
);
console.log("Concentrated liquidity provisioned. Tx:", receipt.transactionHash);
}
main()
.then(() => process.exit(0))
.catch((err) => {
console.error("Error placing concentrated liquidity:", err);
process.exit(1);
});
Bid-Ask
asymmetric distribution (separate shaping on bids vs asks)placeBidAskConcentratedLiquidity
Copy
Ask AI
import { ethers } from "ethers";
import * as KuruSdk from "@kuru-labs/kuru-sdk";
// Docs Template: Place bid-ask distributed concentrated liquidity
// 1) Fill placeholders below
// 2) export PRIVATE_KEY=<your_key>
// 3) Run: npx ts-node scripts/docs_scripts/placeBidAskConcentratedLiquidity.ts
const RPC_URL = ""; // TODO: RPC endpoint
const MARKET_ADDRESS = ""; // TODO: Orderbook/market address
const MIN_FEES_BPS = 30n; // e.g., 30 for 0.3%
const RANGE_PCT = 1n; // +/- percent around best ask
const TOTAL_QUOTE = ""; // TODO: human string (e.g., "1000")
async function main() {
if (!RPC_URL || !MARKET_ADDRESS || !TOTAL_QUOTE) throw new Error("Fill RPC_URL, MARKET_ADDRESS, TOTAL_QUOTE");
const privateKey = process.env.PRIVATE_KEY;
if (!privateKey) throw new Error("Set PRIVATE_KEY in env");
const provider = new ethers.providers.JsonRpcProvider(RPC_URL);
const signer = new ethers.Wallet(privateKey, provider);
const marketParams = await KuruSdk.ParamFetcher.getMarketParams(provider, MARKET_ADDRESS);
const orderbook = await KuruSdk.OrderBook.getL2OrderBook(provider, MARKET_ADDRESS, marketParams);
if (!orderbook.asks?.length) throw new Error("No asks available to anchor price range");
const pricePow = BigInt(10) ** BigInt(KuruSdk.log10BigNumber(marketParams.pricePrecision));
const bestAskFloat = orderbook.asks[0][0];
const bestAskPrice = BigInt(Math.floor(bestAskFloat * Number(pricePow)));
const startPrice = bestAskPrice - (bestAskPrice * RANGE_PCT) / 100n;
const endPrice = bestAskPrice + (bestAskPrice * RANGE_PCT) / 100n;
const totalQuoteWei = ethers.utils.parseUnits(TOTAL_QUOTE, Number(marketParams.quoteAssetDecimals));
const batch = await KuruSdk.PositionViewer.getBidAskBatchLPDetails(
MIN_FEES_BPS,
startPrice,
endPrice,
bestAskPrice,
BigInt(marketParams.pricePrecision.toString()),
BigInt(marketParams.sizePrecision.toString()),
BigInt(marketParams.quoteAssetDecimals.toString()),
BigInt(marketParams.baseAssetDecimals.toString()),
BigInt(marketParams.tickSize.toString()),
BigInt(marketParams.minSize.toString()),
BigInt(totalQuoteWei.toString()),
);
console.log("Provisioning bid-ask concentrated liquidity:", { bids: batch.bids.length, asks: batch.asks.length });
const receipt = await KuruSdk.PositionProvider.provisionLiquidity(signer, MARKET_ADDRESS, batch);
console.log("Tx:", receipt.transactionHash);
}
main().catch((e) => { console.error("Error:", e); process.exit(1); });
Curve
curved distribution (e.g., more weight near best price, tapering out).placeCurveConcentratedLiquidity
Copy
Ask AI
import { ethers } from "ethers";
import * as KuruSdk from "@kuru-labs/kuru-sdk";
// Docs Template: Place curve-distributed concentrated liquidity
// 1) Fill placeholders below
// 2) export PRIVATE_KEY=<your_key>
// 3) Run: npx ts-node scripts/docs_scripts/placeCurveConcentratedLiquidity.ts
const RPC_URL = ""; // TODO: RPC endpoint
const MARKET_ADDRESS = ""; // TODO: Orderbook/market address
const MIN_FEES_BPS = 30n; // e.g., 30 for 0.3%
const RANGE_PCT = 1n; // +/- percent around best ask
const TOTAL_QUOTE = ""; // TODO: human string (e.g., "1000")
async function main() {
if (!RPC_URL || !MARKET_ADDRESS || !TOTAL_QUOTE) throw new Error("Fill RPC_URL, MARKET_ADDRESS, TOTAL_QUOTE");
const privateKey = process.env.PRIVATE_KEY;
if (!privateKey) throw new Error("Set PRIVATE_KEY in env");
const provider = new ethers.providers.JsonRpcProvider(RPC_URL);
const signer = new ethers.Wallet(privateKey, provider);
const marketParams = await KuruSdk.ParamFetcher.getMarketParams(provider, MARKET_ADDRESS);
const orderbook = await KuruSdk.OrderBook.getL2OrderBook(provider, MARKET_ADDRESS, marketParams);
if (!orderbook.asks?.length) throw new Error("No asks available to anchor price range");
const pricePow = BigInt(10) ** BigInt(KuruSdk.log10BigNumber(marketParams.pricePrecision));
const bestAskFloat = orderbook.asks[0][0];
const bestAskPrice = BigInt(Math.floor(bestAskFloat * Number(pricePow)));
const startPrice = bestAskPrice - (bestAskPrice * RANGE_PCT) / 100n;
const endPrice = bestAskPrice + (bestAskPrice * RANGE_PCT) / 100n;
const totalQuoteWei = ethers.utils.parseUnits(TOTAL_QUOTE, Number(marketParams.quoteAssetDecimals));
const batch = await KuruSdk.PositionViewer.getCurveBatchLPDetails(
MIN_FEES_BPS,
startPrice,
endPrice,
bestAskPrice,
BigInt(marketParams.pricePrecision.toString()),
BigInt(marketParams.sizePrecision.toString()),
BigInt(marketParams.quoteAssetDecimals.toString()),
BigInt(marketParams.baseAssetDecimals.toString()),
BigInt(marketParams.tickSize.toString()),
BigInt(marketParams.minSize.toString()),
BigInt(totalQuoteWei.toString()),
);
console.log("Provisioning curve concentrated liquidity:", { bids: batch.bids.length, asks: batch.asks.length });
const receipt = await KuruSdk.PositionProvider.provisionLiquidity(signer, MARKET_ADDRESS, batch);
console.log("Tx:", receipt.transactionHash);
}
main().catch((e) => { console.error("Error:", e); process.exit(1); });