Bound

Build

SDK

The @bound/near TypeScript library: verifyAgent, checkConfinement, certificate reads and pure helpers.

@bound/near 0.1.0 is an ESM package for Node.js 20 or later, built on near-api-js 7.5.3. The testnet deployment ships inside it, so there is nothing to configure. Reads need no NEAR account and no key. Source: packages/near.

sh
npm install @bound/near
EntryWhat
@bound/nearread client, confinement check, pure helpers, write helpers, TESTNET
@bound/near/mcpcreateNearMcpServer (see MCP server)
@bound/near/testnet.jsonthe bundled deployment as JSON
bound-nearthe CLI binary (see CLI)

Gate a request on a certificate

ts
import { fmtUsdc, verifyAgent } from "@bound/near";

const r = await verifyAgent("bound-agent2-mwfnc6q8.testnet");

if (r.deal && r.confinement?.confined) {
  console.log(`deal: coverage ${fmtUsdc(r.coverage)} USDC on ${r.contract}`);
} else {
  console.log(`refuse: ${r.reason}`);
}

verifyAgent(agent, opts?) reads verify(agent) and the agent's keys and code, and returns an AgentVerdict:

ts
interface AgentVerdict {
  agent: string;
  contract: string;
  /** Deal only when true: Verified, not halted, float > 0. */
  deal: boolean;
  reason: string;              // "Verified", or why not
  certificate: CertViewJson | null;
  /** reserve_locked + auditor allocation, USDC base units (0 without a cert). */
  coverage: bigint;
  /** Null when the account does not exist or could not be read. */
  confinement: Confinement | null;
}

deal is the contract's verdict and does not include confinement. Check confinement?.confined too, as in the example.

Options

ts
interface ReadOptions {
  provider?: Provider;   // a near-api-js Provider; wins over rpc
  rpc?: string[];        // RPC URLs, tried in order (default: bundled FastNEAR testnet)
  contractId?: string;   // default: bound-v3.boundproto-nh26.testnet
}

Reads

Certificates

ts
import { TESTNET, getAgentCertificate, getCertificate } from "@bound/near";

const current = await getAgentCertificate("bound-agent2-mwfnc6q8.testnet"); // verify(agent)
const settled = await getCertificate(TESTNET.demo.settledCertId);           // get_cert(0)
console.log(settled?.status, settled?.settlement);

Both return CertViewJson | null: the contract's view with amounts as decimal strings.

Confinement

ts
import { checkConfinement } from "@bound/near";

const c = await checkConfinement("bound-agent2-mwfnc6q8.testnet");
// c.confined        every key pay-only for the contract AND no code, local or global
// c.keysConfined    the keys alone
// c.noContractCode  code_hash all ones and no global contract
// c.noLocalCode     code_hash all ones
// c.globalContract  the global contract hash or account, or null
// c.code, c.keys    the raw view_account fields and view_access_key_list

Pure helpers

No network; unit tested.

ExportDoes
assess(view){ ok, reason }: ok only for Verified, not halted, and not deal=false
coverageOf(view)bigint coverage (reserve_locked + allocation)
isConfined(keys, contractId, code)keysConfined and hasNoCode
keysConfined(keys, contractId)every key a FunctionCall to contractId with methods only pay
hasNoCode(code)code_hash all ones and no global contract
globalContractOf(code)global contract hash or account id, or null
describeCert(view), describeCode(code)one-line summaries (the CLI prints these)
usdc("12.5")12500000n (base units)
fmtUsdc(12500000n)"12.5"
parseEvents(logs), payEvents(logs)NEP-297 bound events from transaction logs
explorerTx(hash), explorerAccount(id)testnet NearBlocks links
NO_CODE_HASH, USDC_DECIMALS, USDCconstants

Read a payment receipt

ts
import { payEvents } from "@bound/near";

// logs: the receipt logs of the agent's pay transaction
for (const p of payEvents(logs)) {
  console.log(p.cert_id, p.agent, "->", p.to, p.amount, p.memo);
}

Low-level client

  • testnetProvider(urls?): a near-api-js FailoverRpcProvider over TESTNET_RPCS. TESTNET_ARCHIVAL_RPCS puts an archival node first for old transactions.
  • view(provider, contractId, method, args): a view call at final, tried up to 3 times (withRetry).
  • accountCodeOf, codeHashOf, chainNow (final block time in seconds).
  • call(account, contractId, method, args, { deposit?, gas? }) sends one function call and waits for FINAL. It never auto-resends. It returns a TxOutcome (hash, url, ok, error, logs, value) rather than throwing for an on-chain failure. tryCall also turns a submission refusal (no transaction created, for example a function-call key refused by NEAR) into ok: false, hash: null.
  • accountFromKey(provider, accountId, key) builds an Account from a key you pass. The package never reads key files.
  • quietRpcRetryLogs() is opt-in: it drops the stack traces near-api-js prints on each failed retry. Importing the library patches nothing.
the agent pays (pay-only key)
import { TESTNET, accountFromKey, testnetProvider, tryCall, usdc } from "@bound/near";

const provider = testnetProvider();
const agent = accountFromKey(provider, process.env.AGENT_ID!, process.env.AGENT_KEY as never);
const o = await tryCall(agent, TESTNET.contracts.bound.accountId, "pay", {
  cert_id: 1,
  to: "bound-service-mwfnc6q8.testnet",
  amount: usdc("1").toString(),
  memo: null,
});
console.log(o.ok ? o.url : o.error); // e.g. "FloatExceeded: amount ... > float ..."

TESTNET

ts
import { TESTNET } from "@bound/near";

TESTNET.contracts.bound.accountId  // "bound-v3.boundproto-nh26.testnet"
TESTNET.contracts.bound.codeHash   // "A3yf1yzihbUdZAaHn92aAm5dBAdFCq1QCwhBJixD2B4H"
TESTNET.contracts.token.accountId  // "usdc.boundproto-nh26.testnet"
TESTNET.accounts                   // { treasury, arbiter, owner }
TESTNET.rpc                        // FastNEAR testnet endpoints
TESTNET.demo                       // { runId: "mwfnc6q8", verifiedAgent, verifiedCertId: 1, settledAgent, settledCertId: 0 }