@telaro/middleware
v0.1.0
Published
Generic Telaro trust-gate middleware. Wraps any TypeScript SDK to enforce on-chain bond + score policy before tx-emitting methods.
Maintainers
Readme
@telaro/middleware
Generic Telaro trust-gate wrapper. Wraps any TypeScript SDK to enforce on-chain bond + score policy before tx-emitting methods.
Use this when:
- You're integrating Telaro into a DApp/SDK that has no hook system
(unlike idolly-acp, where you'd use
@telaro/idolly-acp-hook). - You want a one-line wrap without forking the SDK.
- You're a bot operator wanting to refuse delegations to under-bonded agents your code might receive from external sources.
Install
npm install @telaro/middleware @telaro/sdkPattern
import { TelaroGate, TelaroPolicyError } from "@telaro/middleware";
import { SomeClient } from "some-sdk";
const sdk = new SomeClient({...});
const gated = TelaroGate.wrap(sdk, {
// Which methods to gate
methods: ["placeOrder", "deposit"],
// Where to find the agent pubkey in the call args
resolveAgent: (args) => args[0]?.agentPubkey,
// Policy enforced before each call
policy: { minBond: 10_000_000n, minScore: 700 },
// Optional telemetry
onDecision: (e) => metrics.record(e),
});
try {
await gated.placeOrder({ agentPubkey: "Abc...", marketIndex: 0, ... });
} catch (err) {
if (err instanceof TelaroPolicyError) {
// err.code is one of: NOT_BONDED, BOND_BELOW_MIN, SCORE_BELOW_MIN, FROZEN, LOOKUP_FAILED
}
}Real examples
Drift perpetuals
import { DriftClient } from "@drift-labs/sdk";
import { TelaroGate } from "@telaro/middleware";
const drift = new DriftClient({...});
const gated = TelaroGate.wrap(drift, {
methods: ["placePerpOrder", "placeSpotOrder", "deposit"],
resolveAgent: (args) => args[0]?.subAccountId?.toString(),
policy: { minBond: 50_000_000n, minScore: 800 }, // perp = high VAR
});
await gated.placePerpOrder({...});MarginFi lending
import { MarginfiClient, MarginfiAccountWrapper } from "@mrgnlabs/marginfi-client-v2";
import { TelaroGate } from "@telaro/middleware";
const account = await client.fetchOrCreateMarginfiAccount();
const gated = TelaroGate.wrap(account, {
methods: ["deposit", "borrow", "repay", "withdraw"],
resolveAgent: () => MY_AGENT_PUBKEY,
policy: { minBond: 10_000_000n, minScore: 700 },
});Custom REST API client
class MyDexClient {
async swap(args: { agent: string; from: string; to: string; amount: bigint }) {...}
}
const gated = TelaroGate.wrap(new MyDexClient(), {
methods: ["swap"],
resolveAgent: (args) => (args[0] as { agent: string }).agent,
policy: { minBond: 1_000_000n, minScore: 500 },
});API
TelaroGate.wrap(sdk, config)
Wraps sdk in place. Returns the same reference. Idempotent.
| Field | Type | Default | Notes |
|---|---|---|---|
| methods | string[] | required | Method names to gate |
| resolveAgent | (args) => string \| undefined | required | Returns agent pubkey from args; undefined skips the gate |
| policy.minBond | bigint (atomic USDC, 6dp) | required | E.g., 10_000_000n = 10 USDC |
| policy.minScore | number (0..1000) | required | E.g., 700 |
| policy.allowOnLookupFailure | boolean | false | If true, network errors don't block |
| apiBase | string | https://telaro.xyz | Telaro REST API |
| apiKey | string | none | For higher rate-limit tiers |
| cacheTtlMs | number | 30_000 | Trust profile cache TTL. Set 0 to disable |
| onDecision | (event) => void | none | Telemetry hook |
TelaroPolicyError
Thrown by gated methods when policy fails. Has .code, .agentPubkey, .method.
catch (err) {
if (err instanceof TelaroPolicyError) {
switch (err.code) {
case "NOT_BONDED": /* agent never bonded */ break;
case "BOND_BELOW_MIN": /* bond too low */ break;
case "SCORE_BELOW_MIN": /* score too low */ break;
case "FROZEN": /* agent frozen (open dispute or bond fell below floor) */ break;
case "LOOKUP_FAILED": /* network or API error */ break;
}
}
}Performance
- Cached: 30s TTL by default. Repeated calls to the same agent in the same window cost 1 RPC.
- Async: gate adds ~50-200ms on the first call to a new agent (REST fetch from Telaro API), <1ms on cached calls.
- No on-chain RPC: this is a REST-based gate, not a CPI gate. For
atomic gating in the same tx, see the Anchor
view_bondCPI in thetelaroRust crate.
When to use what
| Need | Use |
|---|---|
| Quick TS wrap, no hooks in target SDK | @telaro/middleware (this) |
| Hook-based SDK (idolly-acp etc.) | @telaro/idolly-acp-hook |
| Atomic on-chain CPI gate | telaro Rust crate (view_bond) |
| Drop-in DApp UI modal | @telaro/react-presign |
| Building from a framework (Sendai, LangChain, etc.) | @telaro/{sendai,langchain,...} |
License
MIT
