| |
| pragma solidity ^0.8.20; |
|
|
| import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; |
| import "@openzeppelin/contracts/access/Ownable.sol"; |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| contract AgentIdentity8004 is ERC721, Ownable { |
| |
| |
|
|
| struct AgentProfile { |
| string name; |
| string description; |
| string version; |
| address controller; |
| uint256 createdAt; |
| uint256 reputationScore; |
| bool active; |
| } |
|
|
| struct Capability { |
| string name; |
| string description; |
| uint256 registeredAt; |
| bool verified; |
| } |
|
|
| struct Attestation { |
| address attester; |
| string attestationType; |
| bytes32 contentHash; |
| uint256 timestamp; |
| int256 scoreImpact; |
| } |
|
|
| struct DecisionReference { |
| uint256 timestamp; |
| bytes32 contentHash; |
| string decisionType; |
| int256 outcomeScore; |
| } |
|
|
| |
|
|
| uint256 private _nextTokenId; |
|
|
| |
| mapping(uint256 => AgentProfile) public agentProfiles; |
| |
| |
| mapping(uint256 => Capability[]) public agentCapabilities; |
| |
| |
| mapping(uint256 => Attestation[]) public agentAttestations; |
| |
| |
| mapping(uint256 => DecisionReference[]) public agentDecisions; |
| |
| |
| mapping(address => bool) public authorizedAttesters; |
|
|
| |
|
|
| event AgentRegistered(uint256 indexed tokenId, string name, address controller); |
| event CapabilityAdded(uint256 indexed tokenId, string capability); |
| event AttestationRecorded(uint256 indexed tokenId, address attester, string attestationType); |
| event DecisionRecorded(uint256 indexed tokenId, string decisionType, bytes32 contentHash); |
| event ReputationUpdated(uint256 indexed tokenId, uint256 newScore); |
| event AttesterAuthorized(address indexed attester, bool authorized); |
|
|
| |
|
|
| constructor() ERC721("RWA Yield Router Agent Identity", "RWAID") Ownable(msg.sender) { |
| _nextTokenId = 1; |
| } |
|
|
| |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| function registerAgent( |
| address controller, |
| string calldata name, |
| string calldata description, |
| string calldata version |
| ) external onlyOwner returns (uint256 tokenId) { |
| tokenId = _nextTokenId++; |
| _mint(controller, tokenId); |
| |
| agentProfiles[tokenId] = AgentProfile({ |
| name: name, |
| description: description, |
| version: version, |
| controller: controller, |
| createdAt: block.timestamp, |
| reputationScore: 5000, |
| active: true |
| }); |
| |
| emit AgentRegistered(tokenId, name, controller); |
| } |
|
|
| |
| |
| |
| |
| function agentOf(uint256 tokenId) external view returns (address) { |
| require(_ownerOf(tokenId) != address(0), "Agent does not exist"); |
| return agentProfiles[tokenId].controller; |
| } |
|
|
| |
| |
| |
| |
| function capabilities(uint256 tokenId) external view returns (Capability[] memory) { |
| return agentCapabilities[tokenId]; |
| } |
|
|
| |
| |
| |
| |
| function reputation(uint256 tokenId) external view returns (uint256) { |
| require(_ownerOf(tokenId) != address(0), "Agent does not exist"); |
| return agentProfiles[tokenId].reputationScore; |
| } |
|
|
| |
|
|
| |
| |
| |
| function addCapability( |
| uint256 tokenId, |
| string calldata name, |
| string calldata description |
| ) external { |
| require( |
| msg.sender == agentProfiles[tokenId].controller || msg.sender == owner(), |
| "Not authorized" |
| ); |
| |
| agentCapabilities[tokenId].push(Capability({ |
| name: name, |
| description: description, |
| registeredAt: block.timestamp, |
| verified: false |
| })); |
| |
| emit CapabilityAdded(tokenId, name); |
| } |
|
|
| |
| |
| |
| function verifyCapability( |
| uint256 tokenId, |
| uint256 capabilityIndex |
| ) external { |
| require(authorizedAttesters[msg.sender], "Not an authorized attester"); |
| require(capabilityIndex < agentCapabilities[tokenId].length, "Invalid index"); |
| |
| agentCapabilities[tokenId][capabilityIndex].verified = true; |
| } |
|
|
| |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| function attest( |
| uint256 tokenId, |
| string calldata attestationType, |
| bytes32 contentHash, |
| int256 scoreImpact |
| ) external { |
| require(authorizedAttesters[msg.sender], "Not an authorized attester"); |
| require(_ownerOf(tokenId) != address(0), "Agent does not exist"); |
| require(scoreImpact >= -1000 && scoreImpact <= 1000, "Impact out of range"); |
| |
| agentAttestations[tokenId].push(Attestation({ |
| attester: msg.sender, |
| attestationType: attestationType, |
| contentHash: contentHash, |
| timestamp: block.timestamp, |
| scoreImpact: scoreImpact |
| })); |
| |
| |
| _updateReputation(tokenId, scoreImpact); |
| |
| emit AttestationRecorded(tokenId, msg.sender, attestationType); |
| } |
|
|
| |
|
|
| |
| |
| |
| function recordDecision( |
| uint256 tokenId, |
| bytes32 contentHash, |
| string calldata decisionType |
| ) external { |
| require( |
| msg.sender == agentProfiles[tokenId].controller || msg.sender == owner(), |
| "Not authorized" |
| ); |
| |
| agentDecisions[tokenId].push(DecisionReference({ |
| timestamp: block.timestamp, |
| contentHash: contentHash, |
| decisionType: decisionType, |
| outcomeScore: 0 |
| })); |
| |
| emit DecisionRecorded(tokenId, decisionType, contentHash); |
| } |
|
|
| |
| |
| |
| function scoreDecision( |
| uint256 tokenId, |
| uint256 decisionIndex, |
| int256 outcomeScore |
| ) external { |
| require(authorizedAttesters[msg.sender], "Not an authorized attester"); |
| require(decisionIndex < agentDecisions[tokenId].length, "Invalid index"); |
| require(outcomeScore >= -10000 && outcomeScore <= 10000, "Score out of range"); |
| |
| agentDecisions[tokenId][decisionIndex].outcomeScore = outcomeScore; |
| |
| |
| int256 repImpact = outcomeScore / 100; |
| _updateReputation(tokenId, repImpact); |
| } |
|
|
| |
|
|
| function _updateReputation(uint256 tokenId, int256 impact) internal { |
| AgentProfile storage profile = agentProfiles[tokenId]; |
| int256 newScore = int256(profile.reputationScore) + impact; |
| |
| |
| if (newScore < 0) newScore = 0; |
| if (newScore > 10000) newScore = 10000; |
| |
| profile.reputationScore = uint256(newScore); |
| emit ReputationUpdated(tokenId, uint256(newScore)); |
| } |
|
|
| |
|
|
| function authorizeAttester(address attester, bool authorized) external onlyOwner { |
| authorizedAttesters[attester] = authorized; |
| emit AttesterAuthorized(attester, authorized); |
| } |
|
|
| function setAgentActive(uint256 tokenId, bool active) external onlyOwner { |
| agentProfiles[tokenId].active = active; |
| } |
|
|
| |
|
|
| function getAttestationCount(uint256 tokenId) external view returns (uint256) { |
| return agentAttestations[tokenId].length; |
| } |
|
|
| function getDecisionCount(uint256 tokenId) external view returns (uint256) { |
| return agentDecisions[tokenId].length; |
| } |
|
|
| function getCapabilityCount(uint256 tokenId) external view returns (uint256) { |
| return agentCapabilities[tokenId].length; |
| } |
|
|
| |
| |
| |
| function _update( |
| address to, |
| uint256 tokenId, |
| address auth |
| ) internal override returns (address) { |
| address from = _ownerOf(tokenId); |
| |
| if (from != address(0) && to != address(0)) { |
| revert("Agent identity is soulbound"); |
| } |
| return super._update(to, tokenId, auth); |
| } |
| } |
|
|