Skip to main content
The SDK builds the policy-gated transactions; the Move contract enforces the policy. Your agent process holds a private key and calls AltheiaSui methods. The contract decides whether each action lands.

Install

npm i @altheia-xyz/sui
Node 20+. @altheia-xyz/sui re-exports the @mysten/sui primitives an agent needs (SuiJsonRpcClient, getJsonRpcFullnodeUrl, Ed25519Keypair, decodeSuiPrivateKey), so there is one copy of @mysten/sui in play. A dapp-kit executor is provided for browser wallet flows.

Env

The agent needs only its key, from provisioning:
AGENT_PRIVKEY=suiprivkey1...

Construct the agent

agentRefs reads the AgentCap the key owns and derives the vault, policy, and cap. keypairExecutor signs and submits, waiting for finality so sequential actions don’t race.
import {
  SuiJsonRpcClient, getJsonRpcFullnodeUrl, Ed25519Keypair, decodeSuiPrivateKey,
  AltheiaSui, keypairExecutor, agentRefs,
} from "@altheia-xyz/sui";

const client = new SuiJsonRpcClient({ url: getJsonRpcFullnodeUrl("testnet"), network: "testnet" });
const kp = Ed25519Keypair.fromSecretKey(decodeSuiPrivateKey(process.env.AGENT_PRIVKEY!).secretKey);

const refs = await agentRefs(client, kp.getPublicKey().toSuiAddress());
const agent = new AltheiaSui(client, refs, keypairExecutor(client, kp));
agentRefs throws if the address owns no AgentCap — wrong key, or the agent is not provisioned.

Act

const r = await agent.swap(50_000_000n);   // 0.05 SUI, gated by the policy
console.log("landed", r.digest);
swap buys (spends the capped quote, receives base into the vault). sellSwap unwinds a base position back to quote. Amounts are base units. Each method builds, signs, and submits internally.

Handle denials

A policy denial surfaces as a thrown error. decodePolicyAbort tells a policy denial apart from a liquidity or gas failure.
import { decodePolicyAbort } from "@altheia-xyz/sui";

try {
  await agent.swap(2_000_000_000n);     // over the per-tx cap
} catch (e) {
  const ab = decodePolicyAbort(e);
  if (ab) handleDenied(ab.reason_code); // "over_per_tx_cap" | "policy_revoked" | ...
  else throw e;                         // not a policy decision — surface it
}
Denials are normal control flow. A capped agent will hit them.

Record denials without spending gas

A denied action aborts on-chain and emits no event, so to log denials, dry-run first. guardedSubmit dry-runs, records a policy abort to the audit backend, and submits only when allowed.
import { buildSwap, guardedSubmit } from "@altheia-xyz/sui";

const r = await guardedSubmit(
  client, keypairExecutor(client, kp), kp.getPublicKey().toSuiAddress(),
  buildSwap(refs, 50_000_000n),
  { baseUrl, token, agent_id, action_type: "deepbook_swap", amount: 0.05, asset: "SUI" },
);
// r.ok ? r.digest : r.reason_code

Read state

const p = await agent.policy();        // live caps + state from chain
const log = await agent.auditLog(10);  // recent on-chain events
Or read directly with readPolicyObject, vaultBalances, readAuditLog. See the SDK reference.

Runnable example

A DeepBook agent: allowed, denied, revoked.