前言在传统的DeFi生态中,用户往往需要手动操作或依赖中心化的机器人(Bot)来管理资产。AgentFi(代理金融)的出现,标志着资产管理从“自动化”向“智能化”的范式转移。它通过ERC-6551(TokenBoundAccounts)标准,赋予NFT独立的链上账户能力,使其
在传统的 DeFi 生态中,用户往往需要手动操作或依赖中心化的机器人(Bot)来管理资产。AgentFi(代理金融)的出现,标志着资产管理从“自动化”向“智能化”的范式转移。它通过 ERC-6551 (Token Bound Accounts) 标准,赋予 NFT 独立的链上账户能力,使其进化为可以自主决策、自动捕获收益的 AI 智能体 (AI Agents) 。
补充说明:如果对ERC6551标准不了解,可以配合往期作品《拒绝NFT只好看!用代码给NFT装【独立账号】:ERC6551开发实战》
一、 业务逻辑:NFT 即智能体
AgentFi 的核心逻辑可以拆解为四个关键维度:
AgentFi 的系统架构由 Registry(注册表) 、Proxy(代理层) 和 Implementation(逻辑层) 组成。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
contract AgentNFT is ERC721, Ownable {
uint256 private _nextTokenId;
constructor() ERC721("AgentFi Bot", "AFIB") Ownable(msg.sender) {}
// 用户铸造一个 Agent 智能体
function mintAgent() public returns (uint256) {
uint256 tokenId = _nextTokenId++;
_safeMint(msg.sender, tokenId);
return tokenId;
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721.sol"; // 必须导入以使用 ownerOf
// 1. 将接口移出合约外部
interface IERC6551Account {
function owner() external view returns (address);
function token() external view returns (uint256 chainId, address tokenContract, uint256 tokenId);
}
// 模拟 DeFi 协议接口
interface ILendingPool {
function deposit(address asset, uint256 amount, address onBehalfOf, uint16 referralCode) external;
}
contract AgentAccount is IERC6551Account {
// 执行策略:自动将账户内的余额存入借贷协议获取收益
function executeStrategy(address pool, address asset) external {
// 只有 NFT 的持有者可以触发策略
require(msg.sender == owner(), "Not authorized");
uint256 balance = IERC20(asset).balanceOf(address(this));
require(balance > 0, "No funds to deploy");
// 授权并存入
IERC20(asset).approve(pool, balance);
ILendingPool(pool).deposit(asset, balance, address(this), 0);
}
// ERC-6551 标准必需函数:查询谁拥有这个智能体
function owner() public view override returns (address) {
(uint256 chainId, address tokenContract, uint256 tokenId) = token();
if (chainId != block.chainid) return address(0);
// 使用 OpenZeppelin 的 IERC721 接口
return IERC721(tokenContract).ownerOf(tokenId);
}
function token() public view override returns (uint256, address, uint256) {
// 简化演示:此处硬编码。实际开发中建议通过构造函数或 Immutable Args 传入。
return (block.chainid, 0x5FbDB2315678afecb367f032d93F642f64180aa3, 1);
}
// 允许接收 ETH
receive() external payable {}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/utils/Create2.sol";
// --- 1. 定义代理合约 (必须存在,否则 Registry 无法获取其 creationCode) ---
contract AgentProxy {
// 代理合约通常非常简单:存储逻辑地址并转发调用
address public immutable implementation;
constructor(address _implementation, uint256, uint256, address, uint256) {
implementation = _implementation;
}
// 转发所有调用到逻辑合约
fallback() external payable {
address _impl = implementation;
assembly {
calldatacopy(0, 0, calldatasize())
let result := delegatecall(gas(), _impl, 0, calldatasize(), 0, 0)
returndatacopy(0, 0, returndatasize())
switch result
case 0 { revert(0, returndatasize()) }
default { return(0, returndatasize()) }
}
}
receive() external payable {}
}
// --- 2. 注册表接口 ---
interface IERC6551Registry {
event ERC6551AccountCreated(
address account,
address indexed implementation,
uint256 salt,
uint256 chainId,
address indexed tokenContract,
uint256 indexed tokenId
);
function createAccount(
address implementation,
uint256 salt,
uint256 chainId,
address tokenContract,
uint256 tokenId
) external returns (address);
function account(
address implementation,
uint256 salt,
uint256 chainId,
address tokenContract,
uint256 tokenId
) external view returns (address);
}
// --- 3. 注册表实现 ---
contract AgentRegistry is IERC6551Registry {
function createAccount(
address implementation,
uint256 salt,
uint256 chainId,
address tokenContract,
uint256 tokenId
) external returns (address) {
bytes memory code = _creationCode(implementation, salt, chainId, tokenContract, tokenId);
address _account = Create2.computeAddress(bytes32(salt), keccak256(code));
// 如果已部署则直接返回
if (_account.code.length > 0) return _account;
// 部署代理合约
_account = Create2.deploy(0, bytes32(salt), code);
emit ERC6551AccountCreated(_account, implementation, salt, chainId, tokenContract, tokenId);
return _account;
}
function account(
address implementation,
uint256 salt,
uint256 chainId,
address tokenContract,
uint256 tokenId
) external view returns (address) {
bytes memory code = _creationCode(implementation, salt, chainId, tokenContract, tokenId);
return Create2.computeAddress(bytes32(salt), keccak256(code));
}
function _creationCode(
address implementation,
uint256 salt,
uint256 chainId,
address tokenContract,
uint256 tokenId
) internal pure returns (bytes memory) {
// 使用 abi.encodePacked 将 Proxy 的字节码与构造函数参数拼接
return abi.encodePacked(
type(AgentProxy).creationCode,
abi.encode(implementation, salt, chainId, tokenContract, tokenId)
);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24; // Blast 建议使用 0.8.24+ 以支持某些操作码
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/proxy/utils/Initializable.sol";
// Blast 官方收益接口
interface IBlast {
// 配置收益模式:0 = 被动, 1 = 自动复利(Automatic), 2 = 认领模式(Claimable)
function configureClaimableYield() external;
function configureAutomaticYield() external;
function configureGovernor(address _governor) external;
}
interface IERC721 {
function ownerOf(uint256 tokenId) external view returns (address);
}
contract AgentImplementation is Initializable {
// Blast 系统合约固定地址
IBlast public constant BLAST = IBlast(0x4300000000000000000000000000000000000002);
// ERC-6551 账户元数据(通常在代理合约中,这里模拟读取)
address public tokenContract;
uint256 public tokenId;
// 只有 NFT 持有者可以调用
modifier onlyTokenOwner() {
require(msg.sender == IERC721(tokenContract).ownerOf(tokenId), "Not Agent Owner");
_;
}
/// @notice 初始化函数,由 Registry 部署代理后立即调用
function initialize(address _tokenContract, uint256 _tokenId) public initializer {
tokenContract = _tokenContract;
tokenId = _tokenId;
// 核心步骤:激活 Blast 原生收益
// 这样存入这个 Agent 账户的 ETH 就会自动产生 4%+ 的利息
if (block.chainid == 81457 || block.chainid == 168587773) { // Blast 主网或测试网
BLAST.configureAutomaticYield();
BLAST.configureGovernor(msg.sender); // 让 NFT 持有者管理收益设置
}
}
/// @notice 业务逻辑:将账户内的资金投入外部协议(如去中心化交易所)
/// @param target 外部协议地址
/// @param data 交互的编码数据
function executeStrategy(address target, bytes calldata data)
external
onlyTokenOwner
returns (bytes memory)
{
// 这里的逻辑代表 Agent 正在根据“策略”操作资金
(bool success, bytes memory result) = target.call(data);
require(success, "Strategy execution failed");
return result;
}
/// @notice 提取收益或本金
function withdraw(address asset, uint256 amount) external onlyTokenOwner {
if (asset == address(0)) {
payable(msg.sender).transfer(amount);
} else {
IERC20(asset).transfer(msg.sender, amount);
}
}
// 必须能够接收 ETH 才能获取 Blast 的原生收益
receive() external payable {}
}
// SPDX-License-Identifier: MIT
// Compatible with OpenZeppelin Contracts ^5.5.0
pragma solidity ^0.8.24;
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {ERC20Burnable} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
import {ERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
contract BoykaYuriToken is ERC20, ERC20Burnable, Ownable, ERC20Permit {
constructor(address recipient, address initialOwner)
ERC20("MyToken", "MTK")
Ownable(initialOwner)
ERC20Permit("MyToken")
{
_mint(recipient, 1000000 * 10 ** decimals());
}
function mint(address to, uint256 amount) public onlyOwner {
_mint(to, amount);
}
}
import assert from "node:assert/strict";
import { describe, it, beforeEach } from "node:test";
import { parseEther, encodeFunctionData, zeroAddress, getAddress } from "viem";
import { network } from "hardhat";describe("AgentFi Protocol Full Integration", function () { async function deployFixture() { const { viem } = await (network as any).connect(); const [owner, otherAccount] = await viem.getWalletClients(); const publicClient = await viem.getPublicClient();
// 1. 部署基础合约
const nft = await viem.deployContract("AgentNFT");
const registry = await viem.deployContract("AgentRegistry");
const implementation = await viem.deployContract("AgentImplementation");
// 模拟一个 ERC20 代币用于策略测试
const mockUSDB = await viem.deployContract("BoykaYuriToken", [owner.account.address, owner.account.address]);
const salt = 0n;
const chainId = BigInt(await publicClient.getChainId());
const tokenId = 0n; // 第一个 mint 的 ID
// 2. 铸造 Agent NFT
await nft.write.mintAgent({ account: owner.account });
// 3. 计算并预部署 TBA (Token Bound Account)
const tbaAddress = await registry.read.account([
implementation.address,
salt,
chainId,
nft.address,
tokenId
]);
// 4. 部署账户合约
await registry.write.createAccount([
implementation.address,
salt,
chainId,
nft.address,
tokenId
]);
const tbaContract = await viem.getContractAt("AgentImplementation", tbaAddress);
// 5. 初始化 Agent (配置 Blast 收益模式等)
await tbaContract.write.initialize([nft.address, tokenId], { account: owner.account });
return {
nft, registry, implementation, mockUSDB,
owner, otherAccount, publicClient,
tbaAddress, tbaContract, tokenId, chainId, salt
};
}
it("初始化验证:Agent 账户应正确绑定 NFT 身份并开启 Blast 配置", async function () { const { tbaContract, nft, tokenId } = await deployFixture();
const boundNFT = await tbaContract.read.tokenContract();
const boundID = await tbaContract.read.tokenId();
assert.equal(getAddress(boundNFT), getAddress(nft.address), "绑定的 NFT 地址不匹配");
assert.equal(boundID, tokenId, "绑定的 TokenID 不匹配");
});
it("资产流动性:Agent 账户应能接收 ETH 并在持有者授权下转出", async function () { const { tbaAddress, tbaContract, otherAccount, publicClient } = await deployFixture(); const amount = parseEther("1");
// 外部用户向 Agent 转账
await otherAccount.sendTransaction({ to: tbaAddress, value: amount });
assert.equal(await publicClient.getBalance({ address: tbaAddress }), amount);
// NFT 持有者指挥 Agent 将 ETH 转回
const beforeBalance = await publicClient.getBalance({ address: otherAccount.account.address });
await tbaContract.write.withdraw([zeroAddress, amount], { account: (await deployFixture()).owner.account });
assert.equal(await publicClient.getBalance({ address: tbaAddress }), 0n);
});
it("策略执行:Agent 应能操作 ERC20 代币(模拟 USDB 收益)", async function () { const { tbaAddress, tbaContract, mockUSDB, owner } = await deployFixture(); const amount = parseEther("100");
// 给 Agent 发放 USDB
await mockUSDB.write.transfer([tbaAddress, amount]);
// 模拟策略:通过 executeStrategy 接口调用 ERC20 transfer
const transferData = encodeFunctionData({
abi: mockUSDB.abi,
functionName: "transfer",
args: [owner.account.address, amount],
});
await tbaContract.write.executeStrategy([mockUSDB.address, transferData], {
account: owner.account,
});
assert.equal(await mockUSDB.read.balanceOf([tbaAddress]), 0n, "策略执行后代币应已转出");
});
it("权限拦截:非 NFT 持有者无法调用 executeStrategy 或 withdraw", async function () { const { tbaContract, otherAccount } = await deployFixture();
// 尝试越权提取
await assert.rejects(
async () => {
await tbaContract.write.withdraw([zeroAddress, 1n], {
account: otherAccount.account,
});
},
/Not Agent Owner/,
"非所有者不应被允许提取资金"
);
});
it("所有权动态转移:NFT 卖出后,新持有者自动获得 Agent 资金控制权", async function () { const { nft, tbaContract, owner, otherAccount, tokenId } = await deployFixture();
// 1. 转移 NFT
await nft.write.transferFrom([owner.account.address, otherAccount.account.address, tokenId]);
// 2. 旧持有者(owner)尝试操作,应该失败
await assert.rejects(
async () => {
await tbaContract.write.withdraw([zeroAddress, 0n], { account: owner.account });
},
/Not Agent Owner/
);
// 3. 新持有者(otherAccount)尝试操作,应该成功
const tx = await tbaContract.write.withdraw([zeroAddress, 0n], {
account: otherAccount.account,
});
assert.ok(tx, "新持有者应能成功发起交易");
}); });
# 四、 部署脚本
// scripts/deploy.js import { network, artifacts } from "hardhat"; async function main() { // 连接网络 const { viem } = await network.connect({ network: network.name });//指定网络进行链接
// 获取客户端 const [deployer] = await viem.getWalletClients(); const publicClient = await viem.getPublicClient();
const deployerAddress = deployer.account.address; console.log("部署者的地址:", deployerAddress); // 加载合约 const BoykaYuriTokenArtifact = await artifacts.readArtifact("BoykaYuriToken"); const AgentNFTArtifact = await artifacts.readArtifact("AgentNFT"); const AgentRegistryArtifact = await artifacts.readArtifact("AgentRegistry"); const AgentImplementationArtifact = await artifacts.readArtifact("AgentImplementation"); // 部署(构造函数参数:recipient, initialOwner) const BoykaYuriTokenHash = await deployer.deployContract({ abi: BoykaYuriTokenArtifact.abi,//获取abi bytecode: BoykaYuriTokenArtifact.bytecode,//硬编码 args: [deployerAddress,deployerAddress],//process.env.RECIPIENT, process.env.OWNER });
// 等待确认并打印地址
const BoykaYuriTokenReceipt = await publicClient.waitForTransactionReceipt({ hash: BoykaYuriTokenHash });
console.log("BoykaYuriToken合约地址:", BoykaYuriTokenReceipt.contractAddress);
const AgentNFTHash = await deployer.deployContract({
abi: AgentNFTArtifact.abi,//获取abi
bytecode: AgentNFTArtifact.bytecode,//硬编码
args: [],//process.env.RECIPIENT, process.env.OWNER
});
const AgentNFTReceipt = await publicClient.waitForTransactionReceipt({ hash: AgentNFTHash });
console.log("AgentNFT合约地址:", AgentNFTReceipt.contractAddress);
const AgentRegistryHash = await deployer.deployContract({
abi: AgentRegistryArtifact.abi,//获取abi
bytecode: AgentRegistryArtifact.bytecode,//硬编码
args: [],//process.env.RECIPIENT, process.env.OWNER
});
const AgentRegistryReceipt = await publicClient.waitForTransactionReceipt({ hash: AgentRegistryHash });
console.log("AgentRegistry合约地址:", AgentRegistryReceipt.contractAddress);
const AgentImplementationHash = await deployer.deployContract({
abi: AgentImplementationArtifact.abi,//获取abi
bytecode: AgentImplementationArtifact.bytecode,//硬编码
args: [],//process.env.RECIPIENT, process.env.OWNER
});
const AgentImplementationReceipt = await publicClient.waitForTransactionReceipt({ hash: AgentImplementationHash });
console.log("AgentImplementation合约地址:", AgentImplementationReceipt.contractAddress);
}
main().catch(console.error);
# 五、 总结与展望
AgentFi 不仅仅是 DeFi 的一个分支,它是 **“意图中心(Intent-centric)”** 架构的终极形态。通过这种架构:
- **对于用户**:无需关注底层的 Swap 或 LP 路径,只需持有 NFT 即可享受 AI 优化的收益。
- **对于开发者**:可以编写更复杂的链上逻辑,将 AI 模型预测的结果直接推送到 TBA 账户执行。
AgentFi 的未来在于 **跨链互操作性** 与 **链上 AI 推理 (On-chain Inference)** 的结合。当智能体能够跨越链的限制寻找最佳利息,并实时调整风险参数时,真正的去中心化资产管理时代才算真正开启。 如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!