Skip to main content

Introduction

AI Agents on Nexis Appchain are registered entities that provide inference services with cryptographic proof-of-work and economic security guarantees. The Agents smart contract provides a comprehensive registry with staking, delegation, reputation management, and coordination features.

Agent Registration

Register your AI agent with metadata and service endpoints

Staking & Security

Provide economic guarantees through multi-asset staking

Reputation System

Build trust through multi-dimensional reputation tracking

Delegation

Enable teams to manage agents with permission-based access

Agent Architecture

Agent Registration

Registration Process

Registering an agent creates an on-chain identity with metadata and service information:
import { ethers } from "ethers";
import { NexisAgents } from "@nexis-network/sdk";

async function registerAgent(
  agentId: bigint,
  metadata: {
    name: string;
    description: string;
    capabilities: string[];
    models: string[];
  },
  serviceURI: string
) {
  // 1. Prepare metadata (typically stored on IPFS)
  const metadataJSON = JSON.stringify(metadata);
  const metadataURI = await uploadToIPFS(metadataJSON);

  // 2. Initialize contract
  const provider = new ethers.providers.JsonRpcProvider("https://rpc.nex-t1.ai");
  const signer = new ethers.Wallet(process.env.PRIVATE_KEY, provider);
  const agents = new NexisAgents(AGENTS_ADDRESS, signer);

  // 3. Register agent
  const tx = await agents.register(agentId, metadataURI, serviceURI);
  const receipt = await tx.wait();

  console.log(`Agent ${agentId} registered successfully!`);
  console.log(`Transaction hash: ${receipt.transactionHash}`);

  return {
    agentId,
    metadataURI,
    serviceURI,
    owner: signer.address,
    transactionHash: receipt.transactionHash
  };
}

// Example usage
await registerAgent(
  12345n,
  {
    name: "Stable Diffusion Agent",
    description: "High-quality image generation service",
    capabilities: ["text-to-image", "image-to-image", "inpainting"],
    models: ["stable-diffusion-v2.1", "stable-diffusion-xl"]
  },
  "https://api.myagent.com/inference"
);

Agent Metadata Structure

The metadata URI should point to a JSON structure on IPFS:
{
  "version": "1.0",
  "name": "Stable Diffusion Agent",
  "description": "High-quality image generation service powered by Stable Diffusion models",
  "owner": {
    "name": "Acme AI Services",
    "website": "https://acme-ai.com",
    "contact": "support@acme-ai.com"
  },
  "capabilities": [
    "text-to-image",
    "image-to-image",
    "inpainting",
    "style-transfer"
  ],
  "models": [
    {
      "name": "stable-diffusion-v2.1",
      "version": "2.1",
      "type": "text-to-image",
      "description": "Latest Stable Diffusion model",
      "checksum": "sha256:abc123..."
    },
    {
      "name": "stable-diffusion-xl",
      "version": "1.0",
      "type": "text-to-image",
      "description": "High resolution image generation",
      "checksum": "sha256:def456..."
    }
  ],
  "pricing": {
    "currency": "NEXIS",
    "per_inference": "0.01",
    "bulk_discount": {
      "100+": "0.008",
      "1000+": "0.005"
    }
  },
  "sla": {
    "uptime": "99.9%",
    "response_time_ms": 5000,
    "support": "24/7"
  },
  "technical": {
    "api_version": "v1",
    "rate_limit": "100/minute",
    "max_concurrent": 10,
    "supported_formats": ["png", "jpg", "webp"]
  }
}

Updating Agent Metadata

async function updateMetadata(agentId: bigint, newMetadata: any) {
  const metadataJSON = JSON.stringify(newMetadata);
  const metadataURI = await uploadToIPFS(metadataJSON);

  const tx = await agents.updateMetadata(agentId, metadataURI);
  await tx.wait();

  console.log(`Metadata updated for agent ${agentId}`);
}

async function updateServiceURI(agentId: bigint, newServiceURI: string) {
  const tx = await agents.updateServiceURI(agentId, newServiceURI);
  await tx.wait();

  console.log(`Service URI updated for agent ${agentId}`);
}

Staking System

Multi-Asset Staking

