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:
TypeScript
Python
Solidity - Contract Function
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 (
12345 n ,
{
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"
);
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" : "[email protected] "
},
"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" ]
}
}
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:
Stake ETH
Stake ERC20 Tokens
Python - Stake ETH
Python - Stake ERC20
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 ( 12345 n , "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 ( 12345 n , ETH_ADDRESS );
Withdrawal Process
The withdrawal process includes an unbonding period for security:
Request Withdrawal
Claim Withdrawals
Cancel Withdrawal
Python - Request Withdrawal
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 ( 12345 n , 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 ( ` \n Withdrawal ${ 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:
Query Locked Stake
Lock/Unlock (Task Module Only)
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" );
TypeScript Constants
Python Constants
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
Permission Description Functions Enabled PERMISSION_METADATAUpdate agent metadata and service URI updateMetadata(), updateServiceURI()PERMISSION_INFERENCERecord inference commitments recordInference()PERMISSION_WITHDRAWManage stake withdrawals requestWithdrawal(), 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 (
12345 n ,
"0xOperatorAddress..." ,
PERMISSIONS . INFERENCE ,
true
);
// Grant metadata permission to developer
await setDelegate (
12345 n ,
"0xDeveloperAddress..." ,
PERMISSIONS . METADATA ,
true
);
// Revoke withdrawal permission
await setDelegate (
12345 n ,
"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 (
12345 n ,
"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 ( ` \n Reputation 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 ( 12345 n );
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 = 0 n
) {
// 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 ( ` \n Agents ( ${ offset } to ${ offset + agents . length } ):` );
for ( const agent of agents ) {
console . log ( ` \n Agent ${ 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 ( ` \n Aggregated 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
Include Sufficient Detail : Store all necessary information for verification
Use Compression : Reduce IPFS storage costs with gzip
Pin Critical Data : Ensure proofs remain accessible
Include Timestamps : Track inference timing
Version Metadata : Include schema version for compatibility
Next Steps