Skip to main content
The owner of a TradeAgent has two on-chain actions, both gated by AgentOwnerCap. Surfaced as the OwnerControls block on /agents (in-place expand) and /agents/:agentId (sticky right column).

Reclaim

The BalanceManager is shared but balance_manager::withdraw_all<DUSDC> is owner-gated by Move. One-click affordance:
const tx = new Transaction();
const [coin] = tx.moveCall({
  target: `${DEEPBOOK_PACKAGE_ID}::balance_manager::withdraw_all`,
  typeArguments: [DUSDC_TYPE],
  arguments: [tx.object(balanceManagerId)],
});
tx.transferObjects([coin], tx.pure.address(ownerAddress));
Button renders only when account.address === agent.owner. Reads the BalanceManager id via sui.getObject(TradeAgent) (field balance_manager_id) and the live DUSDC balance via devInspect of balance_manager::balance<DUSDC>. Demo line: “your funds, your keys. The agent never had the right to keep them.”

Pause / Resume

public fun pause(agent: &mut TradeAgent, cap: &AgentOwnerCap);
public fun resume(agent: &mut TradeAgent, cap: &AgentOwnerCap);
Both abort with ENotOwner if cap.agent_id != object::id(agent).
Neither emits an event. The server paused column in the agents table is stale. The frontend OwnerControls and the agent-service tick guard both read is_active live via sui.getObject(TradeAgent). Do not rely on the API field.
Frontend wiring:
// Discover the user's AgentOwnerCap matching this agent
const caps = await sui.getOwnedObjects({
  owner: account.address,
  filter: { StructType: `${DARKPOOL_PACKAGE_ID}::agent::AgentOwnerCap` },
  options: { showContent: true },
});
const ownerCap = caps.data.find(c =>
  (c.data?.content as any)?.fields?.agent_id === agentId
);
if (!ownerCap) throw new Error('not the owner');

const tx = new Transaction();
tx.moveCall({
  target: `${DARKPOOL_PACKAGE_ID}::agent::pause`,
  arguments: [tx.object(agentId), tx.object(ownerCap.data.objectId)],
});
Header on /agents/:agentId surfaces a yellow PAUSED on-chain badge whenever is_active=false.

Runtime gate

The agent-service reads is_active at the start of every tick when TRADE_AGENT_ID is set:
[agent btc-alpha] tick 142: paused on-chain by owner. Skipping.
Defensive default = active so transient RPC hiccups don’t silently halt the agent.

Three kinds of “off”

This closes the third leg of agent custody:
  1. Stop the runtime. ⌃C the process. Tactical, not on-chain.
  2. Pause on-chain. Owner signs agent::pause. Runtime keeps polling but never trades.
  3. Reclaim DUSDC. Owner signs balance_manager::withdraw_all. Funds back in wallet.