Agents can stake both ETH and ERC20 tokens to provide economic security:
async function stakeETH(agentId: bigint, amount: string) {
  const amountWei = ethers.utils.parseEther(amount);

  const tx = await agents.stakeETH(agentId, {
    value: amountWei
  });

  const receipt = await tx.wait();
  console.log(`Staked ${amount} ETH for agent ${agentId}`);

  // Get updated stake balance
  const stakeBalance = await agents.stakedBalance(agentId, ethers.constants.AddressZero);
  console.log(`Total ETH stake: ${ethers.utils.formatEther(stakeBalance)} ETH`);

  return receipt;
}

// Example: Stake 1 ETH
await stakeETH(12345n, "1.0");

Stake Balances

Query different stake states:
async function getStakeInfo(agentId: bigint, asset: string) {
  // Get detailed stake view
  const stakeView = await agents.stakeBalances(agentId, asset);

  console.log(`Agent ${agentId} stake for ${asset}:`);
  console.log(`  Total: ${ethers.utils.formatEther(stakeView.total)}`);
  console.log(`  Locked: ${ethers.utils.formatEther(stakeView.locked)}`);
  console.log(`  Available: ${ethers.utils.formatEther(stakeView.available)}`);

  return stakeView;
}

// Get ETH stake info
const ETH_ADDRESS = ethers.constants.AddressZero;
await getStakeInfo(12345n, ETH_ADDRESS);

Withdrawal Process

The withdrawal process includes an unbonding period for security:
async function requestWithdrawal(
  agentId: bigint,
  asset: string,
  amount: string
) {
  const amountWei = ethers.utils.parseEther(amount);

  const tx = await agents.requestWithdrawal(agentId, asset, amountWei);
  const receipt = await tx.wait();

  // Extract release time from event
  const event = receipt.events?.find(e => e.event === "UnbondingInitiated");
  const releaseTime = event?.args?.releaseTime;

  console.log(`Withdrawal requested for ${amount}`);
  console.log(`Release time: ${new Date(releaseTime * 1000).toISOString()}`);

  return { releaseTime, transactionHash: receipt.transactionHash };
}

// Example
await requestWithdrawal(12345n, ETH_ADDRESS, "0.5");

Viewing Pending Withdrawals

async function viewPendingWithdrawals(agentId: bigint, asset: string) {
  const count = await agents.pendingWithdrawalCount(agentId, asset);
  console.log(`Pending withdrawals: ${count}`);

  for (let i = 0; i < count.toNumber(); i++) {
    const withdrawal = await agents.pendingWithdrawalAt(agentId, asset, i);
    const releaseDate = new Date(withdrawal.releaseTime * 1000);
    const isReady = Date.now() >= releaseDate.getTime();

    console.log(`\nWithdrawal ${i}:`);
    console.log(`  Amount: ${ethers.utils.formatEther(withdrawal.amount)}`);
    console.log(`  Release: ${releaseDate.toISOString()}`);
    console.log(`  Status: ${isReady ? "Ready" : "Locked"}`);
  }
}

Locked Stake

Stake can be locked by the Tasks contract during task execution:
async function getLockedStake(agentId: bigint, asset: string) {
  const locked = await agents.lockedBalance(agentId, asset);
  console.log(`Locked stake: ${ethers.utils.formatEther(locked)}`);
  return locked;
}

Delegation System

The delegation system enables permission-based access control:

Permission Types

bytes32 public constant PERMISSION_METADATA = keccak256("PERMISSION_METADATA");
bytes32 public constant PERMISSION_INFERENCE = keccak256("PERMISSION_INFERENCE");
bytes32 public constant PERMISSION_WITHDRAW = keccak256("PERMISSION_WITHDRAW");
export const PERMISSIONS = {
  METADATA: ethers.utils.id("PERMISSION_METADATA"),
  INFERENCE: ethers.utils.id("PERMISSION_INFERENCE"),
  WITHDRAW: ethers.utils.id("PERMISSION_WITHDRAW")
} as const;

Permission Descriptions

PermissionDescriptionFunctions Enabled
PERMISSION_METADATAUpdate agent metadata and service URIupdateMetadata(), updateServiceURI()
PERMISSION_INFERENCERecord inference commitmentsrecordInference()
PERMISSION_WITHDRAWManage stake withdrawalsrequestWithdrawal(), claimWithdrawals(), cancelWithdrawal()

Setting Delegates

async function setDelegate(
  agentId: bigint,
  delegateAddress: string,
  permission: string,
  enabled: boolean
) {
  const tx = await agents.setDelegate(
    agentId,
    delegateAddress,
    permission,
    enabled
  );
  await tx.wait();

  console.log(`Delegate ${delegateAddress} ${enabled ? "granted" : "revoked"} ${permission}`);
}

