Mandant Developer Hub & Architecture Specification
Mandant is an EVM-native financial control layer that allows orchestrators and institutional treasuries to give autonomous software access to capital without granting unrestricted wallet authority.
When AI agents operate with flat private keys (MetaMask, EOA, or unconstrained smart accounts), a single prompt injection, context hallucination, or runaway loop can drain the entire wallet balance in one block. Mandant replaces flat accounts with a recursive hierarchy of bounded mandates, outcome-verified escrow (ERC-8183), and 1-transaction subtree circuit breakers.
The Three Core Protocol Primitives
1. Hierarchical Budget Attenuation
A treasury creates a Root Mandate in MandateHub.sol. Parent agents delegate micro-budgets downward to child agents. Authority moves strictly downward: children cannot exceed their parent budget ceiling or expand beyond their parent's allowlisted tools (monotonic attenuation).
2. Outcome-Verified Escrow (ERC-8183)
Agents do not pay external service providers upfront. Payments are locked into JobAdapter.sol against an expected deliverable hash. An evaluator contract (HashMatchEvaluator.sol) deterministically checks the deliverable before funds release. If the output is corrupted or timed out, 100% of the funds are refunded.
3. 1-Transaction Subtree Circuit Breakers
When a worker agent behaves suspiciously, an operator or authorized watchdog sentinel executes revokeSubtree() in a single transaction. The rogue branch is quarantined, unspent capital sweeps upward, and independent sibling workflows continue without downtime.
Quickstart Installation
Mandant adheres strictly to modern package managers. Install the SDK via bun or pnpm:
LangChain Integration (TypeScript & Python)
Wrap any tool invocation in an outcome-conditioned Mandant escrow. If an LLM hallucinations generate invalid schema outputs, the smart contract prevents capital release.
import { DynamicStructuredTool } from "@langchain/core/tools"
import { z } from "zod"
import { MandantClient } from "@mandant/sdk"
import { keccak256, toHex } from "viem"
export function createMandantEscrowTool(client: MandantClient, workerMandateId: `0x${string}`) {
return new DynamicStructuredTool({
name: "mandant_outcome_escrow",
description: "Funds an ERC-8183 escrow for paid computation with onchain hash verification",
schema: z.object({
providerAddress: z.string().describe("Recipient service provider address"),
amountUsdc: z.number().describe("Budget in USDC (e.g. 250)"),
expectedDeliverableText: z.string().describe("Expected JSON schema or hash contract"),
}),
func: async ({ providerAddress, amountUsdc, expectedDeliverableText }) => {
const expectedHash = keccak256(toHex(expectedDeliverableText))
const amountUnits = BigInt(amountUsdc * 1e6)
// 1. Lock funds into onchain JobAdapter
const lockTx = await client.fundJob({
mandateId: workerMandateId,
provider: providerAddress as `0x${string}`,
amount: amountUnits,
deadline: BigInt(Math.floor(Date.now() / 1000) + 3600),
expectedDeliverableHash: expectedHash,
})
return `Escrow locked in tx ${lockTx}. Funds protected by MandateTree balance conservation.`
},
})
}CrewAI Multi-Agent Swarm Adapter (Python)
When using CrewAI, individual autonomous workers can be given bounded budget ceilings. Sub-agents use this tool to hire peer agents without ever holding root vault private keys:
from crewai.tools import BaseTool
from web3 import Web3
class CrewAIMandantTool(BaseTool):
name: str = "delegate_budget_to_peer"
description: str = "Delegates an attenuated micro-budget to a sub-worker agent in the swarm"
def _run(self, peer_agent_address: str, budget_usdc: float) -> str:
# Spawns depth-2 child mandate via MandateTree.spawn()
print(f"Spawning child mandate for {peer_agent_address} with {budget_usdc} USDC limit")
return f"Child mandate active on BNB Testnet. Invariant preserved: Idle - {budget_usdc} USDC."ElizaOS Agent Plugin (TypeScript)
Native ElizaOS plugin providing the MANDANT_ESCROW_PAY action and evaluator:
import { type Plugin, type Action, type IAgentRuntime, type Memory } from "@elizaos/core"
export const mandantEscrowPlugin: Plugin = {
name: "mandant-escrow",
description: "Enforces hierarchical budget constraints and outcome-verified settlement on ElizaOS agents",
actions: [
{
name: "MANDANT_ESCROW_PAY",
similes: ["PAY_VENDOR_WITH_ESCROW", "LOCK_BUDGET_FOR_TASK"],
validate: async (runtime: IAgentRuntime, message: Memory) => true,
handler: async (runtime, message, state, options, callback) => {
callback?.({
text: "Locked $250 USDC in ERC-8183 escrow. Release conditioned upon hash verification.",
})
return true
},
},
],
}Formal Verification: Balance Conservation Law
At every block, Mandant guarantees that capital in the vault matches the exact sum of all downward delegations and open escrow locks.
JobAdapter escrow awaiting delivery.Deep Dive: The Lazy Ancestor Walk in _sync()
In deep hierarchical swarms (e.g. Depth 1 Orchestrator $\to$ Depth 2 Worker $\to$ Depth 3 Sub-Agent), traversing the entire tree to revoke every grandchild eagerly would exceed EVM gas limits. Mandant implements an O(1) lazy downward synchronization algorithm:
function _sync(bytes32 mandateId) internal {
MandateNode storage node = nodes[mandateId];
if (node.status == NodeStatus.REVOKED) return;
// 1. Check self expiry
if (block.timestamp >= node.expiry && node.status == NodeStatus.ACTIVE) {
node.status = NodeStatus.EXPIRED;
_sweepIdleUp(mandateId);
return;
}
if (node.parentId == bytes32(0)) return;
// 2. Multi-hop ancestor walk up to MAX_DEPTH (5 hops)
bytes32 curr = node.parentId;
bool shouldRevoke = false;
for (uint8 d = 0; d < MAX_DEPTH && curr != bytes32(0); d++) {
MandateNode storage anc = nodes[curr];
if (anc.status == NodeStatus.REVOKED || anc.status == NodeStatus.EXPIRED || block.timestamp >= anc.expiry) {
shouldRevoke = true;
break;
}
curr = anc.parentId;
}
// 3. Lazy revocation cascade and capital sweep
if (shouldRevoke) {
node.status = NodeStatus.REVOKED;
_sweepIdleUp(mandateId);
}
}_sync() checks its ancestors, revokes the child, and sweeps its idle funds upward before any state can change.Capital Recovery: _sweepIdleUp()
When a child is revoked, its idle balance is swept upward to the nearest active ancestor, restoring invariant balance conservation:
function _sweepIdleUp(bytes32 mandateId) internal returns (uint128 swept) {
MandateNode storage node = nodes[mandateId];
swept = node.idle;
node.idle = 0;
if (node.parentId == bytes32(0) || swept == 0) return swept;
bytes32 curr = node.parentId;
while (curr != bytes32(0)) {
MandateNode storage ancestor = nodes[curr];
if (ancestor.status == NodeStatus.ACTIVE) {
ancestor.idle += swept;
if (ancestor.childGranted >= swept) {
ancestor.childGranted -= swept;
} else {
ancestor.childGranted = 0;
}
break;
}
curr = ancestor.parentId;
}
}Interactive Balance Conservation Sandbox
Adjust the sliders below to simulate how an agent allocates its $10,000 USDC budget across idle capital, child delegations, and escrow locks:
Center Guard & Policy Commitments
Launch Full StudioEvery mandate commits an onchain policy hash: policyHash = keccak256(policyJson). The EVM checks that:
ERC-8004 Agent Identity & Attributed DIDs
When an agent is quarantined, revoking its mandate sweeps its capital but preserves its ERC-8004 DID (did:8004:bsc:worker-beta) onchain in MandateLog.sol. This ensures malicious LLMs cannot respawn clean identities.
Agent Risk Scoring & Credit Engine (agentRiskScore)
LPs and protocols can query the onchain credit score of any worker before delegating liquidity:
Verified BNB Smart Chain Testnet Contracts
Chain ID: 97All 6 core contracts are verified on BscScan. Click any card to inspect the live bytecode or copy the contract address:
MandantClient Core Methods Reference
client.createRoot(params)client.spawn(params)client.fundJob(params)client.revokeSubtree(mandateId)