> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pariprotocol.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# SDK

> Read data and build unsigned transactions in TypeScript.

The SDK is a TypeScript library for Pari. It reads data, builds unsigned transactions and launches tokens. It never sends a transaction unless you call `sendAndConfirm`.

Pari is live on Solana and built to run on more chains. The SDK works on devnet only today.

## Install

The SDK is not on npm yet, so this command fails today.

```sh theme={null}
npm install @pariprotocol/sdk
```

The SDK uses version 1 of `@solana/web3.js`, so keep your project on it. The SDK is an ES module, so use `import`.

## Read data

`apiClient` reads the [REST API](https://docs.pariprotocol.xyz/build/rest-api.md). It is the best way to read many tokens or wallets. It takes no key, and it cannot sign or send.

```ts theme={null}
import { apiClient } from "@pariprotocol/sdk";

const api = apiClient();

const status = await api.status();
console.log(status.cluster, status.indexedSlot);

const page = await api.tokens({ sort: "volume", limit: 5 });
for (const t of page.items) console.log(t.symbol, t.mint, t.volumeTotal);
```

`all` walks every page for you:

```ts theme={null}
import { apiClient } from "@pariprotocol/sdk";

const api = apiClient();

for await (const t of api.all((cursor) => api.tokens({ limit: 100, cursor }))) {
  console.log(t.mint, t.holders);
}
```

Read one token and one wallet:

```ts theme={null}
import { apiClient } from "@pariprotocol/sdk";

const api = apiClient();
const mint = "AgjecsKW5mRCBqRHECFh4iXq6aHzX1hWCCkyFUoWUCVa";
const wallet = "CfkTXZyqRSTM3qMQ97tk6B2Wdf39c4wdaSkferaypKPd";

const token = await api.token(mint);
const trades = await api.trades(mint, { limit: 5 });
const candles = await api.candles(mint, { secs: 3600 });

const held = await api.walletTokens(wallet, { limit: 5 });
const stake = await api.walletStake(wallet);
const rewards = await api.walletRewards(wallet, { limit: 5 });

console.log(token.symbol, trades.items.length, candles.items.length);
console.log(held.items.length, stake.staked, rewards.items.length);
```

A failed request throws `ApiRequestError`. A `429` waits for `Retry-After` and retries twice first.

```ts theme={null}
import { ApiRequestError, apiClient } from "@pariprotocol/sdk";

try {
  await apiClient().token("not-a-mint");
} catch (e) {
  if (e instanceof ApiRequestError) console.log(e.status, e.code, e.message);
}
```

`stream` reads the live feed. It reconnects when the connection drops and gives you each event once.

```ts theme={null}
import { apiClient } from "@pariprotocol/sdk";

const signal = AbortSignal.timeout(60_000); // stop after one minute
for await (const e of apiClient().stream(["launches", "trades"], { signal })) {
  console.log(e.event, e.data);
}
```

## On-chain reads

`PariClient` reads straight from the chain through an RPC node. Use it when you need the latest state.

`connectionFor` returns a promise, so `await` it. It checks that the RPC node is on the right cluster. It uses `PARI_RPC_URL` when set, or the public devnet RPC.

```ts theme={null}
import { PublicKey } from "@solana/web3.js";
import { PariClient, connectionFor } from "@pariprotocol/sdk";

const connection = await connectionFor("devnet");
const client = new PariClient({ connection });

const mint = new PublicKey("AgjecsKW5mRCBqRHECFh4iXq6aHzX1hWCCkyFUoWUCVa");
const owner = new PublicKey("CfkTXZyqRSTM3qMQ97tk6B2Wdf39c4wdaSkferaypKPd");

const token = await client.token(mint); // null when the mint is not a Pari token
const position = await client.position(owner);
const quote = await client.quote(mint, "buy", 100_000_000n); // 0.1 SOL in lamports

console.log(token?.quote.symbol, position.staked, position.cooling);
console.log(quote.venue, quote.expectedOut, quote.minOut, quote.feeBps);
```

`readTokenFromApi`, `readStakePositionFromApi`, `readHolderRewardsOutlookFromApi` and `readProtocolStatusFromApi` return the same types from the API. You can swap the source without changing the code that uses them.

## Build unsigned transactions

Every `PariClient` write method returns unsigned work. Nothing is sent. You sign with your own wallet.

| Method | Returns |
| - | - |
| `client.trade(args)` | `{ ixs, quote }`: the instructions of a buy or a sell |
| `client.stake(owner, amount)` | Instructions |
| `client.unstake(owner, amount)` | Instructions that start the cooldown |
| `client.withdraw(owner, ids)` | Instructions. `ids` is a list of ticket ids, or `"ready"`. |
| `client.launch(args)` | `{ tx, mint, pool }`: see [Launch](#launch) |

`amountIn` is in base units of the pair for a buy, and in base units of the token for a sell. Tokens have 6 decimals.

This example builds a buy and simulates it. It needs no key and spends nothing. The wallet here is empty, so `result.ok` is `false`.

```ts theme={null}
import { Keypair, PublicKey, TransactionMessage, VersionedTransaction } from "@solana/web3.js";
import { PariClient, connectionFor, simulate } from "@pariprotocol/sdk";

const connection = await connectionFor("devnet");
const client = new PariClient({ connection });

const trader = Keypair.generate().publicKey; // use your wallet's address
const mint = new PublicKey("AgjecsKW5mRCBqRHECFh4iXq6aHzX1hWCCkyFUoWUCVa");

const { ixs, quote } = await client.trade({ trader, mint, side: "buy", amountIn: 100_000_000n, slippageBps: 300, maxFeeBps: 500 });

const { blockhash } = await connection.getLatestBlockhash();
const message = new TransactionMessage({ payerKey: trader, recentBlockhash: blockhash, instructions: ixs }).compileToV0Message();
const tx = new VersionedTransaction(message);

const result = await simulate(connection, tx);
console.log(quote.minOut, result.ok, result.unitsConsumed);
```

A buy in the first moments of a launch pays a high fee that falls over time. `trade` throws when the fee is over `maxFeeBps` (default 500). The error has the name `FeeTooHighError` and a `feeBps` field. Wait and quote again. See [Fees](https://docs.pariprotocol.xyz/protocol/fees.md).

To send, call `sendAndConfirm(connection, tx, [signer])`, or pass the transaction to a wallet.

## Launch

`client.launch` builds the launch transaction. You generate the mint key yourself.

| Field | Meaning |
| - | - |
| `creator` | The wallet that pays and becomes the creator. |
| `mint` | A `Keypair` for the new token. |
| `preset` | `"light"`, `"standard"` or `"strict"`. See [Presets](https://docs.pariprotocol.xyz/protocol/presets.md). |
| `name`, `symbol`, `uri` | The token name, ticker and metadata URL. |
| `devBuyLamports` | The dev buy in base units of the pair. Use `0n` for none. |
| `pair` | Optional. `"SOL"` (default) or a stock symbol. |
| `integrator` | Optional. The authority of a registered integrator. |

The mint key has already signed the result. The creator signs last.

```ts theme={null}
import { readFileSync } from "node:fs";
import { Keypair } from "@solana/web3.js";
import { PariClient, connectionFor, maxDevBuy, sendAndConfirm, simulate } from "@pariprotocol/sdk";

const creator = Keypair.fromSecretKey(Uint8Array.from(JSON.parse(readFileSync(process.env.PARI_KEYPAIR!, "utf8"))));
const mint = Keypair.generate();

const connection = await connectionFor("devnet");
const client = new PariClient({ connection });

console.log("largest dev buy:", await maxDevBuy(connection, "standard"));

const { tx, pool } = await client.launch({
  creator: creator.publicKey,
  mint,
  preset: "standard",
  name: "Cat",
  symbol: "CAT",
  uri: "https://example.com/cat.json",
  devBuyLamports: 0n,
});

const dryRun = await simulate(connection, tx);
if (!dryRun.ok) throw new Error(dryRun.logs.join("\n"));

const signature = await sendAndConfirm(connection, tx, [creator, mint]);
console.log(mint.publicKey.toBase58(), pool.toBase58(), signature);
```

`PARI_KEYPAIR` is the path to a keypair file. Never put a key in your code.

A Solana transaction holds at most 1232 bytes, and the launch carries the name, ticker and URI. Keep the URI short. `launch` throws if the transaction is too large.

### Check a name first

A name can be locked by another token. `getNameLock` tells you before you launch.

```ts theme={null}
import { connectionFor, getNameLock } from "@pariprotocol/sdk";

const connection = await connectionFor("devnet");
console.log(await getNameLock(connection, "Cat")); // { free, lockedUntil, mint }
```

### Upload an image and metadata

Registered integrators can use the hosted upload service. It needs `PARI_API_KEY`. Without a key, host your own metadata and pass its URL as `uri`.

```ts theme={null}
import { readFileSync } from "node:fs";
import { uploadClient, uploadTokenAssets } from "@pariprotocol/sdk";

const uploads = uploadClient({ apiKey: process.env.PARI_API_KEY! });

const { uri } = await uploadTokenAssets(uploads, {
  imageBytes: readFileSync("./cat.png"),
  contentType: "image/png",
  name: "Cat",
  symbol: "CAT",
  description: "A cat",
});
console.log(uri);
```

### Launch with a stock pair

A launch can be paired with a tokenized stock instead of SOL. Pass its symbol as `pair`. `devBuyLamports` is then in base units of the stock, and the creator must already hold it. Fees of that token are in the stock.

Tokenized stocks are not offered to US persons.

```ts theme={null}
import { loadDeployment } from "@pariprotocol/sdk";

console.log(Object.keys(loadDeployment().pairs ?? {})); // the stock symbols you can pass as pair
```

## Other exports

* `PRESETS`: the values of each preset.
* `normalize`: the name normalization the protocol uses.
* `@pariprotocol/sdk/low-level`: the raw instruction builders.

## Next

* [Quickstart](https://docs.pariprotocol.xyz/build/quickstart.md)
* [CLI](https://docs.pariprotocol.xyz/build/cli.md)
* [REST API](https://docs.pariprotocol.xyz/build/rest-api.md)
* [AI tools](https://docs.pariprotocol.xyz/build/ai-tools.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.