Sally
SALLY
TypeScript SDK · v2.1.0

sally-defi-ts-sdk

One typed SallyClient for the Sally CrossHybrid protocol on Base and BSC — best-route hybrid swaps, V2·V3·V4·Slipstream liquidity, 1e18 USD pricing, honeypot screening, locks and referral fees behind one beginner-friendly, AI-agent-ready API. Safe by default: it simulates every route on-chain, sets minOut from the simulation, then asserts the funds actually arrived. 100% on-chain — no Sally API, no subgraph, no backend in the data path.

npm install sally-defi-ts-sdkimport { SallyClient }Node ≥ 18ethers ≥ 6MIT
Why

Raw ethers / viem, minus the foot-guns

Most TypeScript DeFi code leans on untyped tuples, hand-rolled approvals, quotes that don't match execution, and no honeypot or sandwich protection. The SDK folds all of that into one safe call — the diligence is the default, not an opt-in.

Fully on-chain & decentralized. Every read — prices, routes, positions, balances — is a direct eth_call to the contracts. No proprietary API, no Graph Node / subgraph, no indexer. Point it at any RPC and the only thing you trust is the chain.
Safety in the hot path. Simulate-before-execute plus a post-trade balance-delta assertion — shipped in the call path, not just the test suite. Even mature SDKs skip the balance-delta check.
Typed end-to-end. pos.liquidity, plan.isSafe, quote.estimatedAmountOut — classes with autocomplete, ships .d.ts. No tuple[7].
AI-agent-ready by construction. Every return serializes cleanly, so wiring Sally into LLM tools is mechanical. Async, MEV-protection and EIP-2612 permit are constructor/option args — not projects.
Honest about scope. Sally routes within its own protocol on Base + BSC — it is not a 100-source aggregator. The full, cited comparison (advantages and trade-offs) vs raw ethers.js / viem / Uniswap SDK / aggregator SDKs lives in docs/comparison.md.
Install

One package, one peer dep

The only runtime dependency is the ethers v6 stack. Ships ESM and bundled .d.ts types — works in Node and modern bundlers/browsers.

npm / pnpm / yarn
npm install sally-defi-ts-sdk ethers
# or
pnpm add sally-defi-ts-sdk ethers
yarn add sally-defi-ts-sdk ethers
ESM + types. Pure ESM (type: module) and ships .d.tsimport { SallyClient } from "sally-defi-ts-sdk". The async client lives at the sally-defi-ts-sdk/aio subpath.
Requirements. Node ≥ 18 and ethers ≥ 6 (a peer dep you install alongside). Reads need only an RPC; writes need a signer.
Quickstart

Read in three lines

Point the client at any Base RPC and read prices, rates and quotes — no key required for reads.

read-only.ts
import { SallyClient, Base } from "sally-defi-ts-sdk";

const sally = new SallyClient("base", "https://mainnet.base.org");

(await sally.prices.usd(Base.USDC)).asFloat;          // 1.0      (display / spot-mid)
(await sally.prices.usdImpact(Base.WETH)).asFloat;    // 1703.19  (execution-aware)

const quote = await sally.swap.quote(Base.WETH, Base.USDC, 10n ** 18n);
Number(quote.estimatedAmountOut) / 1e6;               // 1703.45 USDC
Two prices, on purpose. prices.usd is the spot-mid display price; prices.usdImpact is the execution-aware price after slippage for a reference size. Same split as the on-chain getUsdPrice / getUsdPriceImpact pair.
Swapping

Safe by default

Inspect a plan first, then execute with the same vetting plus a received-balance assertion. Pass a private key, a mnemonic, the SALLY_PRIVATE_KEY env var — or no key at all (build-only).

swap.ts
const sally = new SallyClient("base", RPC, { privateKey: "0x…" });  // or SALLY_PRIVATE_KEY env

// Inspect first: compares every route, simulates each, screens the output token.
const plan = await sally.swap.plan(Base.WETH, Base.USDC, 10n ** 18n);
plan.summary();      // 'SwapPlan(… out~1702924727 sim=… min=… impact=13bps steps=2 SAFE)'
plan.isSafe;         // false if honeypot / excessive tax / >15% impact / bad route
plan.candidates;     // every route considered, with simulated output

// Execute: same vetting, then send + balance-delta assertion.
const receipt = await sally.swap.execute(Base.WETH, Base.USDC, 10n ** 18n, { slippageBps: 50 });

// Native ETH in — pass the NATIVE sentinel; value is attached for you.
import { NATIVE } from "sally-defi-ts-sdk";
await sally.swap.execute(NATIVE, Base.USDC, 10n ** 17n, { slippageBps: 50 });
What execute() does, in order. approve (exact amount) → enumerate routes → integrity-check each so funds can never enter a wrong pool → simulate every route on-chain → pick best realized output → honeypot/tax preflight on the output token → set minOut from the simulation → send → assert received ≥ minOut. See docs/safety.md.
tune the guard rails
import { SafetyConfig } from "sally-defi-ts-sdk";

const sally = new SallyClient("base", RPC, {
  privateKey: "0x…",
  safety: new SafetyConfig({
    slippageBps: 30,
    taxBlockBps: 3000,
    priceImpactBlockBps: 1000,
  }),
});
API surface

One object, every function

SallyClient exposes the whole protocol through feature namespaces. Raw contracts stay reachable via client.swapContract / liquidityContract / lensContract / sidecarContract.

client.pricesread

usd (display), usdImpact (execution), spot, weth, usdLiquidity, tokenInfo — pricing at 1e18 precision.

