@keep-coffee/sdk
v0.2.0
Published
TypeScript SDK for Keep — non-custodial, refundable onchain fundraising on Solana, built for AI builders and agents.
Maintainers
Readme
@keep-coffee/sdk
Non-custodial, refundable onchain fundraising on Solana — built for AI builders and agents.
Keep lets a builder raise USDC from their users against a fixed target. The SDK builds the transactions; your wallet or agent signs them. The SDK never holds a key and never touches funds — backers pay the contract directly.
0.1.2— live on Solana mainnet (Keep program v2.4). Pre-1.0: the API may still evolve. Every instruction is byte-checked against the on-chain program.
Why it exists
- Non-custodial. Backers pay the on-chain program, not a platform wallet. We never hold or move a cent.
- Refundable on the failure paths only. If a raise doesn't fill, every backer is refunded in full. If it later fails its on-chain price check, the contract refunds them automatically — ~95% at day 7, ~85% at day 30 — funded by the protocol mechanism itself, never a balance sheet.
- A successful raise is a normal open market afterward — not principal-protected.
Install
npm i @keep-coffee/sdk @solana/web3.jsQuickstart
import { Connection, Keypair } from '@solana/web3.js';
import { KeepClient } from '@keep-coffee/sdk';
const connection = new Connection('https://api.mainnet-beta.solana.com', 'confirmed');
const keep = new KeepClient({ network: 'mainnet', connection });
const wallet = Keypair.fromSecretKey(/* your secret key */); // or any wallet adapter / agent signer
// Read a live raise.
const raise = await keep.getRaise(0);
console.log(raise.state, raise.totalRaised, raise.raiseTarget);
// Back a raise during fundraising (fixed-price, refundable on the failure paths).
const tx = await keep.back(0, { amount: 50_000_000n, backer: wallet.publicKey }); // 50 USDC
const sig = await connection.sendTransaction(tx, [wallet]);Every write method returns an unsigned Transaction.
You add your signer — a Keypair, a browser wallet, or an agent's signer — and send it.
Core API
// Reads
keep.getRaise(projectId) // a raise's full on-chain state
keep.listRaises() // all raises (decoded; unreadable slots skipped)
keep.listRaises({ strict: true }) // ...or throw if any slot can't be read
keep.listRaisesDetailed() // { raises, errors[] } — why each slot was skipped
keep.getPosition(projectId, wallet) // a backer's deposit position
keep.getClaimPool(projectId) // the failure-path refund pool
keep.getFactory() // global config + next project id
keep.getRiskProfile(projectId) // agent-friendly action/risk summary
keep.quote(projectId, { side, amountIn }) // expected swap output (to set minOut)
// Start a raise (a builder deploys a launchpad)
keep.createRaise({ owner, mint, name, symbol }) // sign with [owner, mintKeypair]
keep.setMetadata(projectId, { owner, name, symbol, uri }) // Metaplex name/logo
keep.setWhitelistRoot(projectId, { owner, root }) // Private/Hybrid allowlist
// Back, refund, claim (Keep-native, refund-protected)
keep.back(projectId, { amount, backer }) // fund a raise at the fixed price
keep.claimRefund(projectId, { backer }) // refund on a failure / cancel path
keep.claim(projectId, { backer }) // collect your tokens on success
// Trade the graduated market
keep.buy(projectId, { usdcIn, minTokenOut, trader }) // open-market swap (Raydium)
keep.sell(projectId, { tokenIn, minUsdcOut, trader }) // Keep-native in hold, Raydium afterStarting a raise mints a new token, so sign the returned transaction with both the owner and the mint keypair:
import { Keypair } from '@solana/web3.js';
const mint = Keypair.generate(); // optionally vanity-ground to a *keep suffix
const { transaction, projectId } = await keep.createRaise({
owner: owner.publicKey, mint: mint.publicKey, name: 'My Project', symbol: 'MYP',
});
await connection.sendTransaction(transaction, [owner, mint]);Every write method returns an unsigned Transaction.
back and buy are different actions, deliberately kept distinct:
backhappens during the raise, at a fixed price, and carries the failure-path refund protection above.buyis an ordinary open-market swap after the token is trading. It has no refund protection — it's a normal market trade.
The backer's token account is created at back time (keep it)
keep.back(...) returns two instructions: a leading
createAssociatedTokenAccountIdempotent for the backer's project-token account,
then the deposit. Keep the ATA-create — the on-chain program does not
create it (Anchor's in-instruction ATA init overflowed the SBF stack frame), and
on success the keeper's automatic token distribution transfers straight into that
ATA. The backer pays its rent; the create is idempotent, so it's a no-op if the
account already exists.
If you assemble the deposit transaction yourself and drop the ATA-create (or the
backer later closes the empty account), that backer is simply skipped by the
automatic push and must call keep.claim(projectId, { backer }) themselves —
claim recreates the ATA (backer-funded) and pulls their tokens. Funds are
never lost, but automatic, zero-click delivery only happens when the ATA exists
at distribution time. The keeper never creates ATAs on your behalf.
Networks & versioning
Mainnet only. This SDK targets the Keep program v2.4 and follows semantic
versioning — a breaking program upgrade ships as a new SDK major. Reads stay
forward-compatible with append-only account growth. For advanced or internal
testing against another deployment, pass a custom config to KeepClient.
Use it from an agent
The SDK builds transactions; your agent's signer sends them — so it drops into any
agent framework. Full LangChain tools are in
examples/langchain-tools.ts:
import { DynamicStructuredTool } from '@langchain/core/tools';
import { Connection, PublicKey } from '@solana/web3.js';
import { KeepClient } from '@keep-coffee/sdk';
import { z } from 'zod';
const keep = new KeepClient({ network: 'mainnet', connection: new Connection(RPC) });
const backTool = new DynamicStructuredTool({
name: 'keep_back',
description: 'Build an unsigned transaction to back a Keep raise with USDC.',
schema: z.object({ projectId: z.number(), usdc: z.string(), backer: z.string() }),
func: async ({ projectId, usdc, backer }) =>
(await keep.back(projectId, { amount: BigInt(usdc), backer: new PublicKey(backer) }))
.serialize({ requireAllSignatures: false }).toString('base64'),
});For plain-Node usage, see examples/read-and-back.ts.
Agent-launched raises
An agent can create a raise on keep.coffee from a wallet it controls. The protocol does not need a separate "agent identity" path: the wallet that signs is the creator wallet.
The SDK builds the transaction; the agent signs with:
- the creator wallet keypair
- a fresh mint keypair for the new project token
Plain Node example:
KEYPAIR=./agent-wallet.json \
PROJECT_NAME="My Agent Project" \
PROJECT_SYMBOL=AGT \
npx tsx examples/agent-create-raise.tsThe example dry-runs by default. To submit the transaction:
SEND=1 \
KEYPAIR=./agent-wallet.json \
PROJECT_NAME="My Agent Project" \
PROJECT_SYMBOL=AGT \
npx tsx examples/agent-create-raise.tsFor tool-calling agents, examples/langchain-tools.ts exports
keep_create_raise. It returns a base64 unsigned transaction plus the assigned
project id and launchpad address. The caller must sign with both the owner wallet
and the mint keypair before sending.
Agent action map
Agents should call getRiskProfile(projectId) before choosing a money action.
It returns structured fields such as phase, canBack, canClaimRefund,
canClaim, refundPath, refundBpsNow, and marketTradeRefundProtected.
Recommended flow:
const profile = await keep.getRiskProfile(projectId);
if (profile?.canBack) {
// Fixed-price funding during the raise; failure-path refund protection applies.
const tx = await keep.back(projectId, { amount, backer });
}
if (profile?.canClaimRefund) {
// Cancelled, expired, Failed1, or Failed2 path.
const tx = await keep.claimRefund(projectId, { backer });
}
if (profile?.canClaim) {
// Successful/trading path: claim tokens, or quote + buy/sell on the market.
const quote = await keep.quote(projectId, { side: 'buy', amountIn: usdcIn });
const tx = await keep.buy(projectId, { usdcIn, minTokenOut, trader });
}The bundled LangChain example exposes these tools:
keep_get_raisekeep_get_risk_profilekeep_quotekeep_backkeep_claim_refundkeep_claimkeep_buykeep_sellkeep_create_raise
All write tools return unsigned base64 transactions. Your wallet, backend signer, or authorized agent signer remains responsible for signing and sending.
Reusable agent test dashboard
To avoid one-off test scripts, the SDK includes a local dashboard for repeatable agent checks:
npm run agent:dashboardOpen the printed local URL and click Run tests. The dashboard checks:
- SDK build
- TypeScript typecheck
- structural tests
- live mainnet read decode
getRiskProfile(0)read path- local agent wallet readiness across Solana devnet, RH testnet, and Arc testnet
Reports are written to .keep-test-runs/latest.json and
.keep-test-runs/latest.html, so every run leaves a local artifact that can be
reviewed later or compared across conversations.
For a terminal-only run:
npm run test:agentUse KEEP_RPC=... to override the Solana RPC and KEEP_AGENT_WALLET_DIR=... to
point the wallet check at another local test-wallet directory. The dashboard
reads wallet files only to derive public keys and balances; it does not submit
transactions.
Resources
- Product: keep.coffee
- Issues & questions: github.com/keep-coffee/keep-sdk/issues
License
MIT — see LICENSE.