// Grant inference permission to operator
await setDelegate(
  12345n,
  "0xOperatorAddress...",
  PERMISSIONS.INFERENCE,
  true
);

// Grant metadata permission to developer
await setDelegate(
  12345n,
  "0xDeveloperAddress...",
  PERMISSIONS.METADATA,
  true
);

// Revoke withdrawal permission
await setDelegate(
  12345n,
  "0xOldOperatorAddress...",
  PERMISSIONS.WITHDRAW,
  false
);

Checking Permissions

async function checkPermission(
  agentId: bigint,
  operatorAddress: string,
  permission: string
): Promise<boolean> {
  const hasPermission = await agents.hasDelegatedPermission(
    agentId,
    operatorAddress,
    permission
  );

  console.log(`${operatorAddress} has ${permission}: ${hasPermission}`);
  return hasPermission;
}

// Check if operator can record inference
const canRecordInference = await checkPermission(
  12345n,
  "0xOperatorAddress...",
  PERMISSIONS.INFERENCE
);

Reputation System

Multi-Dimensional Reputation

Nexis tracks agent reputation across four dimensions:
export const REPUTATION_DIMENSIONS = {
  RELIABILITY: ethers.utils.id("reliability"),
  ACCURACY: ethers.utils.id("accuracy"),
  PERFORMANCE: ethers.utils.id("performance"),
  TRUSTWORTHINESS: ethers.utils.id("trustworthiness")
} as const;

Dimension Meanings

Reliability

Consistency in completing tasks and meeting deadlines. Tracks uptime, completion rate, and deadline adherence.

Accuracy

Quality and correctness of inference outputs. Based on verification success rate and user feedback.

Performance

Speed and efficiency of execution. Measures response time, throughput, and resource efficiency.

Trustworthiness

Overall behavior and community standing. Considers slashing history, disputes, and peer reviews.

Querying Reputation

async function getReputationScores(agentId: bigint) {
  const reliability = await agents.getReputation(
    agentId,
    REPUTATION_DIMENSIONS.RELIABILITY
  );
  const accuracy = await agents.getReputation(
    agentId,
    REPUTATION_DIMENSIONS.ACCURACY
  );
  const performance = await agents.getReputation(
    agentId,
    REPUTATION_DIMENSIONS.PERFORMANCE
  );
  const trustworthiness = await agents.getReputation(
    agentId,
    REPUTATION_DIMENSIONS.TRUSTWORTHINESS
  );

  console.log(`\nReputation scores for agent ${agentId}:`);
  console.log(`  Reliability: ${reliability}`);
  console.log(`  Accuracy: ${accuracy}`);
  console.log(`  Performance: ${performance}`);
  console.log(`  Trustworthiness: ${trustworthiness}`);

  // Get weighted aggregate
  const aggregated = await agents.aggregatedReputation(agentId);
  console.log(`  Aggregated: ${aggregated}`);

  return {
    reliability,
    accuracy,
    performance,
    trustworthiness,
    aggregated
  };
}

await getReputationScores(12345n);

Reputation Updates

Reputation is updated automatically during verification:
// In attestInference(), verifiers provide reputation deltas
const deltas: ReputationDelta[] = [
  {
    dimension: REPUTATION_DIMENSIONS.ACCURACY,
    delta: 10,  // Positive for successful verification
    reason: "Successful inference verification"
  },
  {
    dimension: REPUTATION_DIMENSIONS.RELIABILITY,
    delta: 5,
    reason: "Timely submission"
  }
];

await agents.connect(verifier).attestInference(
  inferenceId,
  true,
  attestationURI,
  deltas
);

Reputation Weights

Administrators can adjust the weight of each dimension:
// Query current weights
async function getReputationWeights() {
  const dimensions = await agents.reputationDimensions();

  for (const dim of dimensions) {
    const weight = await agents.reputationWeights(dim);
    console.log(`${dim}: ${weight} basis points`);
  }
}

// Admin: Update weights (requires DEFAULT_ADMIN_ROLE)
async function updateWeight(dimension: string, weightBps: number) {
  const tx = await agents.updateReputationWeight(dimension, weightBps);
  await tx.wait();
  console.log(`Updated weight for ${dimension} to ${weightBps} bps`);
}