client.swapsafe

quote (+ V2/V3/V4/Deep), candidates, plan, simulateRoute, execute (+ buildOnly), tokenInfo.

client.walletread

balances, balancesRaw, totalUsd — formatted or raw, with USD value.

client.liquiditywrite

add / increase / decrease / remove / lock / claim / harvest across V2·V3·V4·Slipstream, positions, pool state, dex registry.

client.feeswrite

Referral + batch fee reads & claims, swapFee. See Referrals.

Single source of truth: all addresses + ABIs come from the bundled deployment.json — nothing hard-coded. Proxy-fallback aware: Lens views resolve through the swap proxy, Sidecar views through the liq proxy (see the contract architecture).

Liquidity

Add, lock, claim, harvest

Full LP lifecycle across V2·V3·V4 and Aerodrome Slipstream, plus time-locks and a unified fee claim — typed positions, no positional tuples.

liquidity.ts
await sally.liquidity.addV2(4, Base.WETH, Base.USDC, 10n ** 15n, 2_000_000n, { lockDays: 0 });

await sally.liquidity.positionsV3(wallet, 0, 10);    // V3Position[]
await sally.liquidity.locks(wallet);                 // Lock[]
await sally.liquidity.claimableFees(npm, tokenId);   // ClaimableFees

// addV3 / addV4 / addSlipstream, increase/decrease, removeV2,
// lock/unlock, claimFeesV3/V4(+Locked), massClaimFees, harvestOp
V3/V4 positions are locked by design. V2 LP can be added unlocked (lockDays: 0) or time-locked. V3/V4/Slipstream positions are minted into the controller's lock vault, so they require lockDays ≥ 7 (the on-chain MIN_LOCK_DAYS).
Referrals

Earn & claim referral fees

Every swap and liquidity action takes a referral address. Pass yours and the protocol accrues referral fees to it; read and claim them anytime.

referrals.ts
await sally.swap.execute(Base.WETH, Base.USDC, 10n ** 18n, { referral: "0xYourRef" });

await sally.fees.totalReferralOwed("0xYourRef");        // total owed across tokens
await sally.fees.referralTokens("0xYourRef");           // per-token breakdown (paged)
await sally.fees.claimReferral([Base.USDC, Base.WETH]); // claim selected tokens
Same fee share as the app. This is the same referral system that powers the referral page and the embed widget — your wallet earns a share of the protocol fee on every routed swap. Full guide: docs/referrals.md.
Advanced

Async · permit · private mempool

The hard stuff is an option. Concurrency via the async client, EIP-2612 permit instead of approve, and MEV-protected private relays.

advanced.ts
// Async (concurrent quoting + simulation)
import { AsyncSallyClient } from "sally-defi-ts-sdk/aio";
import { Base } from "sally-defi-ts-sdk";
const sally = new AsyncSallyClient("base", RPC, { privateKey: "0x…" });
await sally.swap.execute(Base.WETH, Base.USDC, 10n ** 18n, { slippageBps: 50 });

// EIP-2612 permit instead of approve
await sally.swap.execute(Base.USDC, Base.WETH, 10n ** 8n, { approval: "permit" });

// Private mempool (MEV protection)
import { PrivateRelays } from "sally-defi-ts-sdk";
new SallyClient("base", RPC, { privateKey: "0x…", privateRpcUrl: PrivateRelays.FLASHBOTS_FAST });

Full guide: docs/advanced.md (async client, EIP-2612 permit, Permit2, private mempool, BSC).

Bring your own wallet

The SDK never has to hold your key

Give a private key, a mnemonic, or no key at all — build a vetted, simulated, ABI-decoded unsigned transaction and sign it with your own ethers wallet.

external-signing.ts
// mnemonic seed phrase
const sally = new SallyClient("base", RPC, { mnemonic: "word1 … word12", accountIndex: 0 });

// external signing — the SDK never holds your key
const ext = new SallyClient("base", RPC, { address: "0xYourWallet" });   // knows sender, no key
const build = await ext.swap.execute(Base.WETH, Base.USDC, 10n ** 18n, { buildOnly: true });
build.plan.summary();       // what happens to your wallet: out, min, impact, safe?
build.needsApproval;        // sign build.approveTx first if true
build.swapTx;               // unsigned tx → sign with your own account, broadcast

// preview ANY call: decoded fn + args + simulated result + revert reason
(await ext.preview(fn, args)).summary();
AI agents. Every call returns a JSON-able typed object with a clear doc comment, so wrapping Sally as LLM tools is trivial. See docs/ai-agents.md and examples/10_ai_agent_tools.ts.
Reference

Docs, examples & deployment

The SDK targets the live v2.1.0 deployment — the same 0x7777 proxies documented in the contract reference. Same address on Base and BSC.

Live deployment (v2.1.0, Base = BSC)
SwapController (proxy)0x7777D0e2…87777LiquidityController (proxy)0x7777d7fF…e7777Treasury0x7777D7dC…887777

Full addresses + ABIs in the contract reference.

Examples & support

Runnable scripts in examples/ (run with npx tsx). Questions, integrations, partnerships → support@sally.tools.

Prefer Python?

The same safe-by-default client ships for Python — pip install sally-defi-py-sdk, one typed SallyClient, identical surface.

Read the Python docs →

MIT-licensed and open source on GitHub ↗ · published on npm ↗. See the contract reference, the security overview, or build a no-code embed widget.

Command palette

Jump to a page or run an action