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| Entry | What |
|---|---|
@bound/near | read client, confinement check, pure helpers, write helpers, TESTNET |
@bound/near/mcp | createNearMcpServer (see MCP server) |
@bound/near/testnet.json | the bundled deployment as JSON |
bound-near | the 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_listPure helpers
No network; unit tested.
| Export | Does |
|---|---|
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, USDC | constants |
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-jsFailoverRpcProvideroverTESTNET_RPCS.TESTNET_ARCHIVAL_RPCSputs an archival node first for old transactions.view(provider, contractId, method, args): a view call atfinal, 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 aTxOutcome(hash,url,ok,error,logs,value) rather than throwing for an on-chain failure.tryCallalso turns a submission refusal (no transaction created, for example a function-call key refused by NEAR) intook: false, hash: null.accountFromKey(provider, accountId, key)builds anAccountfrom 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 }