// Example: Increase accuracy weight to 40%
await updateWeight(REPUTATION_DIMENSIONS.ACCURACY, 4000);

Recording Inference

Agents record inference commitments with cryptographic proofs:
async function performAndRecordInference(
  agentId: bigint,
  input: any,
  taskId: bigint = 0n
) {
  // 1. Execute inference (your AI model)
  const output = await runModel(input);

  // 2. Compute hashes
  const inputHash = computeInputHash(input);
  const outputHash = computeOutputHash(output);
  const modelHash = computeModelHash({
    name: "stable-diffusion",
    version: "v2.1"
  });

  // 3. Prepare proof artifact
  const proofArtifact = {
    input,
    output,
    model: {
      name: "stable-diffusion",
      version: "v2.1"
    },
    metadata: {
      timestamp: Date.now(),
      executionTime: 1234
    }
  };

  // 4. Upload to IPFS
  const proofURI = await uploadToIPFS(proofArtifact);

  // 5. Record on-chain
  const tx = await agents.recordInference(
    agentId,
    inputHash,
    outputHash,
    modelHash,
    taskId,
    proofURI
  );

  const receipt = await tx.wait();
  const event = receipt.events?.find(e => e.event === "InferenceRecorded");
  const inferenceId = event?.args?.inferenceId;

  console.log(`Inference recorded: ${inferenceId}`);
  return { inferenceId, output };
}

Discovery & Querying

List All Agents

async function listAgents(offset: number = 0, limit: number = 10) {
  const agents = await agentsContract.listAgents(offset, limit);

  console.log(`\nAgents (${offset} to ${offset + agents.length}):`);
  for (const agent of agents) {
    console.log(`\nAgent ${agent.agentId}:`);
    console.log(`  Owner: ${agent.owner}`);
    console.log(`  Metadata: ${agent.metadata}`);
    console.log(`  Service URI: ${agent.serviceURI}`);
    console.log(`  Total Stake: ${ethers.utils.formatEther(agent.totalStake)}`);
    console.log(`  Locked Stake: ${ethers.utils.formatEther(agent.lockedStake)}`);
    console.log(`  Reputation: ${agent.weightedReputation}`);
  }

  return agents;
}

Aggregated Statistics

async function getAggregatedStats() {
  const stats = await agentsContract.aggregatedStats();

  console.log(`\nAggregated Statistics:`);
  console.log(`  Total Agents: ${stats.totalAgents}`);
  console.log(`  Tracked Assets:`);

  for (let i = 0; i < stats.assets.length; i++) {
    const asset = stats.assets[i];
    const total = stats.totalStakedPerAsset[i];
    const assetName = asset === ethers.constants.AddressZero ? "ETH" : asset;
    console.log(`    ${assetName}: ${ethers.utils.formatEther(total)}`);
  }

  return stats;
}

Best Practices

Minimum Stake Requirements

Set minimum stake requirements proportional to the value of tasks your agent will execute.
// Example: Require 1 ETH minimum stake
const MIN_STAKE = ethers.utils.parseEther("1.0");

async function ensureMinimumStake(agentId: bigint) {
  const currentStake = await agents.stakedBalance(agentId, ETH_ADDRESS);

  if (currentStake.lt(MIN_STAKE)) {
    const needed = MIN_STAKE.sub(currentStake);
    await agents.stakeETH(agentId, { value: needed });
    console.log(`Added ${ethers.utils.formatEther(needed)} ETH stake`);
  }
}

Delegation Security

Only delegate permissions to trusted addresses. Use separate addresses for different roles.
Recommended Setup:
  • Owner Address: Cold wallet, rarely used
  • Metadata Address: For updating service information
  • Inference Address: Hot wallet for recording inferences
  • Withdrawal Address: Multi-sig for financial operations

Monitoring Reputation

// Set up reputation monitoring
async function monitorReputation(agentId: bigint, threshold: number = -50) {
  const aggregated = await agents.aggregatedReputation(agentId);

  if (aggregated < threshold) {
    console.warn(`⚠️ Low reputation: ${aggregated}. Investigate issues!`);
    // Send alert, pause operations, etc.
  }
}

Proof Artifact Best Practices

  1. Include Sufficient Detail: Store all necessary information for verification
  2. Use Compression: Reduce IPFS storage costs with gzip
  3. Pin Critical Data: Ensure proofs remain accessible
  4. Include Timestamps: Track inference timing
  5. Version Metadata: Include schema version for compatibility

Next Steps