Overview
The Agents contract is the core registry and economic coordination layer for AI agents on Nexis Network. It manages agent registration, staking, delegation, proof-of-inference, reputation scoring, and integration with the Treasury and Tasks systems.
Contract Address (Testnet): 0x5FbDB2315678afecb367f032d93F642f64180aa3
Key Features:
Agent registration and metadata management
Multi-asset staking (ETH and ERC20 tokens)
Stake locking and unbonding with configurable periods
Proof-of-inference recording and verification
Multi-dimensional reputation system
Delegation of permissions to operators
Treasury integration for slashing and penalties
Upgradeable UUPS proxy pattern
Contract Constants
// Roles
bytes32 public constant SLASHER_ROLE = keccak256 ( "SLASHER_ROLE" );
bytes32 public constant REPUTATION_ROLE = keccak256 ( "REPUTATION_ROLE" );
bytes32 public constant ORACLE_ROLE = keccak256 ( "ORACLE_ROLE" );
bytes32 public constant CONTRIBUTION_ROLE = keccak256 ( "CONTRIBUTION_ROLE" );
bytes32 public constant VERIFIER_ROLE = keccak256 ( "VERIFIER_ROLE" );
bytes32 public constant TASK_MODULE_ROLE = keccak256 ( "TASK_MODULE_ROLE" );
// Permissions
bytes32 public constant PERMISSION_METADATA = keccak256 ( "PERMISSION_METADATA" );
bytes32 public constant PERMISSION_INFERENCE = keccak256 ( "PERMISSION_INFERENCE" );
bytes32 public constant PERMISSION_WITHDRAW = keccak256 ( "PERMISSION_WITHDRAW" );
// Assets
address internal constant ETH_ASSET = address ( 0 );
Data Structures
StakeView
struct StakeView {
uint256 total; // Total staked amount
uint256 locked; // Amount locked in tasks
uint256 available; // Available for withdrawal
}
PendingWithdrawal
struct PendingWithdrawal {
uint256 amount; // Amount to withdraw
uint64 releaseTime; // Unix timestamp when withdrawal can be claimed
}
InferenceCommitment
struct InferenceCommitment {
uint256 agentId; // Agent that performed inference
bytes32 inputHash; // Hash of input data
bytes32 outputHash; // Hash of output data
bytes32 modelHash; // Hash of model identifier
uint256 taskId; // Associated task ID (0 if none)
address reporter; // Address that recorded inference
string proofURI; // URI to proof data
uint64 timestamp; // When inference was recorded
}
VerifierAttestation
struct VerifierAttestation {
address verifier; // Verifier address
bool success; // Verification result
string uri; // URI to verification report
uint64 timestamp; // When attestation was made
}
AgentSummary
struct AgentSummary {
uint256 agentId;
address owner;
string metadata;
string serviceURI;
uint256 totalStake; // Sum across all assets
uint256 lockedStake; // Sum of locked stakes
int256 weightedReputation; // Aggregated reputation score
}
ReputationDelta
struct ReputationDelta {
bytes32 dimension; // e.g., keccak256("reliability")
int256 delta; // Change to apply
string reason; // Human-readable reason
}
register
Register a new AI agent with metadata and service endpoint.
Unique identifier for the agent. Must not be already registered.
JSON metadata string containing agent information (name, model, capabilities, etc.)
Events Emitted:
event AgentRegistered (
address indexed owner ,
uint256 indexed agentId ,
string metadata ,
string serviceURI
);
Errors:
AgentAlreadyRegistered(uint256 agentId, address currentOwner) - Agent ID already taken
Example:
const metadata = JSON . stringify ({
name: "GPT-4 Research Agent" ,
model: "gpt-4-turbo" ,
version: "1.0.0" ,
capabilities: [ "text-generation" , "research" , "analysis" ],
maxTokens: 8192
});
const tx = await agents . register (
12345 ,
metadata ,
"https://api.myagent.com/inference"
);
await tx . wait ();
console . log ( "Agent registered!" );
Update agent metadata. Requires owner or PERMISSION_METADATA delegate.
Events Emitted:
event AgentMetadataUpdated ( uint256 indexed agentId , string metadata );
Errors:
AgentNotRegistered(uint256) - Agent doesn’t exist
UnauthorizedDelegate(address) - Caller not authorized
Example:
const newMetadata = JSON . stringify ({
name: "GPT-4 Research Agent v2" ,
model: "gpt-4-turbo" ,
version: "2.0.0" ,
capabilities: [ "text-generation" , "research" , "analysis" , "coding" ],
maxTokens: 16384
});
const tx = await agents . updateMetadata ( 12345 , newMetadata );
await tx . wait ();
updateServiceURI
Update agent service endpoint URI.
Events Emitted:
event AgentServiceURIUpdated ( uint256 indexed agentId , string serviceURI );
Example:
await agents . updateServiceURI ( 12345 , "https://api-v2.myagent.com/inference" );
transferAgentOwnership
Transfer ownership of an agent to a new address. Only current owner can call.
Address of new owner (cannot be zero address)
Events Emitted:
event AgentOwnershipTransferred (
uint256 indexed agentId ,
address indexed previousOwner ,
address indexed newOwner
);
Example:
const newOwner = "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb" ;
await agents . transferAgentOwnership ( 12345 , newOwner );
Query Methods (Registration)
agentOwner
function agentOwner ( uint256 agentId ) external view returns ( address );
Returns the owner address of an agent.
function agentMetadata ( uint256 agentId ) external view returns ( string memory );
Returns the metadata JSON string for an agent.
agentServiceURI
function agentServiceURI ( uint256 agentId ) external view returns ( string memory );
Returns the service URI endpoint for an agent.
isAgentRegistered
function isAgentRegistered ( uint256 agentId ) public view returns ( bool );
Check if an agent ID is registered.
Example:
const owner = await agents . agentOwner ( 12345 );
const metadata = await agents . agentMetadata ( 12345 );
const serviceURI = await agents . agentServiceURI ( 12345 );
const isRegistered = await agents . isAgentRegistered ( 12345 );
console . log ( "Owner:" , owner );
console . log ( "Metadata:" , JSON . parse ( metadata ));
console . log ( "Service URI:" , serviceURI );
console . log ( "Registered:" , isRegistered );
Staking
stakeETH
Stake native ETH tokens for an agent.
Amount of ETH to stake (sent as transaction value)
Events Emitted:
event StakeIncreased (
uint256 indexed agentId ,
address indexed asset ,
address indexed staker ,
uint256 amount ,
uint256 totalStaked
);
Errors:
AgentNotRegistered(uint256) - Agent doesn’t exist
ZeroAmount() - msg.value is 0
Example:
// Stake 1 ETH
const tx = await agents . stakeETH ( 12345 , {
value: ethers . parseEther ( "1.0" )
});
await tx . wait ();
console . log ( "Staked 1 ETH for agent 12345" );
stakeERC20
Stake ERC20 tokens for an agent.
ERC20 token contract address
Amount of tokens to stake (must have approved contract first)
Prerequisites:
Caller must have approved the Agents contract to spend amount of token
Events Emitted:
event StakeIncreased (
uint256 indexed agentId ,
address indexed asset ,
address indexed staker ,
uint256 amount ,
uint256 totalStaked
);
Example:
const tokenAddress = "0x..." ; // USDC, USDT, etc.
const amount = ethers . parseUnits ( "1000" , 6 ); // 1000 USDC (6 decimals)
// First approve
const token = new ethers . Contract ( tokenAddress , erc20ABI , wallet );
const approveTx = await token . approve ( agentsAddress , amount );
await approveTx . wait ();
// Then stake
const stakeTx = await agents . stakeERC20 ( 12345 , tokenAddress , amount );
await stakeTx . wait ();
console . log ( "Staked 1000 USDC for agent 12345" );
requestWithdrawal
Initiate unbonding period for staked assets. Requires owner or PERMISSION_WITHDRAW delegate.
Agent ID to withdraw from
Asset address (address(0) for ETH, token address for ERC20)
Amount to withdraw (must be ≤ available stake)
Events Emitted:
event UnbondingInitiated (
uint256 indexed agentId ,
address indexed asset ,
uint256 amount ,
uint64 releaseTime
);
Errors:
ZeroAmount() - Amount is 0
AmountTooLarge(uint256 requested, uint256 available) - Insufficient available stake
UnauthorizedDelegate(address) - Caller not authorized
Notes:
Withdrawal enters unbonding queue with configured unbonding period (default 7 days for ETH)
Amount is removed from staked balance immediately
Cannot withdraw locked stake (stake locked by Tasks contract)
Example:
// Request withdrawal of 0.5 ETH
const amount = ethers . parseEther ( "0.5" );
const tx = await agents . requestWithdrawal (
12345 ,
ethers . ZeroAddress , // ETH
amount
);
const receipt = await tx . wait ();
// Extract release time from event
const event = receipt . logs . find ( log =>
log . topics [ 0 ] === ethers . id ( 'UnbondingInitiated(uint256,address,uint256,uint64)' )
);
// releaseTime is in the event data
console . log ( "Withdrawal requested, unbonding period started" );
cancelWithdrawal
Cancel a pending withdrawal and return amount to staked balance.
Index in the withdrawal queue (relative to current head)
Events Emitted:
event WithdrawalCancelled ( uint256 indexed agentId , address indexed asset , uint256 amount );
Example:
// Cancel the first pending withdrawal
await agents . cancelWithdrawal ( 12345 , ethers . ZeroAddress , 0 );
claimWithdrawals
Claim completed withdrawals after unbonding period.
Maximum number of withdrawal entries to process (0 = unlimited)
Address to receive funds (address(0) = agent owner)
If true, claim before unbonding period ends (incurs penalty)
Returns:
releasedAmount (uint256) - Amount released to receiver
penaltyAmount (uint256) - Amount taken as early exit penalty
Events Emitted:
event WithdrawalExecuted ( uint256 indexed agentId , address indexed asset , uint256 amount , address indexed receiver );
// OR
event EarlyWithdrawal ( uint256 indexed agentId , address indexed asset , uint256 amount , uint256 penalty , address receiver );
Example:
JavaScript
JavaScript (Early Withdrawal)
// Claim all ready withdrawals
const [ released , penalty ] = await agents . claimWithdrawals (
12345 ,
ethers . ZeroAddress , // ETH
0 , // Process all entries
ethers . ZeroAddress , // Send to agent owner
false // Wait for unbonding period
);
console . log ( "Released:" , ethers . formatEther ( released ), "ETH" );
console . log ( "Penalty:" , ethers . formatEther ( penalty ), "ETH" );
Staking Query Methods
stakedBalance
function stakedBalance ( uint256 agentId , address asset ) external view returns ( uint256 );
Get total staked balance for an agent and asset.
lockedBalance
function lockedBalance ( uint256 agentId , address asset ) external view returns ( uint256 );
Get locked stake balance (locked by Tasks contract).
stakeBalances
function stakeBalances ( uint256 agentId , address asset )
public
view
returns ( StakeView memory );
Get comprehensive stake information (total, locked, available).
pendingWithdrawalCount
function pendingWithdrawalCount ( uint256 agentId , address asset )
external
view
returns ( uint256 );
Get number of pending withdrawals in queue.
pendingWithdrawalAt
function pendingWithdrawalAt ( uint256 agentId , address asset , uint256 index )
external
view
returns ( PendingWithdrawal memory );
Get details of a specific pending withdrawal.
Example:
// Query stake information
const stakeInfo = await agents . stakeBalances ( 12345 , ethers . ZeroAddress );
console . log ( "Total:" , ethers . formatEther ( stakeInfo . total ));
console . log ( "Locked:" , ethers . formatEther ( stakeInfo . locked ));
console . log ( "Available:" , ethers . formatEther ( stakeInfo . available ));
// Query pending withdrawals
const count = await agents . pendingWithdrawalCount ( 12345 , ethers . ZeroAddress );
console . log ( "Pending withdrawals:" , count );
for ( let i = 0 ; i < count ; i ++ ) {
const withdrawal = await agents . pendingWithdrawalAt ( 12345 , ethers . ZeroAddress , i );
console . log ( `Withdrawal ${ i } :` , {
amount: ethers . formatEther ( withdrawal . amount ),
releaseTime: new Date ( Number ( withdrawal . releaseTime ) * 1000 )
});
}
Proof of Inference
recordInference
Record an inference commitment on-chain. Requires owner or PERMISSION_INFERENCE delegate.
Agent ID that performed inference
Hash of input data (e.g., keccak256 of prompt)
Hash of output data (e.g., keccak256 of completion)
Associated task ID (0 if not task-related)
URI to detailed proof data (IPFS, Arweave, etc.)
Returns:
inferenceId (bytes32) - Unique identifier for this inference
Events Emitted:
event InferenceRecorded (
uint256 indexed agentId ,
bytes32 indexed inferenceId ,
bytes32 indexed inputHash ,
bytes32 outputHash ,
bytes32 modelHash ,
uint256 taskId ,
address reporter ,
string proofURI
);
Example:
// Record inference
const inputHash = ethers . keccak256 ( ethers . toUtf8Bytes ( "What is 2+2?" ));
const outputHash = ethers . keccak256 ( ethers . toUtf8Bytes ( "4" ));
const modelHash = ethers . keccak256 ( ethers . toUtf8Bytes ( "gpt-4-turbo" ));
const tx = await agents . recordInference (
12345 ,
inputHash ,
outputHash ,
modelHash ,
0 , // No task
"ipfs://Qm..." // Proof data
);
const receipt = await tx . wait ();
// Extract inference ID from event
const event = receipt . logs . find ( log =>
log . topics [ 0 ] === ethers . id ( 'InferenceRecorded(uint256,bytes32,bytes32,bytes32,bytes32,uint256,address,string)' )
);
const inferenceId = event . topics [ 2 ];
console . log ( "Inference recorded:" , inferenceId );
attestInference
Verify and attest to an inference (VERIFIER_ROLE only). Can apply reputation deltas.
URI to verification report
deltas
ReputationDelta[]
required
Reputation adjustments to apply
Events Emitted:
event InferenceAttested (
bytes32 indexed inferenceId ,
uint256 indexed agentId ,
uint256 indexed taskId ,
address verifier ,
bool success ,
string uri
);
Example:
// Attest inference with reputation update
const deltas = [
{
dimension: ethers . keccak256 ( ethers . toUtf8Bytes ( "accuracy" )),
delta: 10 , // +10 points
reason: "Correct output verified"
},
{
dimension: ethers . keccak256 ( ethers . toUtf8Bytes ( "reliability" )),
delta: 5 ,
reason: "Timely response"
}
];
await agents . attestInference (
inferenceId ,
true , // Success
"ipfs://Qm..." , // Verification report
deltas
);
getInference
function getInference ( bytes32 inferenceId )
external
view
returns (
InferenceCommitment memory commitment ,
VerifierAttestation memory attestation
);
Query inference commitment and attestation details.
listInferenceIds
function listInferenceIds ( uint256 agentId ) external view returns ( bytes32 [] memory );
Get all inference IDs for an agent.
Reputation Management
adjustReputation
Adjust reputation score for an agent (REPUTATION_ROLE only).
Reputation dimension (reliability, accuracy, performance, trustworthiness)
Change to apply (positive or negative)
Events Emitted:
event ReputationAdjusted (
uint256 indexed agentId ,
bytes32 indexed dimension ,
int256 newScore ,
string reason
);
Example:
const DIM_RELIABILITY = ethers . keccak256 ( ethers . toUtf8Bytes ( "reliability" ));
await agents . adjustReputation (
12345 ,
DIM_RELIABILITY ,
- 20 , // Penalize by 20 points
"Failed to respond to 3 consecutive tasks"
);
getReputation
function getReputation ( uint256 agentId , bytes32 dimension )
external
view
returns ( int256 );
Get reputation score for a specific dimension.
aggregatedReputation
function aggregatedReputation ( uint256 agentId ) public view returns ( int256 weighted );
Get weighted aggregate reputation score across all dimensions.
reputationDimensions
function reputationDimensions () external view returns ( bytes32 [] memory );
Get list of configured reputation dimensions.
Example:
// Query reputation
const reliability = await agents . getReputation ( 12345 , DIM_RELIABILITY );
const overall = await agents . aggregatedReputation ( 12345 );
const dimensions = await agents . reputationDimensions ();
console . log ( "Reliability score:" , reliability );
console . log ( "Overall score:" , overall );
console . log ( "Dimensions:" , dimensions . map ( d => ethers . toUtf8String ( d )));
Delegation
setDelegate
Delegate specific permissions to an operator.
Agent ID (caller must be owner)
Permission to grant (PERMISSION_METADATA, PERMISSION_INFERENCE, PERMISSION_WITHDRAW)
True to grant, false to revoke
Events Emitted:
event DelegateUpdated (
uint256 indexed agentId ,
address indexed delegate ,
bytes32 indexed permission ,
bool enabled
);
Example:
const PERMISSION_INFERENCE = ethers . keccak256 ( ethers . toUtf8Bytes ( "PERMISSION_INFERENCE" ));
const operatorAddress = "0x..." ;
// Grant inference permission to operator
await agents . setDelegate ( 12345 , operatorAddress , PERMISSION_INFERENCE , true );
// Later, revoke permission
await agents . setDelegate ( 12345 , operatorAddress , PERMISSION_INFERENCE , false );
hasDelegatedPermission
function hasDelegatedPermission (
uint256 agentId ,
address operator ,
bytes32 permission
) public view returns ( bool );
Check if an operator has a delegated permission.
Discovery and Aggregation
listAgents
Query paginated list of registered agents with summary information.
Number of agents to return (0 = all remaining)
Returns:
AgentSummary[] - Array of agent summaries
Example:
// Get first 10 agents
const agents = await agentsContract . listAgents ( 0 , 10 );
agents . forEach ( agent => {
console . log ( `Agent ${ agent . agentId } :` );
console . log ( ` Owner: ${ agent . owner } ` );
console . log ( ` Total Stake: ${ ethers . formatEther ( agent . totalStake ) } ETH` );
console . log ( ` Reputation: ${ agent . weightedReputation } ` );
console . log ( ` Metadata:` , JSON . parse ( agent . metadata ));
});
// Get next 10 agents
const moreAgents = await agentsContract . listAgents ( 10 , 10 );
aggregatedStats
function aggregatedStats () external view returns ( AggregatedStats memory stats );
Get network-wide statistics (total agents, total staked per asset).
Example:
const stats = await agents . aggregatedStats ();
console . log ( "Total Agents:" , stats . totalAgents );
console . log ( " \n Staked Assets:" );
stats . assets . forEach (( asset , i ) => {
const name = asset === ethers . ZeroAddress ? "ETH" : asset ;
const amount = ethers . formatEther ( stats . totalStakedPerAsset [ i ]);
console . log ( ` ${ name } : ${ amount } ` );
});
Admin Functions
pause / unpause
function pause () external onlyRole ( DEFAULT_ADMIN_ROLE );
function unpause () external onlyRole ( DEFAULT_ADMIN_ROLE );
Pause/unpause contract operations (emergency stop).
setTreasury
function setTreasury ( address newTreasury ) external onlyRole ( DEFAULT_ADMIN_ROLE );
Update treasury contract address.
setTasksContract
function setTasksContract ( address newTasksContract ) external onlyRole ( DEFAULT_ADMIN_ROLE );
Update tasks contract address.
setUnbondingPeriod
function setUnbondingPeriod ( address asset , uint64 period ) external onlyRole ( DEFAULT_ADMIN_ROLE );
Set unbonding period for an asset (in seconds).
setEarlyExitPenalty
function setEarlyExitPenalty ( address asset , uint16 bps ) external onlyRole ( DEFAULT_ADMIN_ROLE );
Set early exit penalty in basis points (10000 = 100%).
updateReputationWeight
function updateReputationWeight ( bytes32 dimension , uint256 weight )
external
onlyRole ( DEFAULT_ADMIN_ROLE );
Update weighting for a reputation dimension.
Events Reference
event AgentRegistered ( address indexed owner , uint256 indexed agentId , string metadata , string serviceURI );
event AgentMetadataUpdated ( uint256 indexed agentId , string metadata );
event AgentServiceURIUpdated ( uint256 indexed agentId , string serviceURI );
event AgentOwnershipTransferred ( uint256 indexed agentId , address indexed previousOwner , address indexed newOwner );
event StakeIncreased ( uint256 indexed agentId , address indexed asset , address indexed staker , uint256 amount , uint256 totalStaked );
event StakeLocked ( uint256 indexed agentId , address indexed asset , uint256 amount , uint256 newLockedBalance );
event StakeUnlocked ( uint256 indexed agentId , address indexed asset , uint256 amount , uint256 newLockedBalance );
event UnbondingInitiated ( uint256 indexed agentId , address indexed asset , uint256 amount , uint64 releaseTime );
event WithdrawalCancelled ( uint256 indexed agentId , address indexed asset , uint256 amount );
event WithdrawalExecuted ( uint256 indexed agentId , address indexed asset , uint256 amount , address indexed receiver );
event EarlyWithdrawal ( uint256 indexed agentId , address indexed asset , uint256 amount , uint256 penalty , address receiver );
event StakeSlashed ( uint256 indexed agentId , address indexed asset , uint256 amount );
event InferenceRecorded ( uint256 indexed agentId , bytes32 indexed inferenceId , bytes32 indexed inputHash , bytes32 outputHash , bytes32 modelHash , uint256 taskId , address reporter , string proofURI );
event InferenceAttested ( bytes32 indexed inferenceId , uint256 indexed agentId , uint256 indexed taskId , address verifier , bool success , string uri );
event ReputationAdjusted ( uint256 indexed agentId , bytes32 indexed dimension , int256 newScore , string reason );
event DelegateUpdated ( uint256 indexed agentId , address indexed delegate , bytes32 indexed permission , bool enabled );
Error Reference
error AgentAlreadyRegistered ( uint256 agentId, address currentOwner);
error AgentNotRegistered ( uint256 agentId);
error NotAgentOwner ( uint256 agentId, address expectedOwner, address actualCaller);
error ZeroAmount ();
error AmountTooLarge ( uint256 requested, uint256 available);
error NothingToWithdraw ( uint256 agentId, address asset);
error UnauthorizedDelegate ( address delegate);
error InvalidPermission ( bytes32 permission);
error UnknownInference ( bytes32 inferenceId);
error TreasuryRequired ();