TypeScript SDK
@parchment-trade/sdk is a TypeScript client for the public API. It reads markets and accounts, previews trades and builds unsigned Sui transactions. You sign and execute them with your own wallet or key.
Package: @parchment-trade/sdk on npm (Apache-2.0). Full API reference: API reference.
Install
npm install @parchment-trade/sdk @mysten/sui| Requirement | Version |
|---|---|
| Node | 22.14 or newer |
@mysten/sui (peer dependency) | >=1.45.2 <3 |
import { Parchment } from "@parchment-trade/sdk";Create a client
const parchment = await Parchment.create({
baseUrl: "https://api.parchment.trade",
});That is the whole setup: no package ids to configure. The client trusts the Parchment API. Each transaction builder returns the transaction the API built for your request, and you sign it with your wallet as you would any other transaction. The package and object ids are listed under Contract addresses for reference.
Reads are get*. Transaction builders are get*Tx.
Amounts
Amounts are integers in base units. USDC has 6 decimals, so 10_000_000n is 10 USDC. Prices are 1e18 fixed-point integers. Inputs take a bigint or a digit string. Every integer in a response is a bigint.
Read markets and an account
const { markets } = await parchment.getMarkets();
const market = markets[0];
// Within the market's range: 100x to 1000x, 50x to 500x on SUI.
const leverage = market.market.risk!.minLeverage;
const account = await parchment.getAccount({ walletAddress });
const positions = account.getPositions();Preview a trade
Previews are estimates at the current mid, not fills. Pass slippageBps (0 to 10000) to get a suggested price bound.
const preview = await parchment.getPreviewOpen({
marketId: market.market.marketId,
accountId,
margin: 10_000_000n,
leverage,
isLong: true,
slippageBps: 50n,
});
if (preview.errors.length === 0) {
console.log(preview.notional, preview.entryWad, preview.bustWad, preview.suggestedBoundWad);
}Open and close
Opens and closes are two-step requests. A keeper fills them at the oracle mid.
const built = await parchment.getRequestOpenTx({
walletAddress,
accountId,
accountCapId, // your AccountCap object id
marketId: market.market.marketId,
margin: 10_000_000n,
leverage,
isLong: true,
acceptablePriceWad: preview.suggestedBoundWad!,
});Without a preview, compute the bound from a price:
import { boundFromSlippage } from "@parchment-trade/sdk";
const acceptablePriceWad = boundFromSlippage({
side: "long",
action: "open",
priceWad: market.price.priceWad!,
slippageBps: 50,
});To close, request a close for the position:
const position = positions[0];
const closed = await parchment.getRequestCloseTx({
walletAddress,
accountId,
accountCapId,
marketId: position.marketId,
positionId: position.positionId,
});- On an open,
acceptablePriceWadis required and non-zero. A missing or zero value throwsprice_bound_requiredbefore any request. - On a close it is optional. Omitted or
0means no bound. - The bound is stored on the order only. Fills do not check it.
marginis the order's margin. The $0.02 execution fee comes from your free balance on top of it.- To send a builder code, pass
builderCode(the API resolves it to its id) orbuilderId, not both. The trade pays the code only if you approved it; see Builder codes.
Built transactions
Every builder returns:
| Field | Meaning |
|---|---|
request | The route and body the transaction was built for |
tx | A @mysten/sui Transaction, with your address as sender |
commands | The Move calls it makes |
note | Guidance text, or null |
txBytesBase64 | The transaction kind as base64 |
Gas is not included. Your wallet sets it when it signs.
import { requestedOrderIdFromTx } from "@parchment-trade/sdk";
// @mysten/sui 2.x
const result = await client.signAndExecuteTransaction({
transaction: built.tx,
signer,
include: { events: true },
});
const orderId = requestedOrderIdFromTx(result);
if (orderId) {
const order = await parchment.waitForOrder(orderId, { timeoutMs: 10_000 });
if (order.status === "filled") console.log(order.positionId);
if (order.status === "refunded") console.log(order.reason);
}One-click trading
A session key is a key in your app that the account grants as a delegate. It can open, close and cancel trades. It cannot withdraw, deposit or move funds out of the account.
| Step | SDK |
|---|---|
| Create a key | createSessionKey() |
| Grant it (the wallet signs once) | getGrantDelegateTx, with an expiryMs. sessionKeyExpiryMs() gives just under 30 days from now; the Parchment app grants with no expiry (a far-future expiryMs) |
| Grant it with the TP/SL delegate in one transaction | getGrantSessionAndTriggerTx |
| Trade as the key | getRequestOpenAsDelegateTx, getRequestCloseAsDelegateTx |
| Revoke | getRevokeDelegateTx, getRevokeSessionAndTriggerTx |
Gas sponsorship
Session-key trades can be sponsored. Sign a proof with signSponsorProof, send it with sponsorTransaction, then sign the result with signSessionSponsoredTx. Owner transactions are never sponsored.
On a sponsor error, shouldPayOwnGas(error) says whether to fall back to the wallet. isSessionKeyRevoked(error) says the key is no longer a delegate.
Take-profit and stop-loss
setTriggers({ owner, accountId, target, takeProfitWad, stopLossWad }, signer) sets both legs with one owner-signed proof. null clears a leg. getTriggers({ walletAddress }) lists them. Triggers need the close-only delegate, granted with getGrantCloseDelegateTx or getGrantSessionAndTriggerTx. See Stop-loss and take-profit.
Close All
Several closes fit in one transaction.
| Function | Does |
|---|---|
planCloseBatches(legs, options) | Groups positions into batches before anything is built |
fitCloseBatch(legs, options) | Returns the longest prefix of built legs that fits one transaction, and that transaction |
batchCloseTx(legs, options) | Builds one transaction from the legs |
Each built leg is { tx: built.tx, request: built.request }. fitCloseBatch and batchCloseTx take { sender }.
import { fitCloseBatch } from "@parchment-trade/sdk";
const legs = [];
for (const p of positions) {
const built = await parchment.getRequestCloseTx({
walletAddress, accountId, accountCapId, marketId: p.marketId, positionId: p.positionId,
});
legs.push({ tx: built.tx, request: built.request });
}
const { tx, count } = await fitCloseBatch(legs, { sender: walletAddress });
// Sign and execute tx. If count < legs.length, batch the rest after it confirms.Locked PARCHMENT
getExitEarlyBoundedTx({ unlocks, maxPenalty }) exits only the entries you quote and aborts above maxPenalty. Pass the game profile's earlyExitUnlocks and earlyExitPenaltyNow. Use it instead of getExitEarlyTx, which exits every eligible entry at execution time.
Pay out the queue
await parchment.getHarvestTx({ walletAddress, maxEntries: 32n });Anyone can run it. maxEntries defaults to 32 and must be between 1 and 128.
Errors
API and local errors throw ParchmentApiError:
| Field | Meaning |
|---|---|
status | HTTP status, or 0 for a local error |
code | Error code |
message | Description |
retryAfterMs | From the Retry-After header, when there is one |
| Code | Cause |
|---|---|
price_bound_required | An open without a non-zero acceptablePriceWad |
bad_request | A malformed address, cursor or limit, caught before sending |
timeout | A request passed its timeoutMs |
The Close All functions throw BatchCloseError.