From 501676ff939cf949ab3e442b0b19bc09c2bd2590 Mon Sep 17 00:00:00 2001 From: oib Date: Fri, 27 Feb 2026 22:54:37 +0100 Subject: [PATCH] feat: recreate AgentWallet.sol based on docs --- contracts/contracts/AgentWallet.sol | 191 ++++++++++++++++++++++++++++ 1 file changed, 191 insertions(+) create mode 100644 contracts/contracts/AgentWallet.sol diff --git a/contracts/contracts/AgentWallet.sol b/contracts/contracts/AgentWallet.sol new file mode 100644 index 00000000..6017d8ff --- /dev/null +++ b/contracts/contracts/AgentWallet.sol @@ -0,0 +1,191 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import "@openzeppelin/contracts/access/Ownable.sol"; +import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; + +/** + * @title AgentWallet + * @dev Isolated agent-specific wallet for micro-transactions, funded by user allowances. + */ +contract AgentWallet is Ownable, ReentrancyGuard { + using SafeERC20 for IERC20; + + IERC20 public aitbcToken; + + // Structs + struct Agent { + address owner; + uint256 balance; + uint256 spendingLimit; + uint256 totalSpent; + bool isActive; + } + + struct Transaction { + uint256 txId; + address agent; + address recipient; + uint256 amount; + uint256 timestamp; + string purpose; + } + + // State Variables + uint256 public txCounter; + mapping(address => Agent) public agents; + mapping(uint256 => Transaction) public transactions; + mapping(address => uint256[]) public agentTransactions; + + // Events + event AgentRegistered(address indexed agent, address indexed owner); + event AgentDeactivated(address indexed agent); + event FundsDeposited(address indexed agent, uint256 amount); + event FundsWithdrawn(address indexed agent, uint256 amount); + event SpendingLimitUpdated(address indexed agent, uint256 newLimit); + event MicroTransactionExecuted(uint256 indexed txId, address indexed agent, address indexed recipient, uint256 amount, string purpose); + + // Modifiers + modifier onlyAgentOwner(address _agent) { + require(agents[_agent].owner == msg.sender, "Not agent owner"); + _; + } + + modifier onlyActiveAgent(address _agent) { + require(agents[_agent].isActive, "Agent is not active"); + _; + } + + constructor(address _aitbcToken) { + require(_aitbcToken != address(0), "Invalid token address"); + aitbcToken = IERC20(_aitbcToken); + } + + /** + * @dev Register a new agent wallet + * @param _agent The address of the agent + * @param _initialSpendingLimit The max the agent can spend + */ + function registerAgent(address _agent, uint256 _initialSpendingLimit) external { + require(_agent != address(0), "Invalid agent address"); + require(agents[_agent].owner == address(0), "Agent already registered"); + + agents[_agent] = Agent({ + owner: msg.sender, + balance: 0, + spendingLimit: _initialSpendingLimit, + totalSpent: 0, + isActive: true + }); + + emit AgentRegistered(_agent, msg.sender); + } + + /** + * @dev Deactivate an agent + * @param _agent The address of the agent + */ + function deactivateAgent(address _agent) external onlyAgentOwner(_agent) { + agents[_agent].isActive = false; + emit AgentDeactivated(_agent); + } + + /** + * @dev Update the spending limit for an agent + * @param _agent The address of the agent + * @param _newLimit The new spending limit + */ + function updateSpendingLimit(address _agent, uint256 _newLimit) external onlyAgentOwner(_agent) { + agents[_agent].spendingLimit = _newLimit; + emit SpendingLimitUpdated(_agent, _newLimit); + } + + /** + * @dev Deposit AITBC tokens into an agent's wallet + * @param _agent The address of the agent + * @param _amount The amount to deposit + */ + function deposit(address _agent, uint256 _amount) external onlyActiveAgent(_agent) nonReentrant { + require(_amount > 0, "Amount must be greater than 0"); + + // Transfer tokens from the caller to this contract + aitbcToken.safeTransferFrom(msg.sender, address(this), _amount); + + // Update agent balance + agents[_agent].balance += _amount; + + emit FundsDeposited(_agent, _amount); + } + + /** + * @dev Withdraw AITBC tokens from an agent's wallet back to the owner + * @param _agent The address of the agent + * @param _amount The amount to withdraw + */ + function withdraw(address _agent, uint256 _amount) external onlyAgentOwner(_agent) nonReentrant { + require(_amount > 0, "Amount must be greater than 0"); + require(agents[_agent].balance >= _amount, "Insufficient balance"); + + // Update agent balance + agents[_agent].balance -= _amount; + + // Transfer tokens back to the owner + aitbcToken.safeTransfer(msg.sender, _amount); + + emit FundsWithdrawn(_agent, _amount); + } + + /** + * @dev Execute a micro-transaction from the agent to a recipient + * @param _recipient The address of the recipient + * @param _amount The amount to send + * @param _purpose The purpose of the transaction + */ + function executeMicroTransaction( + address _recipient, + uint256 _amount, + string calldata _purpose + ) external onlyActiveAgent(msg.sender) nonReentrant returns (uint256) { + require(_recipient != address(0), "Invalid recipient"); + require(_amount > 0, "Amount must be greater than 0"); + + Agent storage agent = agents[msg.sender]; + + require(agent.balance >= _amount, "Insufficient balance"); + require(agent.totalSpent + _amount <= agent.spendingLimit, "Spending limit exceeded"); + + // Update agent balances + agent.balance -= _amount; + agent.totalSpent += _amount; + + // Create transaction record + uint256 txId = txCounter++; + transactions[txId] = Transaction({ + txId: txId, + agent: msg.sender, + recipient: _recipient, + amount: _amount, + timestamp: block.timestamp, + purpose: _purpose + }); + + agentTransactions[msg.sender].push(txId); + + // Transfer tokens to the recipient + aitbcToken.safeTransfer(_recipient, _amount); + + emit MicroTransactionExecuted(txId, msg.sender, _recipient, _amount, _purpose); + + return txId; + } + + /** + * @dev Get transaction history for an agent + * @param _agent The address of the agent + */ + function getAgentTransactions(address _agent) external view returns (uint256[] memory) { + return agentTransactions[_agent]; + } +}