Sign In

@nirholas/pump-sdk

Package Overview
Dependencies
Maintainers
1
Versions
10
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@nirholas/pump-sdk - npm Package Compare versions

Comparing version
1.35.3
to
1.36.0
+163
src/__tests__/cliFormat.test.ts
/**
* Tests for the CLI's rendering layer.
*
* `--json` is a contract: scripts parse it. The two bugs guarded here both shipped
* silently once and produced output that looked fine to a human and was wrong to a
* machine: BN values leaking out in hexadecimal, and the SDK's scaled spot price
* being printed as if it were SOL per token (off by 1000).
*/
import BN from "bn.js";
import { PublicKey } from "@solana/web3.js";
import {
formatBps,
formatCompact,
formatScaledPrice,
formatSol,
formatTokens,
lamportsToSol,
meter,
normalizeForJson,
rawToTokens,
setColorEnabled,
shortAddress,
solToLamports,
toJson,
tokensToRaw,
} from "../cli/format";
beforeAll(() => {
// Colour codes would make every string assertion below unreadable.
setColorEnabled(false);
});
describe("cli/format", () => {
describe("normalizeForJson", () => {
it("renders BN in base 10, not the hex that BN.toJSON would emit", () => {
// BN.prototype.toJSON returns a padded hex string, so a naive
// JSON.stringify replacer never sees the BN at all.
expect(normalizeForJson(new BN(0))).toBe("0");
expect(normalizeForJson(new BN(255))).toBe("255");
expect(normalizeForJson(new BN("1000000000000000"))).toBe(
"1000000000000000",
);
});
it("renders public keys as base58", () => {
const key = PublicKey.default;
expect(normalizeForJson(key)).toBe(key.toBase58());
});
it("walks nested objects and arrays", () => {
const value = normalizeForJson({
outer: { inner: new BN(42), list: [new BN(1), new BN(2)] },
}) as { outer: { inner: string; list: string[] } };
expect(value.outer.inner).toBe("42");
expect(value.outer.list).toEqual(["1", "2"]);
});
it("passes plain scalars through untouched", () => {
expect(normalizeForJson(7)).toBe(7);
expect(normalizeForJson("hello")).toBe("hello");
expect(normalizeForJson(true)).toBe(true);
expect(normalizeForJson(undefined)).toBeNull();
});
it("survives a full round trip through toJson", () => {
const parsed: unknown = JSON.parse(
toJson({ marketCap: new BN("1234500000000"), mint: PublicKey.default }),
);
expect(parsed).toEqual({
marketCap: "1234500000000",
mint: PublicKey.default.toBase58(),
});
});
});
describe("formatScaledPrice", () => {
it("divides the scaled spot price down to SOL per whole token", () => {
// 1e12 scaled units is exactly 1 SOL per token.
expect(formatScaledPrice(new BN("1000000000000"))).toBe("1 SOL");
expect(formatScaledPrice(new BN("500000000000"))).toBe("0.5 SOL");
});
it("does not agree with formatSol, which would be the 1000x bug", () => {
const scaled = new BN("93703");
expect(formatScaledPrice(scaled)).not.toBe(formatSol(scaled));
});
it("renders zero cleanly", () => {
expect(formatScaledPrice(new BN(0))).toBe("0 SOL");
});
});
describe("lamport and token conversions", () => {
it("round-trips SOL through lamports", () => {
expect(lamportsToSol(solToLamports(1.5))).toBeCloseTo(1.5, 9);
expect(solToLamports(1).toString()).toBe("1000000000");
});
it("round-trips whole tokens through raw units", () => {
expect(rawToTokens(tokensToRaw(1234.5))).toBeCloseTo(1234.5, 6);
expect(tokensToRaw(1).toString()).toBe("1000000");
});
it("formats SOL without trailing zero noise", () => {
expect(formatSol(new BN("1500000000"))).toBe("1.5 SOL");
expect(formatSol(new BN(0))).toBe("0 SOL");
});
it("uses exponent notation rather than rounding tiny amounts to zero", () => {
expect(formatSol(new BN(1))).toContain("e-");
});
});
describe("compact numbers", () => {
it("abbreviates by magnitude", () => {
expect(formatCompact(1_500)).toBe("1.5K");
expect(formatCompact(2_400_000)).toBe("2.4M");
expect(formatCompact(3_000_000_000)).toBe("3B");
expect(formatCompact(1_000_000_000_000)).toBe("1T");
});
it("formats a one billion token supply as 1B", () => {
expect(formatTokens(new BN("1000000000000000"))).toBe("1B");
});
});
describe("formatBps", () => {
it("converts basis points to a percentage", () => {
expect(formatBps(10_000)).toBe("100%");
expect(formatBps(125)).toBe("1.25%");
expect(formatBps(0)).toBe("0%");
});
});
describe("meter", () => {
it("clamps out-of-range fractions instead of drawing a broken bar", () => {
expect(meter(-1, 10)).toContain("0.00%");
expect(meter(2, 10)).toContain("100.00%");
expect(meter(0.5, 10)).toContain("50.00%");
});
it("draws exactly the requested width", () => {
const bar = meter(0.5, 20).split(" ")[0] ?? "";
expect([...bar]).toHaveLength(20);
});
});
describe("shortAddress", () => {
it("elides the middle of a long address", () => {
const short = shortAddress("FeMbDoX7R1Psc4GEcvJdsbNbZA3bfztcyDCatJVJpump");
expect(short.startsWith("FeMbDo")).toBe(true);
expect(short.endsWith("JVJpump")).toBe(false);
expect(short).toContain("…");
});
it("leaves short strings alone", () => {
expect(shortAddress("abc")).toBe("abc");
});
});
});
/**
* Regression tests for reading a token whose bonding curve has migrated.
*
* When a token graduates to PumpAMM the program zeroes every reserve field on
* the bonding curve account. Before this was handled, every analytics helper
* that divides by `virtualTokenReserves` threw "Division by zero" for the
* entire population of graduated tokens, which is the majority of tokens anyone
* looks up by the time they are worth looking up.
*/
import BN from "bn.js";
import { getBondingCurveSummary, getTokenPrice } from "../analytics";
import { computeFeesBps } from "../fees";
import {
makeFeeConfig,
makeGlobal,
makeMigratedBondingCurve,
} from "./fixtures";
const global = makeGlobal();
const feeConfig = makeFeeConfig();
const migrated = makeMigratedBondingCurve();
const mintSupply = global.tokenTotalSupply;
describe("migrated bonding curves", () => {
describe("getTokenPrice", () => {
it("reports zeros instead of throwing on a zeroed curve", () => {
const price = getTokenPrice({
global,
feeConfig,
mintSupply,
bondingCurve: migrated,
});
expect(price.isGraduated).toBe(true);
expect(price.marketCap.isZero()).toBe(true);
expect(price.buyPricePerToken.isZero()).toBe(true);
expect(price.sellPricePerToken.isZero()).toBe(true);
});
it("works with a null fee config too", () => {
expect(() =>
getTokenPrice({
global,
feeConfig: null,
mintSupply,
bondingCurve: migrated,
}),
).not.toThrow();
});
});
describe("computeFeesBps", () => {
it("falls back to the base tier rather than dividing by zero", () => {
const fees = computeFeesBps({
global,
feeConfig,
mintSupply,
virtualSolReserves: migrated.virtualSolReserves,
virtualTokenReserves: migrated.virtualTokenReserves,
});
expect(fees.protocolFeeBps).toBeInstanceOf(BN);
expect(fees.protocolFeeBps.isNeg()).toBe(false);
expect(fees.creatorFeeBps.isNeg()).toBe(false);
});
});
describe("getBondingCurveSummary", () => {
it("summarises a migrated curve as graduated at 100%", () => {
const summary = getBondingCurveSummary({
global,
feeConfig,
mintSupply,
bondingCurve: migrated,
});
expect(summary.isGraduated).toBe(true);
expect(summary.progressBps).toBe(10_000);
expect(summary.solNeededToGraduate.isZero()).toBe(true);
expect(summary.marketCap.isZero()).toBe(true);
});
it("still prices a live curve normally", () => {
const summary = getBondingCurveSummary({
global,
feeConfig,
mintSupply,
bondingCurve: liveCurve(),
});
expect(summary.isGraduated).toBe(false);
expect(summary.marketCap.gt(new BN(0))).toBe(true);
expect(summary.buyPricePerToken.gt(new BN(0))).toBe(true);
});
});
});
/** A curve with live reserves, to prove the zero-guard did not swallow them. */
function liveCurve() {
return {
...makeMigratedBondingCurve(),
virtualTokenReserves: new BN("1073000000000000"),
virtualSolReserves: new BN("30000000000"),
realTokenReserves: new BN("793100000000000"),
realSolReserves: new BN(0),
complete: false,
};
}
+1
-1
{
"name": "@nirholas/pump-sdk",
"version": "1.35.3",
"version": "1.36.0",
"description": "TypeScript SDK for the Pump protocol on Solana: token creation, bonding curves, AMM pools, fee sharing, and volume rewards",

@@ -5,0 +5,0 @@ "keywords": [

@@ -29,2 +29,3 @@ <p align="center">

- [Quick Start](#-quick-start)
- [The `pump` CLI](#-the-pump-cli)
- [50 Runnable Examples](#-50-runnable-examples)

@@ -140,2 +141,60 @@ - [Usage Examples](#-usage-examples)

## 💻 The `pump` CLI
The SDK ships with a binary. No wallet, no API key, and no configuration are needed for anything that reads the chain.
```bash
npm install -g @nirholas/pump-sdk
pump curve <mint> # price, market cap, graduation meter, fee tier
pump quote buy <mint> --sol 1 # what 1 SOL really buys, after fees and impact
pump watch <mint> # live dashboard
pump doctor # check your RPC, wallet, and balance in one shot
```
Or without installing anything: `npx -p @nirholas/pump-sdk pump curve <mint>`.
```
FeMbDoX7R1Psc4GEcvJdsbNbZA3bfztcyDCatJVJpump live on the bonding curve
────────────────────────────────────────────────────────────
Market cap 93.705347 SOL
Buy price 0.000000096 SOL per token
Sell price 0.000000091 SOL per token
Trading fee 1.25% 0.95% protocol + 0.3% creator
Graduation ███████████████░░░░░░░░░ 61.39%
SOL to graduate 60.684661 SOL
SOL in curve 24.921536 SOL
Tokens left 306.21M of 793.1M
```
| Command | What it does |
|---|---|
| `pump curve` / `price` / `pool` / `global` | Read curve, price, AMM pool, and protocol state |
| `pump quote buy\|sell` | Price a trade offline: output, fees, effective price, impact |
| `pump buy` / `sell` | Trade, auto-routing between the bonding curve and PumpAMM |
| `pump create` | Launch a token, optionally with an atomic first buy |
| `pump vanity` | Grind a `...pump` mint address |
| `pump fees` / `incentives` | Check and claim creator fees and volume rewards |
| `pump watch` | Live-refreshing dashboard, or a JSON price feed |
| `pump events` | Decode the Pump events in any transaction |
| `pump pda` | Derive any Pump program address |
| `pump doctor` / `config` | Diagnose and configure |
**Safe by construction.** Reads never load a keypair. Every trade is simulated before anything is sent, prints exactly what is about to happen, and waits for an explicit yes. `--simulate` sends nothing; `--yes` is the scripting escape hatch; a piped command with neither refuses rather than spending funds unattended.
**Scriptable.** Every command takes `--json` and writes nothing else to stdout in that mode:
```bash
pump curve <mint> --json | jq .marketCapSol
pump watch <mint> --json --interval 10 | jq -r '"\(.at) \(.marketCapSol) SOL"'
pump events <sig> --json | jq '.events[] | select(.type=="trade")'
```
Full reference: **[docs/cli.md](docs/cli.md)**. Ten-minute walkthrough: **[tutorial 45](tutorials/45-cli-quickstart.md)**.
---
## 🧪 50 Runnable Examples

@@ -142,0 +201,0 @@

@@ -15,3 +15,3 @@ /**

import BN from "bn.js";
import { Keypair, PublicKey } from "@solana/web3.js";
import { Keypair, type PublicKey } from "@solana/web3.js";

@@ -18,0 +18,0 @@ import { calculateBuyPriceImpact, calculateSellPriceImpact } from "../../analytics";

@@ -14,3 +14,3 @@ /**

import type { Command } from "commander";
import { Keypair } from "@solana/web3.js";
import type { Keypair } from "@solana/web3.js";

@@ -17,0 +17,0 @@ import {

@@ -94,13 +94,14 @@ #!/usr/bin/env node

/**
* Read the package version from the manifest rather than hardcoding it, so a
* release bump never leaves `pump --version` lying.
* The package version, injected by tsup at build time (see `tsup.config.ts`).
*
* Declared rather than imported so a release bump can never leave
* `pump --version` lying, and so the binary does not read a file to answer it.
* The fallback covers running the TypeScript source directly, e.g. under `tsx`.
*/
declare const __PUMP_CLI_VERSION__: string | undefined;
function readVersion(): string {
try {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const pkg = require("../../package.json") as { version?: string };
return pkg.version ?? "0.0.0";
} catch {
return "0.0.0";
}
return typeof __PUMP_CLI_VERSION__ === "string"
? __PUMP_CLI_VERSION__
: "0.0.0-dev";
}

@@ -107,0 +108,0 @@

@@ -13,6 +13,6 @@ /**

ComputeBudgetProgram,
Keypair,
PublicKey,
type Keypair,
type PublicKey,
Transaction,
TransactionInstruction,
type TransactionInstruction,
VersionedTransaction,

@@ -19,0 +19,0 @@ TransactionMessage,

Sorry, the diff of this file is too big to display