client.state.getPool()/getPoolByBaseMint()/getPoolsByConfig()/getPoolsByCreator()
in @meteora-ag/dynamic-bonding-curve-sdk all return { poolState: VirtualPool },
not a flat VirtualPool. The existing code guessed at field names via
(pool as any).config || (pool as any).poolConfig-style fallbacks that always
resolved to undefined, so getDbcPoolStatus always reported isMigrated=false,
quoteReserve='0', and empty configAddress/creator regardless of real on-chain
state, and getDbcSwapQuote/V2 called getPoolConfig(undefined) on every
invocation.
Separately, swapQuote()/swapQuote2()'s `virtualPool` param is typed as a flat
VirtualPool in the .d.ts but is actually dereferenced internally as
`virtualPool.poolState.*` (confirmed by reading the shipped dist/index.js) —
the opposite unwrap direction from the pool-status fix. Passing the unwrapped
pool there throws "Cannot read properties of undefined (reading
'quoteReserve')"; passing the untouched wrapper is correct.
Also disabled getDbcPoolConfigs() (unfiltered client.state.getPoolConfigs()
call did not return within 30s live against mainnet — same failure class as
the pre-existing guard on getDbcPools()), pointing callers at
getDbcPoolConfigsByOwner instead.
All fixes verified live against real mainnet DBC pools, including an
end-to-end quote match between the V1 and V2 quote paths on an active
(non-migrated) pool.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Closes out all five items deliberately deferred from the previous audit PR.
1. fetchFeeConfig error swallowing (pumpapi.ts): .catch(() => null) treated
every failure identically, including transient RPC errors, silently
feeding a flat-rate fee fallback into real trade-instruction math.
Added fetchFeeConfigOrNull, which only returns null for Anchor's own
"Account does not exist" error (the genuine "no fee tiers configured"
case, verified against the exact error string Anchor's account fetcher
throws) — any other failure now propagates instead of being silently
swallowed. Live-verified fetchFeeConfig currently succeeds on mainnet
(fee tiers ARE configured), confirming the happy path is unaffected.
2. Deprecated create methods (agents/handlers/solana.ts, pumpfun skill):
migrated createInstruction/createAndBuyInstructions (both @deprecated
in the SDK) to createV2Instruction/createV2AndBuyInstructions. Note the
V2 path always mints under Token-2022 regardless of the mayhemMode flag
(that flag only toggles Mayhem game mechanics) — passing
mayhemMode: false here is a normal, non-Mayhem Token-2022 launch,
matching what pump.fun's own V2 creation flow produces. Live-verified
both createV2Instruction and createV2AndBuyInstructions build correct
instructions against mainnet.
3. metadata_uri validation (same two files): added validateMetadataUri,
checking URL shape, http(s) scheme, reachability, and that the fetched
JSON has the name/symbol fields pump.fun expects — called before any
RPC calls or transaction building, so a bad URI is rejected before
spending real SOL rather than after silently minting a token with
permanently broken metadata. Live-verified all rejection paths
(malformed URL, wrong scheme, unreachable host, valid-but-incomplete
JSON) plus the happy path against a local test server.
4. PumpSwap compute-unit headroom (pumpswap.ts, swarm-builders.ts's
PumpSwapBuilder): raised the hardcoded compute unit limit from 200k to
400k for PumpSwap trades specifically (left the simpler bonding-curve
trade paths at 200k — the worst-case-bundle concern is specific to
PumpSwap's extendAccount + ATA + WSOL wrap/close + boost-account
combinations). Confirmed this doesn't change actual lamports spent on
priority fee: microLamports is derived as
priorityFeeLamports/computeUnitLimit, so the product
(microLamports * computeUnitLimit) stays equal to the caller's
requested priorityFeeLamports regardless of the limit chosen — raising
it only adds headroom, not cost.
5. Blockhash-freshness mismatch (wallet.ts): signAndSendTransaction's
VersionedTransaction branch was fetching a second, newer blockhash
purely to get a lastValidBlockHeight for confirmTransaction, rather
than the height actually corresponding to the blockhash baked into the
already-signed transaction. Added an optional lastValidBlockHeight
parameter threaded through from the two direct pump.fun callers
(executePumpFunTrade, executePumpSwapTrade), which already have the
real value on hand from their own parallelized getLatestBlockhash call.
Falls back to the old fetch-fresh behavior for every other caller of
this shared helper, so this is fully backward compatible.
Live-verified end-to-end: both executePumpFunTrade and executePumpSwapTrade
(with priority fees set, exercising the new CU limits and blockhash
threading together) build and submit correctly, failing only at
on-chain simulation due to the test wallet's zero balance — not at
build time.
Co-authored-by: alsk1992 <alsk1992@users.noreply.github.com>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Ran a final audit comparing pumpapi.ts/pumpswap.ts/the pump.fun create-claim
handlers against the actual pinned SDK source (node_modules/@pump-fun/*/src,
including @pump-fun/pump-swap-sdk's own test specs) rather than against
memory of what a typical Solana SDK looks like. Three parallel passes
covering trading, PumpSwap, and create/claim all independently converged on
the same critical issue plus one each found a real high-severity bug.
CRITICAL — wallet.ts's shared signAndSendTransaction/
signAndSendVersionedTransaction never checked confirmTransaction's
result.value.err. confirmTransaction only THROWS on expiry/timeout/
nonce-invalidation — a transaction that lands on-chain but whose program
instruction reverts (wrong slippage, insufficient funds, graduated
mid-flight, whatever) resolves normally, with the failure reported solely
via .value.err. Every caller was silently treating "landed" as "succeeded."
This is the single highest-leverage fix in the repo: these two functions
are used by jupiter.ts, kamino.ts, meteora.ts, meteora-dbc.ts, marginfi.ts,
orca.ts, pumpapi.ts, pumpswap.ts, raydium.ts, and solend.ts — every one of
them now gets this check for free. Added the same check inline to the
pump.fun create/claim handlers (agents/handlers/solana.ts,
skills/bundled/pumpfun/index.ts), which had their own duplicated
confirmTransaction calls that didn't go through the shared helper.
Also added a getCreatorVaultBalanceBothPrograms pre-check before claiming —
a zero-balance claim now returns a clear "nothing to claim" message instead
of a cryptic simulation error.
HIGH — getPumpSwapQuote (pumpswap.ts) passed state.pool.coinCreator where
the SDK's own pure quote functions need state.pool.creator — two distinct
Pool fields. This made isPumpPool() evaluate false for virtually every real
pool, silently routing every quote into the wrong (flat, not tiered) fee
branch. Numerically verified: ~2.57% quote error on a real fee schedule.
Confined to the quote path — executePumpSwapTrade/PumpSwapBuilder use the
SDK's own instance methods, which get pool.creator right internally, so
actual trade execution and its on-chain slippage bound were never affected.
Also added the missing virtualQuoteReserves field to the same two calls,
which the SDK's own instance methods pass but the standalone quote calls
were omitting — matters for boost/cashback-enabled pools.
MEDIUM-HIGH — getTokenBalance/getUserPumpTokens (pumpapi.ts) never
detected Token-2022 (Mayhem-mode tokens), always deriving/querying against
classic SPL Token. A Mayhem-mode holding would silently report as a zero
balance or be omitted from the holdings list entirely — inconsistent with
the trading path, which already handles this correctly via
detectTokenProgram. Fixed both to detect/query the real token program.
Known lower-severity findings from the same audit, deliberately left for a
follow-up rather than rushed in under time pressure: fetchFeeConfig()'s
.catch(() => null) doesn't distinguish "no fee tiers configured" from "RPC
call failed" (bounded by slippage, not a fund-loss risk); create/
createAndBuy use SDK methods marked @deprecated in favor of V2 variants
(works today, confirmed against the current IDL, but no compile-time
warning if pump.fun retires the legacy path); metadata_uri is never
validated before spending real SOL on token creation; PumpSwap's
hardcoded 200k compute unit limit could be tight in a worst-case trade
needing extendAccount + ATA creation + WSOL wrap + boost accounts
simultaneously; wallet.ts's VersionedTransaction confirm path fetches a
second, later blockhash purely for lastValidBlockHeight rather than the
one actually baked into the already-signed transaction, which can make
confirmTransaction poll longer than necessary in a genuine-expiry case
(latency cost, not a correctness bug — verified the direction is
fail-safe, not fail-open).
Co-authored-by: alsk1992 <alsk1992@users.noreply.github.com>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Measured (not assumed) what actually costs time on the trade-execution
path once PumpPortal was already removed: new PumpSdk() takes ~19.5ms/call
and new OnlinePumpSdk(connection) ~33ms/call — both reconstruct Anchor
Program instances (instruction/account Borsh coders) from the Pump
program's IDL from scratch on every single invocation, which was
happening once per trade. That's ~53ms of pure CPU overhead before any
network call even starts, on a path where every millisecond determines
whether a snipe/buy lands first.
Two fixes:
- new PumpSdk() -> the package's own exported PUMP_SDK singleton (its
constructor takes no connection, so there's no reason not to reuse the
package's instance directly — zero cost after the package's own module
load).
- new OnlinePumpSdk(connection) / new OnlinePumpAmmSdk(connection) ->
cached by connection.rpcEndpoint URL, not object identity.
wallet.getSolanaConnection() returns a fresh Connection object on every
call rather than reusing one, so an object-identity cache would never
hit; keying by the endpoint URL (what actually determines whether two
calls talk to the same node) does.
Also removed redundant sequential RPC round-trips that don't need to be
sequential: fetchGlobal/fetchFeeConfig depend on neither tokenProgram nor
the bonding-curve/pool state fetch (or each other), and
getLatestBlockhash depends on nothing instruction-building computes —
these were awaited one after another purely due to code order, not any
real data dependency. Now issued concurrently via Promise.all wherever
the dependency graph actually allows it, in
buildPumpFunTradeInstructions, getPumpFunQuote, executePumpFunTrade,
PumpFunBuilder, PumpSwapBuilder, and executePumpSwapTrade.
Live-verified against mainnet: cold call (cache empty) 333.8ms, warm call
(cache populated) 48.8ms — a 6.8x speedup — and a third call using a
brand-new Connection object pointed at the same RPC URL still hit the
cache (51.8ms), confirming the endpoint-keyed cache does what
getSolanaConnection()'s per-call Connection construction actually needs.
Quote output verified unchanged/correct after the restructuring.
Also applied the trivial, zero-risk PUMP_SDK swap to the create/claim
handlers (agents/handlers/solana.ts, pumpfun skill) even though token
creation isn't hot-path — free to fix, no reason not to.
Co-authored-by: alsk1992 <alsk1992@users.noreply.github.com>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Trading (buy/sell/quote) was already migrated off PumpPortal in prior
commits. This finishes the job for every other capability that still
touched pumpportal.fun:
- Token creation (agents/handlers/solana.ts, pumpfun skill): now builds
and signs create/createAndBuy instructions locally via
@pump-fun/pump-sdk's PumpSdk. Metadata hosting (name/symbol/description/
image -> URI) is now the caller's responsibility (metadata_uri param) —
pump.fun's own IPFS upload endpoint is Cloudflare-gated against
server-side/bot requests (verified live, same protection as their
trading frontend API), so there's no reliable official endpoint this
codebase can depend on for that step either.
- Creator fee claiming (same two files): now uses OnlinePumpSdk's
collectCoinCreatorFeeInstructions, which claims fees from both the
bonding-curve program and PumpSwap in one transaction. This is keyed by
creator address, not mint, so the mint parameter these tools used to
require is gone.
- Renamed getPumpPortalQuote/PumpPortalQuote to getPumpFunQuote/
PumpFunQuote (was already 100% local SDK computation, just kept the old
name from the earlier trading migration).
- /pump watch and /pump snipe were fake capabilities — static instructional
text pointing users at PumpPortal's websocket to do it themselves, not
actually implemented. /pump watch is now a real bounded-window
connection.onLogs listener. /pump snipe is honestly marked as not an
auto-trading command in this one-shot request/response tool, pointing
at the pump-swarm skill / copytrade.ts (persistent processes) instead
of fabricating a rushed auto-buy feature.
- Real correctness fix surfaced along the way: PumpPortal's `pool` param
used to route trades across other DEXes entirely (raydium, launchlab,
bonk) via its own aggregation — not just Pump's bonding curve. The
earlier SDK-based rewrite silently ignored that param instead of
erroring, meaning `--pool raydium` would silently execute a Pump.fun
bonding-curve trade instead of the requested route. Added
assertSupportedPumpPool, called from every entry point (single-wallet
trade, swarm builder, quote), which now fails loudly for any pool value
this codebase doesn't actually support instead of silently trading
something different than requested.
- Updated all doc/help text (SKILL.md files, inline help strings, tool
schemas) to match.
Live-verified: assertSupportedPumpPool correctly blocks execution before
any network call for an unsupported pool, and getPumpFunQuote returns
null (not a silently-wrong quote) for the same case.
Co-authored-by: alsk1992 <alsk1992@users.noreply.github.com>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
pumpswap.ts already quoted PumpSwap trades (the AMM a pump.fun token
graduates to) via the official @pump-fun/pump-swap-sdk, but had no
instruction-building/execute path — a graduated token was not tradeable
through this codebase at all. Adds executePumpSwapTrade, built on the same
SDK (PUMP_AMM_SDK.buyQuoteInput/sellBaseInput), reusing the exact
pool-selection logic (findBestPumpSwapState, extracted from
getPumpSwapQuote) so a quote and the trade it's for route to the same pool.
Native SOL wrapping/unwrapping and ATA creation are handled by the SDK's
own instruction builder.
Also wires PumpSwap into the multi-wallet swarm layer: new PumpSwapBuilder
in swarm-builders.ts (same shape as PumpFunBuilder — buildBuyTransaction/
buildSellTransaction/getQuote), and a new 'pumpswap' DexType value.
Live-verified against mainnet: PUMP's own token (63 real pools) quoted
and built a real buy transaction end-to-end — failed only at simulation
due to the test wallet having zero funds, confirming instruction
construction itself is correct.
Adding 'pumpswap' to the shared DexType union surfaced four separate
files (pump-swarm.ts, swarm-strategies.ts x2, swarm-ai-builder.ts) that
had independently duplicated the same dex/executionMode literal unions
inline instead of importing the shared types — TypeScript's structural
checks caught the resulting incompatibilities immediately. Fixed each to
reference the shared DexType/ExecutionMode types instead of maintaining
a fifth copy.
Co-authored-by: alsk1992 <alsk1992@users.noreply.github.com>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Both the single-wallet path (executePumpFunTrade) and the multi-wallet
swarm path (PumpFunBuilder) built and relayed trades through PumpPortal's
third-party trade-local API. Replaced both with buildPumpFunTradeInstructions,
a shared helper that builds real bonding-curve buy/sell instructions locally
via the official @pump-fun/pump-sdk — no third-party API round-trip for
either constructing or relaying the transaction.
Verified live against mainnet during development (real trading pair,
BPmsvKnjXXJpkYcnJsjFd6VBBfreJ81uJf1SbX5Lpump): the SDK's README examples are
stale relative to the published v1.36.0 API (wrong PumpSdk constructor arity,
wrong getBuyTokenAmountFromSolAmount signature) — went to source (sdk.ts,
onlineSdk.ts, bondingCurve.ts, fees.ts) to get the real shapes instead of
trusting the docs. Also surfaced that this specific token uses Token-2022,
not classic SPL Token — token program is now detected per-mint via the mint
account's owner rather than assumed, and that the real fee-tier-aware
protocol fee (measured live at 100bps total) differs from the flat 125bps
this codebase had hardcoded as an assumption.
Also fixes the swarm's PumpFunBuilder.getQuote, which previously hit
frontend-api-v3.pump.fun directly (Cloudflare-blocks server-side/bot
requests) — now computed locally via the same SDK path, and adds
client-side priority-fee compute-budget instructions since PumpPortal
is no longer relaying (and therefore no longer applying priority fee
server-side).
Known remaining scope, not touched here:
- Token creation and creator-fee-claiming (agents/handlers/solana.ts,
skills/bundled/pumpfun) still call PumpPortal's /api/create and
/api/claim-fees — separate feature from trading, left as-is.
- PumpSwap (post-graduation AMM) already quotes via the official
@pump-fun/pump-swap-sdk (src/solana/pumpswap.ts) but has no execute/swap
instruction builder yet, and swarm-builders.ts's DexType has no 'pumpswap'
entry — a graduated token is not yet tradeable through this codebase.
Co-authored-by: alsk1992 <alsk1992@users.noreply.github.com>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Adds Robinhood Chain (EVM L2, chainId 4663) as a supported chain, and
trading support for both live generations of the Pons Family launchpad
that runs on it:
- V1: CREATE2-launched fixed-supply tokens trading on a per-launch
Uniswap V3 pool. DEX config (factory/router/pool fee) is resolved
live per-launch rather than hardcoded, since Robinhood Chain's V1
deployment uses a custom (non-canonical) Uniswap V3 fork. No
Quoter/QuoterV2 contract for that fork could be found published
anywhere, so quoting reads the pool's slot0() sqrtPriceX96 directly
(spot price — ignores tick-crossing impact on large trades).
- V2: bonding-curve pre-graduation trading (native ETH or ERC-20 quote
asset), constant-product math matching the curve's own formula. The
anti-snipe tax is priced via the curve's live currentSnipeTaxBps(recipient)
view rather than a reconstructed decay formula — the published
contractsV2 source doesn't document the decay curve, but the live
contract exposes it directly and per-recipient (respecting exemptions).
Both factory addresses and the V1 custom-fork factory address were
verified against live deployed bytecode (eth_getCode) rather than
trusted from source alone; the V2 curve ABI was cross-checked against
a production trading bot's interface since the published GitHub source
for PonsV2BondingCurve.sol lags the actual live deployment.
Wires in via the existing src/evm barrel export, plus new pons_quote /
pons_trade agent tools that auto-detect V1 vs V2 per token.
Co-authored-by: alsk1992 <alsk1992@users.noreply.github.com>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
85 -> 62 production vulnerabilities (52 moderate, 9 high, 1 critical remain,
all allowlisted below) via two npm audit fix passes plus manual major-version
bumps for nodemailer (7 -> 9) and sharp (0.34 -> 0.35), chosen because both
have narrow, stable-surface usage in this codebase (createTransport/sendMail;
a basic resize/metadata chain) verified before bumping.
The sharp bump broke its own TS types in two places, both fixed:
- src/media/index.ts: sharp's ESM types now separately export a named
SharpConstructor and a default rather than making the module namespace
itself callable, breaking `typeof import('sharp')` as a cast target.
Replaced with a small local SharpFactory type describing only what this
file actually calls (resize/toFormat/toBuffer/metadata) — sharp has now
changed its export shape at least once, so pinning to its exact type
surface here was already fragile.
- src/extensions/open-prose/index.ts: an unrelated puppeteer version shift
(pulled in transitively during the audit-fix passes) dropped 'networkidle0'
as a valid page.setContent() waitUntil value (still valid for page.goto(),
just not setContent()) — switched to 'load', the correct equivalent for
rendering already-inlined HTML.
New audit-ci.jsonc allowlists the remaining findings with per-advisory
justification: almost all are protobufjs/@grpc-js/uuid pulled in transitively
by the Solana SDK ecosystem's own pinned old @solana/web3.js/@coral-xyz/anchor
versions (Drift, Orca, Kamino, Raydium, etc.) — none fixable without either
downgrading a trading integration this session verified working, or forcing
a major bump whose breaking changes (e.g. @drift-labs/sdk needing Node 24)
haven't been vetted. sharp's one remaining finding is an inherited libvips
CVE with no fix published yet even on the latest release just installed here.
ci.yml and security.yml now run `audit-ci --config audit-ci.jsonc` instead of
plain `npm audit`, since plain npm audit has no allowlist mechanism at all —
without this, the allowlist above would have no effect on actual CI results.
placeOrder/cancelOrder/cancelAllOrders/getBalance/getPositions/getOpenOrders
all called a plain X-API-Key REST API that doesn't match how Lighter (a
zk-rollup) actually works: every order/cancel is an L2 transaction, signed
by a native binary the official SDK loads via ctypes (compiled from
github.com/elliottech/lighter-go) — there's no published algorithm spec to
reimplement in TypeScript, and Lighter's own official AI-agent integration
(elliottech/lighter-agent-kit) works the same way: Python calling the real
SDK, not a from-scratch reimplementation in the host language.
New scripts/lighter-bridge/bridge.py wraps the official lighter-sdk (pinned
to a specific commit, matching elliottech/lighter-agent-kit's own lockfile),
invoked as a subprocess from src/exchanges/lighter/index.ts — one JSON
request in on stdin, one JSON result out on stdout, mirroring the existing
Rust fast-broadcast subprocess pattern (src/evm/fast-broadcast.ts) so both
"shell out to a real, verified implementation" paths in this codebase look
and behave the same way. Lazily pip-installs into a local .vendor/ dir on
first use, so nothing needs a pre-existing venv.
Verified live against mainnet: the bridge reaches Lighter's real API,
correctly loads the native signer, and gets back a genuine server response
("invalid account index" for a made-up test account) — proving the full
chain (bootstrap -> native signer load -> key validation -> live network
call) works end to end.
Also rewrote LighterOrder/LighterPosition/LighterBalance to match the real
API response shapes (verified against elliottech/lighter-agent-kit's
schemas-read.md) instead of the old guessed shape, and fixed several stale
"Lighter orderbook DEX on Arbitrum" doc/description strings — Lighter is its
own standalone zk-rollup, unrelated to Arbitrum.
V1 and V2 exchanges are both live simultaneously on-chain (verified), but GET /version
on the live CLOB currently returns 2 — V2 is the exchange-wide default order format
right now, and the codebase only ever signed V1-shaped orders.
- New V2 order signer (src/utils/polymarket-order-signer.ts): V2's Order struct drops
taker/expiration/nonce/feeRateBps and adds timestamp/metadata/builder — verified
against ctf-exchange-v2's Structs.sol/Hashing.sol and clob-client-v2's
exchangeOrderBuilderV2.ts (both agree byte-for-byte on the EIP-712 type string).
Proven byte-identical to an independent ethers EIP-712 signature across both the
standard and negRisk exchange contracts (see the new byte-parity test).
- getCurrentPolymarketOrderVersion() queries GET /version (5-min cache, falls back to
2 on failure, matching the official client's own fallback) so order placement
auto-detects V1 vs V2 rather than hardcoding one.
- execution/index.ts: both single and batch order placement now dispatch to the
resolved version's signer.
- Extended USDC/CTF approvals (approveUSDC/getUSDCAllowance and the onboarding
getRequiredApprovals/checkAllApprovals) to cover all four exchange contracts (V1 +
V2, standard + negRisk) — a correctly V2-signed order would otherwise fail at
settlement from an allowance that only ever covered the V1 exchange.
- V2's POLY_1271 (smart-contract wallet) signature type needs a different nested
TypedDataSign wrapper, not a plain EIP-712 signature — intentionally not
implemented; buildSignedOrderV2 throws a clear error if it's requested.
Verified: typecheck clean, 172/172 tests passing (3 new), V1 signing path untouched.
api.lighter.xyz doesn't resolve to the live API; the real host is
mainnet.zklighter.elliot.ai. Verified market-data path (getMarkets/
getOrderbook/subscribeOrderbook) live against the real API. Account/trading
endpoints were left as-is — Lighter's real trading flow requires client-side
L2 transaction signing, not a bearer-token REST call, so those are almost
certainly still wrong; flagged in a comment as out of scope for this pass
(market data only).
POST/DELETE /portfolio/orders* (and /batched, /amend) now return HTTP 410
deprecated_v1_order_endpoint (verified live) — order placement, cancellation,
batch operations, and amend have been failing since the sunset date. Rewrote
all five operations against the v2 schema (verified against
docs.kalshi.com/api-reference/orders/* and live validation-layer responses):
- Endpoints move under /portfolio/events/orders* (single + /batched + /amend).
GET (order listing) is unaffected — confirmed still returns 401, not 410.
- The book is YES-referenced: side is bid/ask (not yes/no + separate
buy/sell action), with one price field instead of yes_price/no_price. A NO
order maps onto the YES book (bid iff is_buy == is_yes; price flips to
1 - price for NO). count/price are now fixed-point decimal strings, and
self_trade_prevention_type is a new required field.
- Responses are flat (no `order` wrapper) and omit `status` entirely — it's
now synthesized client-side from fill_count/remaining_count, matching
Kalshi's documented semantics.
- amend now requires the full new order state (ticker, side, price, count)
rather than a partial patch, so it gained a ticker parameter.
Verified live: old endpoints return the exact documented 410, new endpoints
route correctly (401 unauthenticated), and the exact request body this fix
produces is accepted through Kalshi's validation layer (401 auth failure,
not 400 validation failure) using placeholder credentials.
Two independent, longstanding bugs affecting every authenticated CLOB call
(order placement/cancellation, portfolio, history, execution, the user
WebSocket feed, auto-redeem — ~20 call sites):
- Header names were hyphenated (POLY-ADDRESS, POLY-API-KEY, ...); Polymarket's
actual wire format is underscored (POLY_ADDRESS, POLY_API_KEY, ...),
confirmed against docs.polymarket.com's CLOB auth reference and both
clob-client and clob-client-v2's header-building code, which pass these
keys straight to axios/fetch with no name transformation.
- The L2 HMAC signature used standard base64; Polymarket requires URL-safe
base64 (+/ -> -/_), present identically in both the archived v1 and
current v2 official clients with an explicit "must be url safe" comment.
A plain base64 digest mismatches whenever it contains '+' or '/' — most
of the time. Verified the fix byte-identical to a reference implementation
matching the official algorithm across 9 cases spanning 3 secrets.
Fixed identically in polymarket-setup.ts's separate L1 (API-key-creation)
header builder, which had the same header-name bug.
Every quote, buy, sell, and graduation check for a non-graduated agent token
called buy()/sell()/assetBalance()/tokenBalance()/graduated() directly on the
agent token's own address. That contract is a plain ERC20 — those functions
don't exist there. Verified against the real deployed contracts (source:
code-423n4/2025-04-virtuals-protocol) and live on-chain calls on Base:
- Bonding is a SINGLETON (VIRTUALS_CONTRACTS.bondingProxy) that trades on
behalf of every agent token via buy(amountIn, tokenAddress)/sell(amountIn,
tokenAddress) — no minAmountOut param exists on-chain, and the recipient is
always msg.sender (no custom recipient support pre-graduation).
- Reserves live on a per-token FPair contract, found via
Bonding.factory().getPair(agentToken, VIRTUAL_TOKEN); quoting now calls the
router's own getAmountsOut so it matches on-chain math exactly instead of a
hand-rolled formula, including the router's buy/sell tax handling.
- Since there's no atomic on-chain slippage protection, buy/sell now re-quote
immediately before sending and abort if price moved past tolerance.
Applied the same fix to the read-only src/feeds/virtuals price/graduation feed,
which had the identical wrong-contract assumption (baked into a comment that
explicitly said trades "go directly to agent token contracts").
- Uniswap/PancakeSwap: both kept a private, duplicated RPC-provider config
whose env-var lookup ("ETHEREUM_RPC_URL") didn't match the rest of the
codebase's convention ("ETH_RPC_URL", used everywhere else including
multichain.ts). Setting ETH_RPC_URL silently never reached these two,
leaving them stuck on the unreliable free default (eth.llamarpc.com),
which manifested as false "no pool found" errors that were actually RPC
connectivity failures. Both now delegate to multichain.ts's getChainConfig
instead of maintaining their own drifted copy.
- 1inch: parsed response.toAmount/estimatedGas and swapResponse.toAmount;
the live v6.0 API (still current — confirmed against 1inch's own
1inch-sdk-go, which still targets v6.0) actually returns dstAmount/gas.
Every quote and swap call would throw on BigInt(undefined). Also switched
the deprecated api.1inch.dev host to api.1inch.com per 1inch's migration
notice and the official SDK's own default.
- Raydium: two live bugs — wrong query param name (amount vs input/outputAmount,
every quote returned REQ_AMOUNT_ERROR), and response parsing read fields
(outAmount/minOutAmount/priceImpact) that don't exist in the current API
response (outputAmount/otherAmountThreshold/priceImpactPct). Also replaced
the bulk mainnet.json pool listing (crashes past V8's string-length limit)
with the modern paginated /pools/info endpoint.
- Orca: whirlpool quotes were silently hitting the SDK's default RPC
(a discontinued GenesysGo endpoint) whenever no connection was passed
explicitly; now always passes a real connection.
- Meteora DBC: unfiltered pool listing crashed the process the same way
Raydium's bulk endpoint did; now throws with guidance toward the
paginated by-config/by-creator queries instead of silently corrupting output.
- Pump.fun: bonding-curve fee default was 100bps (1%); current live fee is
125bps (1.25% = 0.95% protocol + 0.30% creator).
- New: PumpSwap venue built on the real @pump-fun/pump-swap-sdk (pool discovery
via getProgramAccounts + memcmp, quoting via the SDK's own curve math),
wired into the arbitrage scanner alongside Jupiter/Raydium/Orca/Meteora.
- New Rust binary (rust/fast-broadcast) races eth_sendRawTransaction across
multiple RPC/sequencer endpoints via a TS wrapper (src/evm/fast-broadcast.ts);
proven byte-identical to ethers' native signing path.
- multichain.ts: adds rpcFallbacks/getRaceUrls per chain, plus official
write-only sequencer endpoints for Arbitrum and Base (Optimism's public
sequencer is excluded per its own docs — rate-limited, not for default use).
- odos.ts: races broadcast across configured endpoints when more than one is
set; bumps the retired /sor/quote/v2 endpoint to /sor/quote/v3 and adds the
pathViz request flag (route was previously always empty) and mandatory
pre-broadcast simulation (Odos's own hard rule — refuse to broadcast a
transaction their simulator predicts will revert).
- postinstall self-heals two real environment bugs: bigint-buffer's native
binary SIGILLs on this platform, and @coral-xyz/anchor's CJS build exposes
BN via a getter that breaks native ESM named-export synthesis for
dependents like @meteora-ag/dlmm.
- tsconfig: incremental builds (measured ~22% faster warm typecheck).
codex built the HFT venue/multi-hop planners and opportunity integration on
this branch without ever having a working Node/TS toolchain, so none of it
had actually been compiled or run. This installs deps and fixes what a real
compiler and test suite turn up, plus finishes wiring up the previously
orphaned live venue-arbitrage scanner.
Compile/runtime fixes:
- mergeConfig in venue-arbitrage.ts and multi-hop-arbitrage.ts used a
keyof-indexed write pattern that doesn't typecheck; cast through
Record<string, unknown> instead.
- Multi-hop cycle dedup picked between equivalent rotations of the same
cycle via a floating-point netEdgeBps comparison, making the reported
startAsset arbitrary; now keeps whichever rotation is found first.
- src/solana/orca.ts imported `u64` from @solana/spl-token, an export
removed when that package moved to native bigint; importing it crashed
the process with SIGILL on load. Switched to BN from @coral-xyz/anchor,
matching meteora.ts.
- Fixed test fixtures that never actually cleared the planner's real
thresholds (missing minTargetProfitUsd overrides in opportunity-hft
tests; a maker/taker spread too thin to clear kalshi's taker fee in
venue-arbitrage.test.ts).
venue-arbitrage-scanner.ts (live quote scanning across Jupiter, Raydium,
Orca, Meteora, Uniswap, 1inch, PancakeSwap, Lighter) existed but had no
command, route, or test coverage. Wired it into the rust-hft-arbitrage
skill as `/hft-arb scan`, fixed a dead-code formatting bug in its output,
and added test coverage from zero via its existing dependency-injection
seam. Read-only throughout — never signs or sends a transaction.
Verified clean: tsc --noEmit and the full test suite (161/161).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Override ajv to v8.18.0 to fix ReDoS vulnerability in json schema validation
- This resolves the last moderate security vulnerability found by npm audit
- Reduces reported vulnerabilities from 1 to 0
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Regenerate lock file to properly reflect:
- npm overrides for minimatch and qs
- Security workflow configuration changes
- Dependabot dependency updates (PR #11)
This ensures CI/CD npm ci can find all packages correctly.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add npm overrides for minimatch (10.2.1) and qs (6.15.0) to fix vulnerabilities
- Remove dev-only packages from main dependencies (minimatch, qs)
- Update CI/CD workflows to use --production flag for npm audit
- Excludes dev dependencies (eslint, nyc, testing tools) from security checks
- All remaining vulnerabilities are dev-only and don't affect production
- Keep production dependencies clean: no vulnerabilities
This reduces reported vulnerabilities from 18 to 0 in production code while maintaining
dev/test tooling integrity.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Update fast-xml-parser from 5.3.4 to 5.3.7 (fixes critical XXE vulnerabilities)
- Update bn.js from 5.2.2 to 5.2.3 (fixes infinite loop vulnerability)
- Update minimatch to 10.2.1 (fixes ReDoS vulnerability in pattern matching)
- Update qs to 6.15.0 (fixes DoS vulnerability in query string parsing)
- Adds minimatch as direct dependency to reduce transitive vulnerability chain
Reduces high-severity vulnerabilities from 21 to 18 (remaining are in dev-only dependencies)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add Claude Opus 4.6, Sonnet 4.5, Haiku 4.5 (latest models)
- Add Google Gemini 2.0 Flash and Gemini 1.5 Pro
- Organize models by provider (Anthropic, OpenAI, Google, Local)
- Clearly mark legacy Claude models
- Add context window information for each model
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Just say \"token launches\" without overdoing Pump.fun mention.
Pump.fun still highlighted in Solana integration section.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Update opening to detail:
- EVM chains: Base (prominent!), ETH, Arbitrum, Optimism, Polygon
- Protocols: Uniswap V3, 1inch, Virtuals Protocol
Base is emerging as key chain - deserves explicit mention.
Virtuals Protocol is important for AI agent trading.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
More accurate terminology. Shows comprehensive multi-chain support, not just swapping.
Also updated \"Solana token launches\" → \"Solana & EVM token launches\" to reflect multi-chain capability.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Highlight that Clodds supports both Solana and EVM chains for crypto trading:
- Solana: Jupiter, Raydium
- EVM: Uniswap, 1inch on 5 chains (ETH, ARB, OP, Base, Polygon)
Makes clear this is multi-chain, not just Solana-only.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Skills are more impressive than just channel count - shows the breadth of capabilities and automation.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Clodds supports thousands of markets across prediction markets, perpetual futures, and DeFi. The \"10\" was misleading - represents 10 prediction market platforms, but total accessible markets is 1000+.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Add:
- Colosseum Agent Hackathon badge to shields
- Callout: \"Built for Colosseum Agent Hackathon on Solana — Developed in 12 days\"
Shows this is a hackathon project demonstrating autonomous trading agent capabilities.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>