solmachina-sdk 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +101 -0
- package/index.d.ts +96 -0
- package/index.mjs +121 -0
- package/package.json +27 -0
package/README.md
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# solmachina-sdk
|
|
2
|
+
|
|
3
|
+
The official SDK for the **SolMachina Agent Financial Firewall** — the trust & decision layer an autonomous
|
|
4
|
+
agent calls *before* it moves money.
|
|
5
|
+
|
|
6
|
+
One call returns **EXECUTE / REVIEW / REJECT** + a risk index (SMRI 0–100) + the evidence + an Ed25519 **signed
|
|
7
|
+
receipt** you can verify offline — plus a **fail-closed policy gate** (`ALLOW` / `DENY`). Paid per call in USDC
|
|
8
|
+
over **x402** (Solana + Base). No account, no API key.
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm i solmachina-sdk
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## 30-second taste (free, no wallet)
|
|
15
|
+
|
|
16
|
+
```js
|
|
17
|
+
import { SolMachina } from "solmachina-sdk";
|
|
18
|
+
|
|
19
|
+
const sm = new SolMachina();
|
|
20
|
+
console.log(await sm.sample("/v1/decision")); // free, truncated preview via the remote MCP
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## The one call before your agent moves money
|
|
24
|
+
|
|
25
|
+
```js
|
|
26
|
+
import { SolMachina } from "solmachina-sdk";
|
|
27
|
+
|
|
28
|
+
const sm = new SolMachina({ payFetch }); // payFetch = an x402-enabled fetch (below)
|
|
29
|
+
|
|
30
|
+
const { allowed, decision, reason, failed } = await sm.guard({
|
|
31
|
+
mint: "So11111111111111111111111111111111111111112",
|
|
32
|
+
policy: "conservative", // conservative | bluechip | anti-rug | degen
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
if (allowed) {
|
|
36
|
+
await wallet.send(tx); // proceed
|
|
37
|
+
} else {
|
|
38
|
+
console.warn("blocked:", decision, reason, failed);
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
`allowed` is `true` **only** when the decision is `EXECUTE` **and** (there is no policy, or the policy verdict is
|
|
43
|
+
`ALLOW`). SolMachina never tells your agent *what* to trade — it decides whether the action your agent already
|
|
44
|
+
chose satisfies its policy, and proves it.
|
|
45
|
+
|
|
46
|
+
## Enabling payments (`payFetch`)
|
|
47
|
+
|
|
48
|
+
`payFetch` is any `fetch` that speaks x402 (handles `402 → pay → retry`). Wire it to your wallet with a standard
|
|
49
|
+
x402 client — the SDK stays wallet-agnostic and dependency-free. Example with `x402-fetch`:
|
|
50
|
+
|
|
51
|
+
```js
|
|
52
|
+
import { wrapFetchWithPayment } from "x402-fetch";
|
|
53
|
+
// ...create your Solana/Base signer with your wallet library of choice...
|
|
54
|
+
const payFetch = wrapFetchWithPayment(fetch, signer);
|
|
55
|
+
|
|
56
|
+
const sm = new SolMachina({ payFetch });
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Without a `payFetch`, paid calls throw `SolMachinaPaymentRequired` carrying the real USDC terms (so you can pay
|
|
60
|
+
them yourself) — never a silent guess.
|
|
61
|
+
|
|
62
|
+
## Policies
|
|
63
|
+
|
|
64
|
+
Named preset **or** inline rules (mix freely; explicit rules override a preset):
|
|
65
|
+
|
|
66
|
+
```js
|
|
67
|
+
await sm.guard({ mint, policy: "anti-rug" });
|
|
68
|
+
await sm.guard({ mint, minSmri: 80, denyMintAuthority: true, denyFreezeAuthority: true });
|
|
69
|
+
await sm.guard({ mint, denyIlliquid: true, minRoundTripRetainedPct: 90 }); // must be sellable
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Rules: `minSmri`, `minConfidence`, `maxConcentrationPct`, `denyMintAuthority`, `denyFreezeAuthority`,
|
|
73
|
+
`denyHighRiskBand`, `denyIlliquid`, `minRoundTripRetainedPct`. Every rule is **fail-closed**: if it can't be
|
|
74
|
+
verified from on-chain facts, it fails (DENY) — never a false ALLOW.
|
|
75
|
+
|
|
76
|
+
## Other endpoints
|
|
77
|
+
|
|
78
|
+
```js
|
|
79
|
+
await sm.decision(mint, { amountUsd: 5000 }); // full decision object (+ .receipt)
|
|
80
|
+
await sm.smri(mint); // just the risk index (cheap, high-frequency)
|
|
81
|
+
await sm.pretrade(mint); // one-call pre-trade sweep
|
|
82
|
+
await sm.tokenRisk(mint);
|
|
83
|
+
await sm.walletScan(address);
|
|
84
|
+
await sm.dexQuote(inputMint, outputMint, amount);
|
|
85
|
+
await sm.call("token-holders", { mint }); // escape hatch: any paid endpoint
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Errors
|
|
89
|
+
|
|
90
|
+
- `SolMachinaPaymentRequired` — a paid call hit 402 and no (or a failing) `payFetch` was set. `err.terms` holds
|
|
91
|
+
the decoded USDC terms (`resource`, `accepts[]`).
|
|
92
|
+
- `SolMachinaError` — validation or an upstream error; `err.code` mirrors the API (`invalid_mint`,
|
|
93
|
+
`invalid_policy`, `rpc_unavailable`, …) and `err.details` holds the server body.
|
|
94
|
+
|
|
95
|
+
## Honesty
|
|
96
|
+
|
|
97
|
+
Not financial advice, not a profit prediction. SolMachina is a transparent **risk gate** against *your* stated
|
|
98
|
+
thresholds; it returns `REVIEW`/`unknown` instead of guessing, and every paid answer is signed and verifiable.
|
|
99
|
+
|
|
100
|
+
Docs: https://api.solmachina.com/docs · MCP: `https://api.solmachina.com/mcp` · registry:
|
|
101
|
+
`io.github.imnotamob/solmachina-x402`
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
// Type definitions for solmachina-sdk
|
|
2
|
+
|
|
3
|
+
export type Verdict = "ALLOW" | "DENY";
|
|
4
|
+
export type Decision = "EXECUTE" | "REVIEW" | "REJECT";
|
|
5
|
+
export type PolicyPreset = "conservative" | "bluechip" | "anti-rug" | "degen";
|
|
6
|
+
|
|
7
|
+
/** Individual firewall rules (fail-closed: a rule that can't be verified fails). */
|
|
8
|
+
export interface PolicyRules {
|
|
9
|
+
minSmri?: number;
|
|
10
|
+
minConfidence?: number;
|
|
11
|
+
maxConcentrationPct?: number;
|
|
12
|
+
denyMintAuthority?: boolean;
|
|
13
|
+
denyFreezeAuthority?: boolean;
|
|
14
|
+
denyHighRiskBand?: boolean;
|
|
15
|
+
/** Deny if the token cannot be sold back (honeypot / one-sided liquidity). Requires exit-liquidity enabled. */
|
|
16
|
+
denyIlliquid?: boolean;
|
|
17
|
+
/** Deny unless a $100 round-trip retains at least this %. Requires exit-liquidity enabled. */
|
|
18
|
+
minRoundTripRetainedPct?: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface DecisionOptions {
|
|
22
|
+
/** A named preset OR an inline PolicyRules object. */
|
|
23
|
+
policy?: PolicyPreset | PolicyRules;
|
|
24
|
+
action?: string;
|
|
25
|
+
amountUsd?: number | string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface GuardArgs extends PolicyRules {
|
|
29
|
+
mint: string;
|
|
30
|
+
policy?: PolicyPreset | PolicyRules;
|
|
31
|
+
action?: string;
|
|
32
|
+
amountUsd?: number | string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface GuardResult {
|
|
36
|
+
/** true only when decision === "EXECUTE" AND (no policy, or policy verdict === "ALLOW"). */
|
|
37
|
+
allowed: boolean;
|
|
38
|
+
decision: Decision;
|
|
39
|
+
verdict: Verdict | null;
|
|
40
|
+
smri: number | null;
|
|
41
|
+
confidence: number | null;
|
|
42
|
+
reason: string;
|
|
43
|
+
failed: string[];
|
|
44
|
+
evidence: Record<string, unknown>;
|
|
45
|
+
/** Ed25519 signed receipt (X-SolMachina-Receipt), verifiable offline; null if absent. */
|
|
46
|
+
receipt: string | null;
|
|
47
|
+
raw: Record<string, unknown>;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface SolMachinaOptions {
|
|
51
|
+
baseUrl?: string;
|
|
52
|
+
/** An x402-enabled fetch for PAID calls (handles 402 -> pay -> retry). */
|
|
53
|
+
payFetch?: typeof fetch;
|
|
54
|
+
/** fetch implementation for FREE calls (default: global fetch). */
|
|
55
|
+
fetch?: typeof fetch;
|
|
56
|
+
timeoutMs?: number;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface PaymentTerms {
|
|
60
|
+
resource?: string;
|
|
61
|
+
accepts: Array<{ network?: string; amount?: string; asset?: string; payTo?: string }>;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export class SolMachinaError extends Error {
|
|
65
|
+
code: string;
|
|
66
|
+
details: unknown;
|
|
67
|
+
constructor(code: string, message?: string, details?: unknown);
|
|
68
|
+
}
|
|
69
|
+
export class SolMachinaPaymentRequired extends SolMachinaError {
|
|
70
|
+
path: string;
|
|
71
|
+
terms: PaymentTerms | null;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export class SolMachina {
|
|
75
|
+
constructor(opts?: SolMachinaOptions);
|
|
76
|
+
baseUrl: string;
|
|
77
|
+
|
|
78
|
+
// free (no payment)
|
|
79
|
+
catalog(): Promise<Record<string, unknown>>;
|
|
80
|
+
status(): Promise<Record<string, unknown>>;
|
|
81
|
+
sample(endpoint?: string): Promise<Record<string, unknown>>;
|
|
82
|
+
|
|
83
|
+
// paid (x402 USDC)
|
|
84
|
+
decision(mint: string, opts?: DecisionOptions): Promise<Record<string, unknown>>;
|
|
85
|
+
smri(mint: string): Promise<Record<string, unknown>>;
|
|
86
|
+
pretrade(mint: string): Promise<Record<string, unknown>>;
|
|
87
|
+
tokenRisk(mint: string): Promise<Record<string, unknown>>;
|
|
88
|
+
walletScan(address: string): Promise<Record<string, unknown>>;
|
|
89
|
+
dexQuote(inputMint: string, outputMint: string, amount: string | number, opts?: Record<string, unknown>): Promise<Record<string, unknown>>;
|
|
90
|
+
call(endpoint: string, params?: Record<string, unknown>): Promise<Record<string, unknown>>;
|
|
91
|
+
|
|
92
|
+
/** The one call an agent makes before it moves money. */
|
|
93
|
+
guard(args: GuardArgs): Promise<GuardResult>;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export default SolMachina;
|
package/index.mjs
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
// solmachina — the official SDK for the SolMachina Agent Financial Firewall.
|
|
2
|
+
//
|
|
3
|
+
// Turn "understand 20 endpoints + hand-roll x402 payments" into three lines:
|
|
4
|
+
//
|
|
5
|
+
// import { SolMachina } from "solmachina-sdk";
|
|
6
|
+
// const sm = new SolMachina({ payFetch }); // payFetch = an x402-enabled fetch (any wallet)
|
|
7
|
+
// const { allowed } = await sm.guard({ mint, policy: "conservative" });
|
|
8
|
+
// if (allowed) await wallet.send(tx);
|
|
9
|
+
//
|
|
10
|
+
// Zero dependencies (uses global fetch). Payment is INJECTED: you pass a `payFetch` — an x402-capable fetch
|
|
11
|
+
// that handles 402 -> pay -> retry (e.g. from `x402-fetch`/`@x402/fetch` wired to your signer). This keeps the
|
|
12
|
+
// SDK wallet-agnostic and tiny. Free tools (catalog/status/sample) need no payment. Trust-first: paid calls
|
|
13
|
+
// without a payer throw a clear PaymentRequired carrying the real USDC terms — never a silent guess.
|
|
14
|
+
|
|
15
|
+
const DEFAULT_BASE = "https://api.solmachina.com";
|
|
16
|
+
const B58 = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/;
|
|
17
|
+
|
|
18
|
+
export class SolMachinaError extends Error {
|
|
19
|
+
constructor(code, message, details) { super(message || code); this.name = "SolMachinaError"; this.code = code; this.details = details || null; }
|
|
20
|
+
}
|
|
21
|
+
export class SolMachinaPaymentRequired extends SolMachinaError {
|
|
22
|
+
constructor(path, terms) {
|
|
23
|
+
super("payment_required", `Paid endpoint ${path} returned HTTP 402. Provide an x402 payFetch to pay automatically (see README), or pay the returned terms manually.`, { terms });
|
|
24
|
+
this.name = "SolMachinaPaymentRequired"; this.path = path; this.terms = terms || null;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Decode the base64 x402 `payment-required` header into readable terms (never throws).
|
|
29
|
+
function decodeTerms(res) {
|
|
30
|
+
try {
|
|
31
|
+
const h = res.headers.get?.("payment-required"); if (!h) return null;
|
|
32
|
+
const json = typeof atob === "function" ? atob(h) : Buffer.from(h, "base64").toString("utf8");
|
|
33
|
+
const t = JSON.parse(json);
|
|
34
|
+
return { resource: t?.resource?.url, accepts: (t?.accepts || []).map(a => ({ network: a.network, amount: a.amount, asset: a.asset, payTo: a.payTo })) };
|
|
35
|
+
} catch { return null; }
|
|
36
|
+
}
|
|
37
|
+
const qs = (o) => { const p = new URLSearchParams(); for (const [k, v] of Object.entries(o)) if (v !== undefined && v !== null && v !== "") p.set(k, String(v)); const s = p.toString(); return s ? "?" + s : ""; };
|
|
38
|
+
|
|
39
|
+
export class SolMachina {
|
|
40
|
+
/**
|
|
41
|
+
* @param {object} [opts]
|
|
42
|
+
* @param {string} [opts.baseUrl] default https://api.solmachina.com
|
|
43
|
+
* @param {Function} [opts.payFetch] an x402-enabled fetch(url, init) for PAID calls (402->pay->retry)
|
|
44
|
+
* @param {Function} [opts.fetch] fetch impl for FREE calls (default: global fetch)
|
|
45
|
+
* @param {number} [opts.timeoutMs] per-request timeout (default 20000)
|
|
46
|
+
*/
|
|
47
|
+
constructor(opts = {}) {
|
|
48
|
+
this.baseUrl = String(opts.baseUrl || DEFAULT_BASE).replace(/\/+$/, "");
|
|
49
|
+
this.payFetch = typeof opts.payFetch === "function" ? opts.payFetch : null;
|
|
50
|
+
this.fetch = opts.fetch || globalThis.fetch;
|
|
51
|
+
this.timeoutMs = Number.isFinite(opts.timeoutMs) ? opts.timeoutMs : 20000;
|
|
52
|
+
if (typeof this.fetch !== "function") throw new SolMachinaError("no_fetch", "No fetch available. Pass opts.fetch (Node <18) or run where global fetch exists.");
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async _get(path, { paid = false } = {}) {
|
|
56
|
+
const url = this.baseUrl + path;
|
|
57
|
+
const doFetch = paid ? (this.payFetch || this.fetch) : this.fetch;
|
|
58
|
+
let res;
|
|
59
|
+
try { res = await doFetch(url, { headers: { accept: "application/json" }, signal: AbortSignal.timeout(this.timeoutMs) }); }
|
|
60
|
+
catch (e) { throw new SolMachinaError("network_error", String(e?.message || e)); }
|
|
61
|
+
if (res.status === 402) throw new SolMachinaPaymentRequired(path, decodeTerms(res));
|
|
62
|
+
let body = null; try { body = await res.json(); } catch { /* leave null */ }
|
|
63
|
+
if (!res.ok) throw new SolMachinaError(body?.error || `http_${res.status}`, body?.message || `HTTP ${res.status}`, body);
|
|
64
|
+
if (body && typeof body === "object") { const r = res.headers.get?.("x-solmachina-receipt"); if (r) Object.defineProperty(body, "receipt", { value: r, enumerable: false }); }
|
|
65
|
+
return body;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
_mint(m) { if (!B58.test(String(m || ""))) throw new SolMachinaError("invalid_mint", "mint must be a base58 SPL mint address"); return m; }
|
|
69
|
+
|
|
70
|
+
// ---- FREE (no payment) ----
|
|
71
|
+
catalog() { return this._get("/.well-known/x402"); }
|
|
72
|
+
status() { return this._get("/health"); }
|
|
73
|
+
/** Free truncated taste of any endpoint via the remote MCP `solmachina_sample` tool. */
|
|
74
|
+
async sample(endpoint = "/v1/decision") {
|
|
75
|
+
const res = await this.fetch(this.baseUrl + "/mcp", {
|
|
76
|
+
method: "POST", headers: { "content-type": "application/json", accept: "application/json" },
|
|
77
|
+
body: JSON.stringify({ jsonrpc: "2.0", id: 1, method: "tools/call", params: { name: "solmachina_sample", arguments: { endpoint } } }),
|
|
78
|
+
signal: AbortSignal.timeout(this.timeoutMs),
|
|
79
|
+
});
|
|
80
|
+
const j = await res.json().catch(() => null);
|
|
81
|
+
const text = j?.result?.content?.[0]?.text;
|
|
82
|
+
try { return JSON.parse(text); } catch { return j?.result ?? j; }
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// ---- PAID (x402 USDC) ----
|
|
86
|
+
/** The flagship. EXECUTE/REVIEW/REJECT + SMRI + evidence + policy firewall (+ signed receipt). */
|
|
87
|
+
async decision(mint, { policy, action, amountUsd, ...firewall } = {}) {
|
|
88
|
+
const params = { mint: this._mint(mint), action, amountUsd };
|
|
89
|
+
if (typeof policy === "string") params.policy = policy; // named preset
|
|
90
|
+
else if (policy && typeof policy === "object") Object.assign(params, policy); // inline rules
|
|
91
|
+
Object.assign(params, firewall);
|
|
92
|
+
return this._get("/v1/decision" + qs(params), { paid: true });
|
|
93
|
+
}
|
|
94
|
+
async smri(mint) { return this._get("/v1/smri" + qs({ mint: this._mint(mint) }), { paid: true }); }
|
|
95
|
+
async pretrade(mint) { return this._get("/v1/pretrade" + qs({ mint: this._mint(mint) }), { paid: true }); }
|
|
96
|
+
async tokenRisk(mint) { return this._get("/v1/token-risk" + qs({ mint: this._mint(mint) }), { paid: true }); }
|
|
97
|
+
async walletScan(address) { return this._get("/v1/wallet-scan" + qs({ address }), { paid: true }); }
|
|
98
|
+
async dexQuote(inputMint, outputMint, amount, opts = {}) { return this._get("/v1/dex-quote" + qs({ inputMint, outputMint, amount, ...opts }), { paid: true }); }
|
|
99
|
+
/** Escape hatch: any paid endpoint by path + params. */
|
|
100
|
+
async call(endpoint, params = {}) { return this._get("/v1/" + String(endpoint).replace(/^\/?(v1\/)?/, "") + qs(params), { paid: true }); }
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* The one call an agent makes before it moves money.
|
|
104
|
+
* @returns {Promise<{allowed:boolean, decision:string, verdict:string|null, smri:number|null,
|
|
105
|
+
* confidence:number|null, reason:string, failed:string[], evidence:object, receipt:string|null, raw:object}>}
|
|
106
|
+
* `allowed` is true only when the decision is EXECUTE AND (no policy, or the policy verdict is ALLOW).
|
|
107
|
+
*/
|
|
108
|
+
async guard({ mint, policy, action, amountUsd, ...firewall } = {}) {
|
|
109
|
+
const d = await this.decision(mint, { policy, action, amountUsd, ...firewall });
|
|
110
|
+
const verdict = d?.policy?.verdict ?? null;
|
|
111
|
+
return {
|
|
112
|
+
allowed: d?.decision === "EXECUTE" && (verdict === null || verdict === "ALLOW"),
|
|
113
|
+
decision: d?.decision, verdict,
|
|
114
|
+
smri: d?.smri ?? null, confidence: d?.confidence ?? null,
|
|
115
|
+
reason: d?.reason || "", failed: d?.policy?.failed || [],
|
|
116
|
+
evidence: d?.evidence || {}, receipt: d?.receipt || null, raw: d,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export default SolMachina;
|
package/package.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "solmachina-sdk",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Official SDK for the SolMachina Agent Financial Firewall — one call returns EXECUTE/REVIEW/REJECT + risk index + evidence + a signed receipt, with a fail-closed policy gate, paid per call in USDC over x402 (Solana + Base).",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "index.mjs",
|
|
7
|
+
"module": "index.mjs",
|
|
8
|
+
"types": "index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./index.d.ts",
|
|
12
|
+
"import": "./index.mjs"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": ["index.mjs", "index.d.ts", "README.md"],
|
|
16
|
+
"engines": { "node": ">=18" },
|
|
17
|
+
"sideEffects": false,
|
|
18
|
+
"keywords": [
|
|
19
|
+
"solmachina", "x402", "solana", "base", "usdc", "ai-agents", "agent", "autonomous",
|
|
20
|
+
"financial-firewall", "decision", "risk", "policy", "mcp", "defi", "trading", "wallet"
|
|
21
|
+
],
|
|
22
|
+
"homepage": "https://api.solmachina.com/docs",
|
|
23
|
+
"bugs": { "url": "https://api.solmachina.com/security" },
|
|
24
|
+
"repository": { "type": "git", "url": "git+https://github.com/imnotamob/solmachina.git" },
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"author": "SolMachina"
|
|
27
|
+
}
|