@gramstore/agent
v0.1.4
Published
Agent SDK for gram.store — scan TON token auctions, place bids, and trade post-graduation tokens programmatically.
Readme
@gramstore/agent
Agent SDK + MCP server for gram.store token auctions on TON.
AI agents can participate in bonding-curve auctions fully on-chain — bids are plain internal messages to the auction contract, so an agent only needs a funded TON wallet. This package provides the convenience layer: auction discovery, consolidated state, transaction builders, signing helpers, and post-graduation trading via the Omniston aggregator (STON.fi and other resolvers).
Everything here is non-custodial. The agent (or the user's agentic wallet) signs and broadcasts; gram.store never sees private keys.
Install
npm install @gramstore/agent
# or from this repo:
npm install ./sdkQuick start
import { GramAgent, MnemonicW5Signer } from "@gramstore/agent";
const signer = await MnemonicW5Signer.create({
mnemonic: process.env.AGENT_MNEMONIC!,
network: "mainnet",
});
const agent = new GramAgent({
signer,
tonApiKey: process.env.TONCENTER_API_KEY, // recommended for polling
});
// 1. scan
const auctions = await agent.listAuctions("auction");
// 2. bid (market) or limit bid with a max price per token
for (const a of auctions) {
if (!a.contractAddress) continue;
const { chain, position } = await agent.getAuction(a.contractAddress);
if (!chain.goalReached && (position?.committedTon ?? 0) === 0) {
await agent.bid(a.contractAddress, 0.5, { maxPriceTon: chain.floorPriceTon * 2 });
}
}
// 3. after graduation — trade the TEP-74 jetton
const live = await agent.listAuctions("live");
if (live[0]?.tokenAddress) {
await agent.buyToken(live[0].tokenAddress, 0.2);
}See examples/agent.ts for a full scan → bid → trade loop.
Concepts
Auction lifecycle
| Phase | What happens |
|---|---|
| scheduled | Contract deployed, window not started (owner sends StartBonding). |
| auction | Clearing periods run for the tier window (Blitz 4h/240×1m, Starter 24h/24×1h, Verified+Premium 72h/72×1h). Bids are accepted until the deadline. |
| sellout → graduation | All 72M contributor tokens allocated → goalReached, then Gram admin sends DeployDex (10% treasury / 10% owner / 80% LP), jettons mint to bidder wallets, LP goes live on STON.fi. |
| miss | Deadline passes without sellout → refunds open; bidders call Withdraw (or get auto-refunded). |
Bidding semantics (contract-level)
- A bid is an internal message to the auction contract carrying GRAM. Bidder = message sender. No approvals.
Commit(op0x636f6d6d) — market order; fills at any clearing price.Bid(op0x62696400+maxPrice:coins) — limit order;maxPriceis nanoton per whole token (1 token = 1e9 nano). Bids below the ratchetingmin_bid_priceare rejected.- Each bid's budget is spread across the remaining periods (
perPeriodBudget = amount / periodsLeft). To increase a position, send another bid — budgets accumulate per wallet. - Per-wallet cap:
tokensForSale / 36tokens (2M = 2% of the 100M total supply). The GRAM cap for a limit bid iscap_tokens * maxPrice; market bids are token-capped only. Withdraw(op0x77697468) — claim unfilled GRAM after sellout, or full refund after a miss.Checkpoint(op0x63686b70) — permissionless crank that clears elapsed periods. Anyone can send it; useful to force settlement right after the deadline.
REST API (v1, no auth)
| Endpoint | Purpose |
|---|---|
| GET /api/v1/meta | Protocol constants: supply, caps, tier windows, opcodes, get-methods |
| GET /api/v1/auctions?status=scheduled\|active\|ended\|auction\|live\|graduated\|all&tier=blitz\|starter\|verified\|premium | Auction discovery — default returns auctions only; each item carries phase (scheduled|active|ended|live — render it as the status tag), tier (badge: blitz|starter|verified|premium), committedTon/goalTon, progressPercent, committedUsers (bidders), origin and graduatedAt |
| GET /api/v1/auctions/{contractAddress}?wallet={addr} | Listing + fresh chain state + bidder position |
| GET /api/v1/auctions/{contractAddress}/bidders | Bidder feed + aggregates — bidderCount, totalCommittedTon, topCommittedTon (max bid), topMaxPriceTon, per-wallet committedTon. Live on-chain view (cached ~60s); ?source=index for the cheap indexed feed |
| GET /api/v1/auctions/{contractAddress}/chart | Auction chart — per-period min/clearing/avg-clearing price, cumulative tokens/GRAM, periodSeconds/periodCount schedule, gramUsd, plus the listing race line. Same data the gram.store UI draws |
| GET /api/v1/tokens/{tokenAddress} | Provenance lookup — resolves a jetton master to its gram.store listing (origin: "gram-store", contractAddress, poolAddress, graduatedAt); 404 when the token isn't ours. Lets agents tag gram.store-graduated tokens found on DEXes |
| POST /api/v1/auctions/{contractAddress}/tx | Unsigned tx builder: {action:"bid",amountTon,maxPriceTon?} / {action:"withdraw"} / {action:"checkpoint"} → {to, amountNano, bodyBase64, bounce, sendMode} |
| POST /api/tmas/{id}/commit | Record a broadcast bid for instant indexing (txHash = relaxed internal-msg BoC) |
| POST /api/tmas/{id}/commit-relay | Gasless bid: relay a wallet-signed internal message BoC (W5 internal_signed) |
| POST /api/wallet/ton-broadcast | Broadcast a signed wallet external BoC ({boc, walletAddress, publicKey}) |
| POST /api/wallet/swap-relay | Gasless swap relay (validated against the Omniston quote) |
Agents in any language can work against the REST API: GET discovery/state + POST .../tx for unsigned payloads; signing stays client-side.
Graduated tokens stay visible. ?status=live (or graduated) lists every token that graduated from a gram.store auction to STON.fi, GET /api/v1/tokens/{tokenAddress} resolves any jetton master back to its gram.store listing (404 = not ours), and every summary carries origin: "gram-store" + graduatedAt — so an agent scanning DEX pairs can tag our tokens. For "when this auction graduates, trade it", poll getAuction until listing.tokenAddress appears or just await agent.waitForGraduation(contractAddress) (gram_wait_graduation in MCP).
All endpoints above send Access-Control-Allow-Origin: * (OPTIONS preflights handled), so Telegram Mini Apps and browser agents can call them directly. Per-IP rate limits apply — 429 responses carry Retry-After and X-RateLimit-* headers; see GET /api/v1/meta → rateLimits for the budgets.
Signing: pluggable AgentSigner
interface AgentSigner {
readonly address: Address;
send(messages: AgentMessage[]): Promise<SentTx>;
}MnemonicW5Signer— a self-custodial Wallet v5r1 from a mnemonic. Good for a dedicated agent wallet.CallbackSigner— wrap anysendfunction:new CallbackSigner(agenticWalletAddress, async (msgs) => myAgenticWallet.send(msgs));- TON Agentic Wallets (docs.ton.org/onboarding/ai/wallets): split-key wallets where the user holds the owner key and the agent holds a revocable operator key — the recommended custody model for agents acting on behalf of users. Agentic wallets are managed by the official
@ton/mcpAgentKit. Because an auction bid is just an internal message from the agentic wallet, wire aCallbackSignerto the operator send path (or rungram-agent-mcpin build-only mode and let@ton/mcpbroadcast).
MCP server: gram-agent-mcp
Plug gram.store into Claude Desktop / Cursor / Windsurf / any MCP client:
{
"mcpServers": {
"gram-store": {
"command": "npx",
"args": ["-y", "-p", "@gramstore/agent", "gram-agent-mcp"],
"env": {
"TON_NETWORK": "testnet",
"TONCENTER_API_KEY": "...",
"AGENT_MNEMONIC": "optional 24 words — enables signing tools"
}
}
}
}Or run from this repo: cd sdk && npm install && npm run build && AGENT_MNEMONIC=... node dist/mcp.js.
Tools:
| Tool | Description |
|---|---|
| gram_meta | Protocol constants |
| gram_list_auctions | Scan auctions by status |
| gram_get_auction | Listing + on-chain state + position |
| gram_get_position | Bidder position |
| gram_bidders | Bidder feed (on-chain or indexed) |
| gram_build_bid | Unsigned bid tx (validated server-side) |
| gram_bid | Place/increase bid (needs signer) |
| gram_withdraw | Withdraw unfilled/refunded GRAM (needs signer) |
| gram_checkpoint | Send the permissionless clearing crank (needs signer) |
| gram_swap_quote | Omniston quote for a TON↔jetton swap |
| gram_buy_token / gram_sell_token | Execute a swap (needs signer) |
| gram_jetton_balance | TEP-74 balance lookup |
| gram_session | Guardrail state: wallet, spend vs cap, allowlists |
Guardrails (enforced in code, not the prompt)
- Spend cap —
AGENT_MAX_SPEND_TONcaps cumulative bids + token buys for the session; exceeding calls are refused with the remaining budget. In-process, so restart resets it — fund the wallet with only what the agent may spend as the first line of defense. - Allowlists —
AGENT_ALLOWED_AUCTIONS(auction contract addresses) restrictsbid/withdraw/checkpoint/build_bid;AGENT_ALLOWED_TOKENS(jetton masters) restricts swaps. Reads stay open so agents can still discover auctions. - Idempotent bids —
gram_bid,gram_buy_token,gram_sell_tokentake an optionalnonce; retrying with the same nonce returns the original result and cannot double-spend. On ambiguous send errors the agent re-checks the wallet's on-chain committed+spent before reporting failure.
Without AGENT_MNEMONIC the server runs in build-only mode: gram_build_bid returns {to, amountNano, bodyBase64} for the caller's wallet (e.g. an agentic wallet operated by @ton/mcp) to sign and send.
Direct on-chain access (no gram.store API)
Reads are plain get-methods via any toncenter-compatible endpoint:
const detail = await agent.getAuctionOnchain(contractAddress, walletAddress);Writes are internal messages — assemble them yourself if you prefer:
import { beginCell, toNano } from "@ton/core";
const body = beginCell().storeUint(0x62696400, 32).storeCoins(toNano("0.0004")).endCell();
// wallet.sendTransfer → internal message { to: auction, value: bidNano, body }Local sandbox (Acton simulator)
Develop and test agents without testnet or real GRAM. The repo ships
scripts/agent-sim-blitz.mjs, a "director" that stands up a full-format
Blitz auction (1,000 GRAM goal · 4h window · 240 one-minute periods) on the
local Acton simulator and plays a seeded random bidder scenario — market and
limit bids from ~60 funded wallets, adaptive repricing, chase bids, and
end-of-auction sweeps — as real on-chain messages, through sellout and the
10/10/80 raise split.
# terminal 1 — the chain
acton simulator start --db-path .acton/simulator/5411/sim.db
# terminal 2 — the app, pointed at the simulator
E2E_NETWORK=local NEXT_PUBLIC_TON_NETWORK=testnet \
TON_API_ENDPOINT=http://127.0.0.1:5411/api/v2/jsonRPC npm run dev
# terminal 3 — the auction
npm run sim:agent-blitzThen point your agent/SDK at the sandbox:
export GRAM_STORE_API=http://localhost:3000
export TONCENTER_ENDPOINT=http://127.0.0.1:5411/api/v2/jsonRPC
export TON_NETWORK=testnet
export AGENT_MNEMONIC="..." # any 24 words; fund via /acton_fundAccountDefaults run the 4-hour auction in real time, so agents see a live auction ticking period-by-period. Useful env knobs (see the script header for all):
SIM_TIME_SCALE=60— compress the window to ~4 minutes via sim-time jumpsSIM_SEED=<n>— replay a deterministic scenarioSIM_UNTIL=bids|sellout|dex— stop mid-flight so your own agents can bidSIM_BIDDER_COUNT,SIM_DEMAND_RATIO,SIM_DEMAND_SHAPE
Caveats: the on-chain /bidders feed scans TonAPI (absent on the simulator) —
use ?source=index, which the director populates through the real /commit
integrator path. The STON.fi pool leg is skipped on a fresh simulator;
acton simulator start --fork-net mainnet covers DEX deploy. If the sim
clock has been time-traveled ahead of wall time, real-time pacing can't work —
restart the simulator fresh or use SIM_TIME_SCALE.
Environment variables
| Var | Default | Purpose |
|---|---|---|
| TON_NETWORK | mainnet | mainnet / testnet |
| AGENT_MNEMONIC | — | W5 wallet mnemonic for signing |
| AGENT_SUBWALLET | 0 | v5 subwallet number (shard strategies per key) |
| TONCENTER_API_KEY | — | Higher rate limits on toncenter |
| TONCENTER_ENDPOINT | toncenter | Custom JSON-RPC endpoint |
| GRAM_STORE_API | https://gram.store | API origin override |
| AGENT_MAX_SPEND_TON | — | Session GRAM spend cap (bids + buys) |
| AGENT_ALLOWED_AUCTIONS | — | CSV of auction contracts allowed for writes |
| AGENT_ALLOWED_TOKENS | — | CSV of jetton masters allowed in swaps |
Notes & caveats
- Graduation = full sellout. Reaching the GRAM goal is not enough — all 72M contributor tokens must be allocated. Funded wallets can absorb the final residual at the last clearing price (the per-wallet cap is relaxed only in the final residual pass).
- Checkpoint gas. A
Checkpointcall that clears many periods at once is expensive. Cron checkpoints normally keep state fresh; agents should not rely on forcing a full-window clear in one message. - Refunds ≤ 0.01 GRAM are skipped by the contract (dust guard).
- Testnet faucet: https://t.me/testgiver_ton_bot
