Skip to main content

Installation

Install
npm install @sudont/sdk
The SDK requires Node.js 18+.

Initialize the Client

SudontClient is the main entry point. Pass your local Sudont sidecar URL and the target VM type.
import { SudontClient } from '@sudont/sdk';

const client = new SudontClient(
  'http://localhost:8545',  // Sudont sidecar URL
  'evm'                     // vmType: routes to sudont-cage (revm)
);

Send Your First Transaction

Use sendTransaction to route a signed raw transaction through the Sudont firewall. If the transaction violates Constitution policy, a structured SudontError is thrown — never a generic revert string.
Send EVM Transaction
import { SudontClient } from '@sudont/sdk';
import { Wallet, parseUnits } from 'ethers';

const client = new SudontClient('http://localhost:8545', 'evm');

// Sign your transaction as usual with ethers
const wallet = new Wallet(process.env.PRIVATE_KEY!, client.provider);
const signedTx = await wallet.signTransaction({
  to: '0x...',
  data: '0x...',   // Uniswap V3 exactInputSingle calldata
  value: 0n,
  gasLimit: 200_000n,
});

try {
  const tx = await client.sendTransaction(signedTx);
  console.log('Approved and broadcast:', tx.hash);
} catch (err) {
  // SudontError contains machine-readable diagnosis
  console.error('Blocked by firewall:', err.message);
  console.error('Reason code:', err.data?.sudont?.reason_code);
  // e.g. "SUDONT_SLIPPAGE_EXCEEDED"
}

Reading the Verdict

When a transaction is blocked, Sudont returns a standard JSON-RPC error shape with structured diagnosis:
Blocked Transaction Response
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32000,
    "message": "Transaction blocked by Sudont policy",
    "data": {
      "sudont": {
        "reason_code": "SUDONT_SLIPPAGE_EXCEEDED",
        "severity": "high",
        "observed": {
          "slippage_bps": 312,
          "max_allowed_bps": 100
        },
        "actionable_feedback": "Reduce trade size or increase slippage tolerance.",
        "policy_hits": ["max_slippage_bps"],
        "original_revert_data": "0x..."
      }
    }
  }
}
The SDK’s parseRpcError helper extracts this into a typed SudontError with all fields accessible as properties — no manual JSON parsing required.

Handling Interrogation

When a transaction triggers a medium-risk signal — an unlimited approval, an unverified spender, or a non-allowlisted destination — Sudont returns an INTERROGATE response instead of a hard block. Your agent must handle this to complete the ReAct loop.
The response includes actionable_feedback: a structured, machine-readable instruction telling the agent exactly what correction is required.
ReAct Loop — Agent Self-Correction
import { SudontClient } from '@sudont/sdk';
import { Wallet, MaxUint256, parseUnits } from 'ethers';

const client = new SudontClient('http://localhost:8545', 'evm');
const wallet = new Wallet(process.env.PRIVATE_KEY!, client.provider);

// --- Step 1: Agent submits its initial transaction ---
// The agent naively requests an unlimited token approval.
const unlimitedApproval = await wallet.signTransaction({
  to: '0xTokenContractAddress',
  data: encodeApproveCalldata('0xSpenderAddress', MaxUint256),
  value: 0n,
  gasLimit: 60_000n,
});

let result;
try {
  result = await client.sendTransaction(unlimitedApproval);
  console.log('Approved:', result.hash);
} catch (err) {
  const sudont = err.data?.sudont;

  // --- Step 2: Agent receives INTERROGATE response ---
  if (sudont?.reason_code === 'SUDONT_INTERROGATE') {
    console.log('Interrogation triggered.');
    console.log('Feedback:', sudont.actionable_feedback);
    // e.g. "Approval amount exceeds maximum. Reduce to exact trade amount."

    const exactAmount = parseUnits(
      sudont.observed?.required_amount ?? '0', 6
    );

    // --- Step 3: Agent self-corrects ---
    const correctedApproval = await wallet.signTransaction({
      to: '0xTokenContractAddress',
      data: encodeApproveCalldata('0xSpenderAddress', exactAmount),
      value: 0n,
      gasLimit: 60_000n,
    });

    // --- Step 4: Agent resubmits ---
    try {
      result = await client.sendTransaction(correctedApproval);
      console.log('Corrected approval accepted:', result.hash);
    } catch (retryErr) {
      console.error('Correction rejected:', retryErr.data?.sudont?.reason_code);
    }
  } else {
    // Hard block — no retry warranted
    console.error('Hard block:', sudont?.reason_code);
  }
}
The adversarial record is written regardless of outcome. Every interrogation — including successful self-corrections — is logged with the original request, the challenge issued, and the agent’s response. Auditors can reconstruct the full correction sequence from the event stream without access to the code.

Custom Diagnosis Methods

Beyond standard JSON-RPC, Sudont exposes diagnosis methods you can call directly:
const policy = await client.provider.send('sudont_getPolicySnapshot', []);
console.log('Max slippage bps:', policy.max_slippage_bps);