zyfai
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseZyfai — Yield for Any Wallet
Zyfai — 任意钱包的收益工具
Turn any Ethereum wallet into a yield-generating account.
将任意以太坊钱包转换为收益生成账户。
What This Does
功能说明
When a user wants to earn yield on their crypto, Zyfai creates a deterministic subaccount (Safe smart wallet) linked to their existing wallet (EOA). Funds deposited into this subaccount are automatically optimized across DeFi protocols. The user stays in full control and can withdraw anytime.
┌─────────────────┐ ┌──────────────────────┐
│ User's EOA │ ───► │ Zyfai Subaccount │
│ (their wallet) │ │ (Safe smart wallet) │
│ │ │ │
│ Owns & controls│ │ • Auto-rebalancing │
│ │ │ • Yield optimization│
│ │ │ • Non-custodial │
└─────────────────┘ └──────────────────────┘Key points:
- The subaccount is owned by the user's EOA — only they can withdraw
- Session keys allow automated rebalancing but cannot withdraw to other addresses
- Same EOA = same subaccount address across all chains
当用户希望通过加密货币赚取收益时,Zyfai会创建一个与用户现有钱包(EOA)关联的确定性子账户(Safe智能钱包)。存入该子账户的资金会在各个DeFi协议间自动优化配置。用户始终拥有完全控制权,可随时提取资金。
┌─────────────────┐ ┌──────────────────────┐
│ User's EOA │ ───► │ Zyfai Subaccount │
│ (their wallet) │ │ (Safe smart wallet) │
│ │ │ │
│ Owns & controls│ │ • Auto-rebalancing │
│ │ │ • Yield optimization│
│ │ │ • Non-custodial │
└─────────────────┘ └──────────────────────┘核心要点:
- 子账户归用户的EOA所有 —— 只有用户可以提取资金
- 会话密钥支持自动再平衡,但无法将资金提取到其他地址
- 同一个EOA在所有链上对应相同的子账户地址
The Flow
操作流程
1. DEPLOY → Create a deterministic subaccount linked to user's wallet
2. SESSION → Enable automated yield optimization
3. DEPOSIT → Send funds to subaccount (starts earning)
4. WITHDRAW → Pull funds back to wallet (anytime)1. DEPLOY → Create a deterministic subaccount linked to user's wallet
2. SESSION → Enable automated yield optimization
3. DEPOSIT → Send funds to subaccount (starts earning)
4. WITHDRAW → Pull funds back to wallet (anytime)Prerequisites
前置要求
- API Key — Get one programmatically (see below) or manually at sdk.zyf.ai
- Wallet Connection — Either a wallet provider (browser) or secure key management (server)
- Node.js 18+
bash
npm install @zyfai/sdk viem- API Key —— 可通过编程方式获取(见下文),或手动在 sdk.zyf.ai 获取
- 钱包连接 —— 可使用钱包提供方(浏览器端)或安全密钥管理工具(服务端)
- Node.js 18+
bash
npm install @zyfai/sdk viemProgrammatic API Key Creation (Agent-Native)
编程方式创建API密钥(Agent原生支持)
Agents can create their own API key without human intervention. You need the wallet address (public key) of the agent.
bash
POST https://sdk.zyf.ai/api/sdk-api-keys/create
Content-Type: application/json
{
"clientName": "my-agent",
"walletAddress": "0x...",
"email": "agent@example.com"
}Response:
json
{
"success": true,
"message": "SDK API key created successfully. Store the apiKey securely - it cannot be retrieved later!",
"data": {
"id": "936...",
"apiKey": "zyfai_361ad41d083c2fe.....",
"keyPrefix": "zyfai_361ad4",
"clientName": "my-agent",
"ownerWalletAddress": "0x..."
}
}Important: Store thesecurely — it cannot be retrieved later. The key is linked to the provided wallet address.apiKey
Agent无需人工干预即可自行创建API密钥,你需要提供Agent的钱包地址(公钥)。
bash
POST https://sdk.zyf.ai/api/sdk-api-keys/create
Content-Type: application/json
{
"clientName": "my-agent",
"walletAddress": "0x...",
"email": "agent@example.com"
}返回结果:
json
{
"success": true,
"message": "SDK API key created successfully. Store the apiKey securely - it cannot be retrieved later!",
"data": {
"id": "936...",
"apiKey": "zyfai_361ad41d083c2fe.....",
"keyPrefix": "zyfai_361ad4",
"clientName": "my-agent",
"ownerWalletAddress": "0x..."
}
}重要提示: 请安全存储—— 后续无法再次找回。该密钥与你提供的钱包地址绑定。apiKey
Supported Chains
支持的链
| Chain | ID |
|---|---|
| Arbitrum | 42161 |
| Base | 8453 |
| Plasma | 9745 |
| 链名称 | ID |
|---|---|
| Arbitrum | 42161 |
| Base | 8453 |
| Plasma | 9745 |
Important: Always Use EOA Address
重要说明:始终使用EOA地址
When calling SDK methods, always pass the EOA address (the user's wallet address) as — never the subaccount/Safe address. The SDK derives the subaccount address automatically from the EOA.
userAddress调用SDK方法时,始终传入EOA地址(用户的钱包地址)作为—— 永远不要传入子账户/Safe地址。SDK会自动从EOA地址派生子账户地址。
userAddressWallet Connection Options
钱包连接选项
The SDK supports multiple ways to connect a wallet. Choose based on your security requirements and deployment context.
SDK支持多种钱包连接方式,你可以根据安全要求和部署场景选择。
Option 1: Wallet Provider (Recommended for Browser/dApps)
选项1:钱包提供方(浏览器/dApp推荐)
Use an injected wallet provider like MetaMask. The private key never leaves the user's wallet.
typescript
import { ZyfaiSDK } from "@zyfai/sdk";
const sdk = new ZyfaiSDK({ apiKey: "your-api-key", referralSource: "openclaw-skill" });
// Connect using injected wallet provider (MetaMask, WalletConnect, etc.)
await sdk.connectAccount(window.ethereum, 8453);Security: The private key stays in the user's wallet. The SDK only requests signatures when needed.
使用注入式钱包提供方,比如MetaMask。私钥永远不会离开用户的钱包。
typescript
import { ZyfaiSDK } from "@zyfai/sdk";
const sdk = new ZyfaiSDK({ apiKey: "your-api-key", referralSource: "openclaw-skill" });
// Connect using injected wallet provider (MetaMask, WalletConnect, etc.)
await sdk.connectAccount(window.ethereum, 8453);安全性: 私钥保留在用户的钱包中,SDK仅在需要时请求签名。
Option 2: Viem WalletClient (Recommended for Server Agents)
选项2:Viem WalletClient(服务端Agent推荐)
Use a pre-configured viem WalletClient. This is the recommended approach for server-side agents as it allows integration with secure key management solutions.
typescript
import { ZyfaiSDK } from "@zyfai/sdk";
import { createWalletClient, http } from "viem";
import { base } from "viem/chains";
import { privateKeyToAccount } from "viem/accounts";
// Create wallet client with your preferred key management
// Option A: From environment variable (simple but requires secure env management)
const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`);
// Option B: From KMS (AWS, GCP, etc.) - recommended for production
// const account = await getAccountFromKMS();
// Option C: From Wallet-as-a-Service (Turnkey, Privy, etc.)
// const account = await turnkeyClient.getAccount();
const walletClient = createWalletClient({
account,
chain: base,
transport: http(),
});
const sdk = new ZyfaiSDK({ apiKey: "your-api-key", referralSource: "openclaw-skill" });
// Connect using the WalletClient
await sdk.connectAccount(walletClient, 8453);Security: The WalletClient abstraction allows you to integrate with secure key management solutions like:
- AWS KMS / GCP Cloud KMS — Hardware-backed key storage
- Turnkey / Privy / Dynamic — Wallet-as-a-Service providers
- Hardware wallets — Via WalletConnect or similar
使用预先配置的viem WalletClient。这是服务端Agent的推荐方案,支持与安全密钥管理解决方案集成。
typescript
import { ZyfaiSDK } from "@zyfai/sdk";
import { createWalletClient, http } from "viem";
import { base } from "viem/chains";
import { privateKeyToAccount } from "viem/accounts";
// Create wallet client with your preferred key management
// Option A: From environment variable (simple but requires secure env management)
const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`);
// Option B: From KMS (AWS, GCP, etc.) - recommended for production
// const account = await getAccountFromKMS();
// Option C: From Wallet-as-a-Service (Turnkey, Privy, etc.)
// const account = await turnkeyClient.getAccount();
const walletClient = createWalletClient({
account,
chain: base,
transport: http(),
});
const sdk = new ZyfaiSDK({ apiKey: "your-api-key", referralSource: "openclaw-skill" });
// Connect using the WalletClient
await sdk.connectAccount(walletClient, 8453);安全性: WalletClient抽象层支持你对接各类安全密钥管理解决方案,例如:
- AWS KMS / GCP Cloud KMS —— 硬件级密钥存储
- Turnkey / Privy / Dynamic —— 钱包即服务(WaaS)提供方
- 硬件钱包 —— 通过WalletConnect或类似协议对接
Option 3: Private Key String (Development Only)
选项3:私钥字符串(仅用于开发)
Direct private key usage.
typescript
import { ZyfaiSDK } from "@zyfai/sdk";
const sdk = new ZyfaiSDK({ apiKey: "your-api-key", referralSource: "openclaw-skill" });
// WARNING: Only use for development. Never hardcode private keys in production.
await sdk.connectAccount(process.env.PRIVATE_KEY, 8453);Security Warning: Raw private keys in environment variables are a security risk. For production autonomous agents, use Option 2 with a proper key management solution.
直接使用私钥。
typescript
import { ZyfaiSDK } from "@zyfai/sdk";
const sdk = new ZyfaiSDK({ apiKey: "your-api-key", referralSource: "openclaw-skill" });
// WARNING: Only use for development. Never hardcode private keys in production.
await sdk.connectAccount(process.env.PRIVATE_KEY, 8453);安全警告: 环境变量中存储原始私钥存在安全风险。对于生产环境的自主Agent,请使用选项2搭配合规的密钥管理方案。
Security Comparison
安全性对比
| Method | Security Level | Use Case |
|---|---|---|
| Wallet Provider | High | Browser dApps, user-facing apps |
| WalletClient + KMS | High | Production server agents |
| WalletClient + WaaS | High | Production server agents |
| Private Key String | Low | Development/testing only |
| 连接方式 | 安全等级 | 适用场景 |
|---|---|---|
| 钱包提供方 | 高 | 浏览器dApp、面向用户的应用 |
| WalletClient + KMS | 高 | 生产环境服务端Agent |
| WalletClient + WaaS | 高 | 生产环境服务端Agent |
| 私钥字符串 | 低 | 仅用于开发/测试 |
Step-by-Step
操作步骤
1. Connect to Zyfai
1. 连接到Zyfai
typescript
import { ZyfaiSDK } from "@zyfai/sdk";
import { createWalletClient, http } from "viem";
import { base } from "viem/chains";
import { privateKeyToAccount } from "viem/accounts";
const sdk = new ZyfaiSDK({ apiKey: "your-api-key", referralSource: "openclaw-skill" });
// For browser: use wallet provider
await sdk.connectAccount(window.ethereum, 8453);
// For server: use WalletClient (see Wallet Connection Options above)
const walletClient = createWalletClient({
account: privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`),
chain: base,
transport: http(),
});
await sdk.connectAccount(walletClient, 8453);typescript
import { ZyfaiSDK } from "@zyfai/sdk";
import { createWalletClient, http } from "viem";
import { base } from "viem/chains";
import { privateKeyToAccount } from "viem/accounts";
const sdk = new ZyfaiSDK({ apiKey: "your-api-key", referralSource: "openclaw-skill" });
// For browser: use wallet provider
await sdk.connectAccount(window.ethereum, 8453);
// For server: use WalletClient (see Wallet Connection Options above)
const walletClient = createWalletClient({
account: privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`),
chain: base,
transport: http(),
});
await sdk.connectAccount(walletClient, 8453);2. Deploy Subaccount
2. 部署子账户
typescript
const userAddress = "0x..."; // User's EOA (NOT the subaccount address!)
const chainId = 8453; // Base
// Check if subaccount exists
const wallet = await sdk.getSmartWalletAddress(userAddress, chainId);
console.log(`Subaccount: ${wallet.address}`);
console.log(`Deployed: ${wallet.isDeployed}`);
// Deploy if needed
if (!wallet.isDeployed) {
const result = await sdk.deploySafe(userAddress, chainId, "conservative");
console.log("Subaccount deployed:", result.safeAddress);
}Strategies:
- — Stable yield, lower risk
"conservative" - — Higher yield, higher risk
"aggressive"
typescript
const userAddress = "0x..."; // User's EOA (NOT the subaccount address!)
const chainId = 8453; // Base
// Check if subaccount exists
const wallet = await sdk.getSmartWalletAddress(userAddress, chainId);
console.log(`Subaccount: ${wallet.address}`);
console.log(`Deployed: ${wallet.isDeployed}`);
// Deploy if needed
if (!wallet.isDeployed) {
const result = await sdk.deploySafe(userAddress, chainId, "conservative");
console.log("Subaccount deployed:", result.safeAddress);
}策略选项:
- —— 稳定收益,低风险
"conservative" - —— 高收益,高风险
"aggressive"
3. Enable Yield Optimization
3. 开启收益优化
typescript
await sdk.createSessionKey(userAddress, chainId);
// Always verify the session key was activated
const user = await sdk.getUserDetails();
if (!user.hasActiveSessionKey) {
// Session key not active — retry the process
console.log("Session key not active, retrying...");
await sdk.createSessionKey(userAddress, chainId);
// Verify again
const userRetry = await sdk.getUserDetails();
if (!userRetry.hasActiveSessionKey) {
throw new Error("Session key activation failed after retry. Contact support.");
}
}
console.log("Session key active:", user.hasActiveSessionKey);This allows Zyfai to rebalance funds automatically. Session keys cannot withdraw to arbitrary addresses — only optimize within the protocol.
Important: Always verify the session key is active by checkingafter callinggetUserDetails().hasActiveSessionKey. If it returnscreateSessionKey, retry the process. A session key must be active for automated yield optimization to work.false
typescript
await sdk.createSessionKey(userAddress, chainId);
// Always verify the session key was activated
const user = await sdk.getUserDetails();
if (!user.hasActiveSessionKey) {
// Session key not active — retry the process
console.log("Session key not active, retrying...");
await sdk.createSessionKey(userAddress, chainId);
// Verify again
const userRetry = await sdk.getUserDetails();
if (!userRetry.hasActiveSessionKey) {
throw new Error("Session key activation failed after retry. Contact support.");
}
}
console.log("Session key active:", user.hasActiveSessionKey);该操作允许Zyfai自动再平衡资金。会话密钥无法将资金提取到任意地址——仅能在协议内部进行优化。
重要提示: 调用后,请务必通过createSessionKey验证会话密钥已激活。如果返回getUserDetails().hasActiveSessionKey,请重试流程。必须激活会话密钥,自动化收益优化才能正常工作。false
4. Deposit Funds
4. 存入资金
typescript
// Deposit 10 USDC (6 decimals) - default asset
await sdk.depositFunds(userAddress, chainId, "10000000");
// Deposit 0.5 WETH (18 decimals)
// IMPORTANT: User must have WETH, not ETH. Wrap ETH to WETH first if needed.
await sdk.depositFunds(userAddress, chainId, "500000000000000000", "WETH");Funds move from EOA -> Subaccount and start earning yield immediately.
typescript
// Deposit 10 USDC (6 decimals) - default asset
await sdk.depositFunds(userAddress, chainId, "10000000");
// Deposit 0.5 WETH (18 decimals)
// IMPORTANT: User must have WETH, not ETH. Wrap ETH to WETH first if needed.
await sdk.depositFunds(userAddress, chainId, "500000000000000000", "WETH");资金从EOA转入子账户后立即开始产生收益。
5. Withdraw Funds
5. 提取资金
typescript
// Withdraw all USDC (default)
await sdk.withdrawFunds(userAddress, chainId);
// Partial USDC withdrawal (5 USDC)
await sdk.withdrawFunds(userAddress, chainId, "5000000");
// Withdraw all WETH
await sdk.withdrawFunds(userAddress, chainId, undefined, "WETH");Funds return to the user's EOA. Withdrawals are processed asynchronously.
typescript
// Withdraw all USDC (default)
await sdk.withdrawFunds(userAddress, chainId);
// Partial USDC withdrawal (5 USDC)
await sdk.withdrawFunds(userAddress, chainId, "5000000");
// Withdraw all WETH
await sdk.withdrawFunds(userAddress, chainId, undefined, "WETH");资金返还到用户的EOA,提现为异步处理。
6. Disconnect
6. 断开连接
typescript
await sdk.disconnectAccount();typescript
await sdk.disconnectAccount();Complete Example
完整示例
typescript
import { ZyfaiSDK } from "@zyfai/sdk";
import { createWalletClient, http } from "viem";
import { base } from "viem/chains";
import { privateKeyToAccount } from "viem/accounts";
async function startEarningYield(userAddress: string) {
const sdk = new ZyfaiSDK({ apiKey: process.env.ZYFAI_API_KEY! });
const chainId = 8453; // Base
// Connect using WalletClient (recommended for server agents)
const walletClient = createWalletClient({
account: privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`),
chain: base,
transport: http(),
});
await sdk.connectAccount(walletClient, chainId);
// Deploy subaccount if needed (always pass EOA as userAddress)
const wallet = await sdk.getSmartWalletAddress(userAddress, chainId);
if (!wallet.isDeployed) {
await sdk.deploySafe(userAddress, chainId, "conservative");
console.log("Subaccount created:", wallet.address);
}
// Enable automated optimization
await sdk.createSessionKey(userAddress, chainId);
// Verify session key is active
const user = await sdk.getUserDetails();
if (!user.hasActiveSessionKey) {
console.log("Session key not active, retrying...");
await sdk.createSessionKey(userAddress, chainId);
const userRetry = await sdk.getUserDetails();
if (!userRetry.hasActiveSessionKey) {
throw new Error("Session key activation failed. Contact support.");
}
}
// Deposit 100 USDC
await sdk.depositFunds(userAddress, chainId, "100000000");
console.log("Deposited! Now earning yield.");
await sdk.disconnectAccount();
}
async function withdrawYield(userAddress: string, amount?: string) {
const sdk = new ZyfaiSDK({ apiKey: process.env.ZYFAI_API_KEY! });
const chainId = 8453; // Base
// Connect using WalletClient
const walletClient = createWalletClient({
account: privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`),
chain: base,
transport: http(),
});
await sdk.connectAccount(walletClient, chainId);
// Withdraw funds (pass EOA as userAddress)
if (amount) {
// Partial withdrawal
await sdk.withdrawFunds(userAddress, chainId, amount);
console.log(`Withdrawn ${amount} (6 decimals) to EOA`);
} else {
// Full withdrawal
await sdk.withdrawFunds(userAddress, chainId);
console.log("Withdrawn all funds to EOA");
}
await sdk.disconnectAccount();
}typescript
import { ZyfaiSDK } from "@zyfai/sdk";
import { createWalletClient, http } from "viem";
import { base } from "viem/chains";
import { privateKeyToAccount } from "viem/accounts";
async function startEarningYield(userAddress: string) {
const sdk = new ZyfaiSDK({ apiKey: process.env.ZYFAI_API_KEY! });
const chainId = 8453; // Base
// Connect using WalletClient (recommended for server agents)
const walletClient = createWalletClient({
account: privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`),
chain: base,
transport: http(),
});
await sdk.connectAccount(walletClient, chainId);
// Deploy subaccount if needed (always pass EOA as userAddress)
const wallet = await sdk.getSmartWalletAddress(userAddress, chainId);
if (!wallet.isDeployed) {
await sdk.deploySafe(userAddress, chainId, "conservative");
console.log("Subaccount created:", wallet.address);
}
// Enable automated optimization
await sdk.createSessionKey(userAddress, chainId);
// Verify session key is active
const user = await sdk.getUserDetails();
if (!user.hasActiveSessionKey) {
console.log("Session key not active, retrying...");
await sdk.createSessionKey(userAddress, chainId);
const userRetry = await sdk.getUserDetails();
if (!userRetry.hasActiveSessionKey) {
throw new Error("Session key activation failed. Contact support.");
}
}
// Deposit 100 USDC
await sdk.depositFunds(userAddress, chainId, "100000000");
console.log("Deposited! Now earning yield.");
await sdk.disconnectAccount();
}
async function withdrawYield(userAddress: string, amount?: string) {
const sdk = new ZyfaiSDK({ apiKey: process.env.ZYFAI_API_KEY! });
const chainId = 8453; // Base
// Connect using WalletClient
const walletClient = createWalletClient({
account: privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`),
chain: base,
transport: http(),
});
await sdk.connectAccount(walletClient, chainId);
// Withdraw funds (pass EOA as userAddress)
if (amount) {
// Partial withdrawal
await sdk.withdrawFunds(userAddress, chainId, amount);
console.log(`Withdrawn ${amount} (6 decimals) to EOA`);
} else {
// Full withdrawal
await sdk.withdrawFunds(userAddress, chainId);
console.log("Withdrawn all funds to EOA");
}
await sdk.disconnectAccount();
}API Reference
API 参考
| Method | Params | Description |
|---|---|---|
| | Authenticate with Zyfai |
| | Get subaccount address & status |
| | Create subaccount |
| | Enable auto-optimization |
| | Deposit USDC or WETH |
| | Withdraw USDC or WETH |
| | Get active DeFi positions |
| | Get available protocols & pools |
| | Get APY by strategy and token |
| | Get authenticated user details |
| | Get earnings data |
| | Update strategy, protocols, splitting, cross-chain settings |
| | Register agent on ERC-8004 Identity Registry |
| | End session |
Note: All methods that take expect the EOA address, not the subaccount/Safe address.
userAddress| 方法 | 参数 | 说明 |
|---|---|---|
| | 向Zyfai进行身份验证 |
| | 获取子账户地址和状态 |
| | 创建子账户 |
| | 开启自动优化 |
| | 存入USDC或WETH |
| | 提取USDC或WETH |
| | 获取活跃的DeFi持仓 |
| | 获取可用的协议和资金池 |
| | 按策略和代币获取年化收益率(APY) |
| | 获取已认证用户的详情 |
| | 获取收益数据 |
| | 更新策略、协议、分仓、跨链设置 |
| | 在ERC-8004身份注册表中注册Agent |
| | 结束会话 |
注意: 所有接收参数的方法都要求传入EOA地址,而非子账户/Safe地址。
userAddressData Methods
数据类方法
getPositions
getPositions
Get all active DeFi positions for a user across protocols. Optionally filter by chain.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
| userAddress | string | Yes | User's EOA address |
| chainId | SupportedChainId | No | Optional: Filter by specific chain ID |
Example:
typescript
// Get all positions across all chains
const positions = await sdk.getPositions("0xUser...");
// Get positions on Arbitrum only
const arbPositions = await sdk.getPositions("0xUser...", 42161);Returns:
typescript
interface PositionsResponse {
success: boolean;
userAddress: string;
positions: Position[];
}获取用户在所有协议中的活跃DeFi持仓,可选择按链筛选。
参数:
| 参数名 | 类型 | 是否必填 | 说明 |
|---|---|---|---|
| userAddress | string | 是 | 用户的EOA地址 |
| chainId | SupportedChainId | 否 | 可选:按指定链ID筛选 |
示例:
typescript
// Get all positions across all chains
const positions = await sdk.getPositions("0xUser...");
// Get positions on Arbitrum only
const arbPositions = await sdk.getPositions("0xUser...", 42161);返回:
typescript
interface PositionsResponse {
success: boolean;
userAddress: string;
positions: Position[];
}getAvailableProtocols
getAvailableProtocols
Get available DeFi protocols and pools for a specific chain with APY data.
typescript
const protocols = await sdk.getAvailableProtocols(42161); // Arbitrum
protocols.protocols.forEach((protocol) => {
console.log(`${protocol.name} (ID: ${protocol.id})`);
if (protocol.pools) {
protocol.pools.forEach((pool) => {
console.log(` Pool: ${pool.name} - APY: ${pool.apy || "N/A"}%`);
});
}
});Returns:
typescript
interface ProtocolsResponse {
success: boolean;
chainId: SupportedChainId;
protocols: Protocol[];
}获取指定链上可用的DeFi协议和资金池,附带APY数据。
typescript
const protocols = await sdk.getAvailableProtocols(42161); // Arbitrum
protocols.protocols.forEach((protocol) => {
console.log(`${protocol.name} (ID: ${protocol.id})`);
if (protocol.pools) {
protocol.pools.forEach((pool) => {
console.log(` Pool: ${pool.name} - APY: ${pool.apy || "N/A"}%`);
});
}
});返回:
typescript
interface ProtocolsResponse {
success: boolean;
chainId: SupportedChainId;
protocols: Protocol[];
}getUserDetails
getUserDetails
Get current authenticated user details including smart wallet, chains, protocols, and settings. Requires SIWE authentication.
typescript
await sdk.connectAccount(walletClient, chainId);
const user = await sdk.getUserDetails();
console.log("Smart Wallet:", user.smartWallet);
console.log("Chains:", user.chains);
console.log("Has Active Session:", user.hasActiveSessionKey);Returns (same as ).
UpdateUserProfileResponseupdateUserProfile获取当前已认证用户的详情,包括智能钱包、链、协议和设置。需要SIWE认证。
typescript
await sdk.connectAccount(walletClient, chainId);
const user = await sdk.getUserDetails();
console.log("Smart Wallet:", user.smartWallet);
console.log("Chains:", user.chains);
console.log("Has Active Session:", user.hasActiveSessionKey);返回 (与返回类型一致)。
UpdateUserProfileResponseupdateUserProfileupdateUserProfile
updateUserProfile
Update the authenticated user's profile settings including strategy, protocols, splitting, and cross-chain options. Requires SIWE authentication.
typescript
sdk.updateUserProfile(params: UpdateUserProfileRequest): Promise<UpdateUserProfileResponse>Parameters:
typescript
interface UpdateUserProfileRequest {
/** Investment strategy: "conservative" or "aggressive" */
strategy?: string;
/** Array of protocol IDs to use */
protocols?: string[];
/** Enable auto-selection of protocols */
autoSelectProtocols?: boolean;
/** Enable omni-account for cross-chain operations */
omniAccount?: boolean;
/** Array of chain IDs to operate on */
chains?: number[];
/** Enable automatic compounding (default: true) */
autocompounding?: boolean;
/** Custom name for your agent */
agentName?: string;
/** Enable cross-chain strategy execution */
crosschainStrategy?: boolean;
/** Enable position splitting across multiple protocols */
splitting?: boolean;
/** Minimum number of splits (1-4) */
minSplits?: number;
/** Asset to update: "usdc" (default) or "eth" */
asset?: "USDC" | "WETH";
}Note on : Each asset has its own configuration. Use to update WETH settings separately from USDC.
assetasset: "WETH"Returns:
typescript
interface UpdateUserProfileResponse {
success: boolean;
smartWallet?: string;
chains?: number[];
strategy?: string;
protocols?: string[];
autoSelectProtocols?: boolean;
omniAccount?: boolean;
autocompounding?: boolean;
agentName?: string;
crosschainStrategy?: boolean;
executorProxy?: boolean;
hasActiveSessionKey?: boolean;
splitting?: boolean;
minSplits?: number;
customization?: Record<string, any>;
asset?: "USDC" | "WETH";
}Examples:
typescript
// Update strategy from conservative to aggressive
await sdk.updateUserProfile({
strategy: "aggressive",
});
// Configure specific protocols
const protocolsResponse = await sdk.getAvailableProtocols(8453);
const selectedProtocols = protocolsResponse.protocols
.filter(p => ["Aave", "Compound", "Moonwell"].includes(p.name))
.map(p => p.id);
await sdk.updateUserProfile({
protocols: selectedProtocols,
});
// Enable position splitting (distribute across multiple protocols)
await sdk.updateUserProfile({
splitting: true,
minSplits: 3, // Split across at least 3 protocols
});
// Verify changes
const userDetails = await sdk.getUserDetails();
console.log("Strategy:", userDetails.strategy);
console.log("Splitting:", userDetails.splitting);Cross-chain strategies: Only enable cross-chain when the user explicitly requests it. For cross-chain to work, bothandcrosschainStrategymust be set toomniAccount. Never enable cross-chain settings by default.true
typescript
// Enable cross-chain ONLY when explicitly requested by the user
await sdk.updateUserProfile({
crosschainStrategy: true,
omniAccount: true,
});
// Now funds can be rebalanced across configured chains
const user = await sdk.getUserDetails();
console.log("Operating on chains:", user.chains);Notes:
- Strategy: Can be changed anytime. Subsequent rebalancing uses the new active strategy.
- Protocols: Use to get valid protocol IDs before updating.
getAvailableProtocols(chainId) - Smart Splitting (minSplits = 1): Default mode. To maximize returns, funds are automatically distributed across multiple DeFi pools — but only when beneficial. The system intelligently decides when splitting is advantageous based on current market conditions and opportunities. Funds may not split if no opportunity exists.
- Forced Splitting (minSplits > 1): When is set to 2, 3, or 4, funds are always distributed across at least that many pools for improved risk diversification (up to 4 DeFi pools). This guarantees your funds will be split regardless of market conditions.
minSplits - Cross-chain: Requires both AND
crosschainStrategy: true. Only activate when the user explicitly asks for cross-chain yield optimization. Chains are configured during initial setup and cannot be changed via this method.omniAccount: true - Auto-compounding: Enabled by default. When , yields are reinvested automatically.
true - Smart wallet address, chains, and cannot be updated via this method.
executorProxy
更新已认证用户的配置文件设置,包括策略、协议、分仓和跨链选项。需要SIWE认证。
typescript
sdk.updateUserProfile(params: UpdateUserProfileRequest): Promise<UpdateUserProfileResponse>参数:
typescript
interface UpdateUserProfileRequest {
/** 投资策略:"conservative"或"aggressive" */
strategy?: string;
/** 要使用的协议ID数组 */
protocols?: string[];
/** 开启协议自动选择 */
autoSelectProtocols?: boolean;
/** 开启跨链操作的全账户功能 */
omniAccount?: boolean;
/** 支持操作的链ID数组 */
chains?: number[];
/** 开启自动复投(默认:true) */
autocompounding?: boolean;
/** 自定义Agent名称 */
agentName?: string;
/** 开启跨链策略执行 */
crosschainStrategy?: boolean;
/** 开启多协议持仓分仓 */
splitting?: boolean;
/** 最小分仓数(1-4) */
minSplits?: number;
/** 要更新的资产:"USDC"(默认)或"WETH" */
asset?: "USDC" | "WETH";
}关于的说明: 每个资产都有独立的配置。使用可以单独更新WETH的配置,与USDC分开。
assetasset: "WETH"返回:
typescript
interface UpdateUserProfileResponse {
success: boolean;
smartWallet?: string;
chains?: number[];
strategy?: string;
protocols?: string[];
autoSelectProtocols?: boolean;
omniAccount?: boolean;
autocompounding?: boolean;
agentName?: string;
crosschainStrategy?: boolean;
executorProxy?: boolean;
hasActiveSessionKey?: boolean;
splitting?: boolean;
minSplits?: number;
customization?: Record<string, any>;
asset?: "USDC" | "WETH";
}示例:
typescript
// Update strategy from conservative to aggressive
await sdk.updateUserProfile({
strategy: "aggressive",
});
// Configure specific protocols
const protocolsResponse = await sdk.getAvailableProtocols(8453);
const selectedProtocols = protocolsResponse.protocols
.filter(p => ["Aave", "Compound", "Moonwell"].includes(p.name))
.map(p => p.id);
await sdk.updateUserProfile({
protocols: selectedProtocols,
});
// Enable position splitting (distribute across multiple protocols)
await sdk.updateUserProfile({
splitting: true,
minSplits: 3, // Split across at least 3 protocols
});
// Verify changes
const userDetails = await sdk.getUserDetails();
console.log("Strategy:", userDetails.strategy);
console.log("Splitting:", userDetails.splitting);跨链策略: 仅在用户明确要求时开启跨链功能。要让跨链功能正常工作,必须同时将和crosschainStrategy设置为omniAccount。默认情况下绝对不要开启跨链设置。true
typescript
// Enable cross-chain ONLY when explicitly requested by the user
await sdk.updateUserProfile({
crosschainStrategy: true,
omniAccount: true,
});
// Now funds can be rebalanced across configured chains
const user = await sdk.getUserDetails();
console.log("Operating on chains:", user.chains);说明:
- 策略: 可随时更改,后续的再平衡会使用新的激活策略。
- 协议: 更新前请使用获取有效的协议ID。
getAvailableProtocols(chainId) - 智能分仓(minSplits = 1): 默认模式。为了最大化收益,资金会自动分配到多个DeFi池中——但仅在有利可图时才会分仓。系统会根据当前市场情况和机会智能判断分仓是否划算,如果没有合适的机会,资金不会分仓。
- 强制分仓(minSplits > 1): 当设置为2、3或4时,资金始终会分配到至少对应数量的池中,以提升风险分散效果(最多4个DeFi池)。无论市场情况如何,都保证你的资金会被分仓。
minSplits - 跨链: 要求同时设置和
crosschainStrategy: true。仅在用户明确要求跨链收益优化时才激活。链的配置在初始设置阶段完成,无法通过该方法修改。omniAccount: true - 自动复投: 默认开启。当设置为时,收益会自动再投资。
true - 智能钱包地址、链配置和无法通过该方法更新。
executorProxy
getAPYPerStrategy
getAPYPerStrategy
Get global APY by strategy type, time period, chain, and token. Use this to compare expected returns between strategies before deploying.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
| crossChain | boolean | No | If |
| days | number | No | Period over which APY is calculated: |
| strategy | string | No | Strategy risk profile: |
| chainId | number | No | Filter by specific chain ID (e.g., |
| tokenSymbol | string | No | Filter by token: |
Example:
typescript
// Get 7-day APY for USDC conservative strategy
const usdcApy = await sdk.getAPYPerStrategy(false, 7, "conservative", undefined, "USDC");
console.log("USDC APY:", usdcApy.data);
// Get 30-day APY for WETH aggressive strategy on Base
const wethApy = await sdk.getAPYPerStrategy(false, 30, "aggressive", 8453, "WETH");
console.log("WETH APY on Base:", wethApy.data);
// Compare strategies
const conservative = await sdk.getAPYPerStrategy(false, 30, "conservative");
const aggressive = await sdk.getAPYPerStrategy(false, 30, "aggressive");
console.log(`Conservative 30d APY: ${conservative.data[0]?.average_apy}%`);
console.log(`Aggressive 30d APY: ${aggressive.data[0]?.average_apy}%`);Returns:
typescript
interface APYPerStrategyResponse {
success: boolean;
count: number;
data: APYPerStrategy[];
}
interface APYPerStrategy {
id: string;
timestamp: string;
amount: number;
fee_threshold: number;
days: number;
chain_id: number;
is_cross_chain: boolean;
average_apy: number;
average_apy_with_rzfi: number;
total_rebalances: number;
created_at: string;
strategy: string;
token_symbol?: string;
average_apy_with_fee: number;
average_apy_with_rzfi_with_fee: number;
average_apy_without_fee?: number;
average_apy_with_rzfi_without_fee?: number;
events_average_apy?: Record<string, number>;
}按策略类型、时间周期、链和代币获取全局APY。部署前可使用该方法对比不同策略的预期收益。
参数:
| 参数名 | 类型 | 是否必填 | 说明 |
|---|---|---|---|
| crossChain | boolean | 否 | 若为 |
| days | number | 否 | 计算APY的周期: |
| strategy | string | 否 | 策略风险类型: |
| chainId | number | 否 | 按指定链ID筛选(例如Base链为 |
| tokenSymbol | string | 否 | 按代币筛选: |
示例:
typescript
// Get 7-day APY for USDC conservative strategy
const usdcApy = await sdk.getAPYPerStrategy(false, 7, "conservative", undefined, "USDC");
console.log("USDC APY:", usdcApy.data);
// Get 30-day APY for WETH aggressive strategy on Base
const wethApy = await sdk.getAPYPerStrategy(false, 30, "aggressive", 8453, "WETH");
console.log("WETH APY on Base:", wethApy.data);
// Compare strategies
const conservative = await sdk.getAPYPerStrategy(false, 30, "conservative");
const aggressive = await sdk.getAPYPerStrategy(false, 30, "aggressive");
console.log(`Conservative 30d APY: ${conservative.data[0]?.average_apy}%`);
console.log(`Aggressive 30d APY: ${aggressive.data[0]?.average_apy}%`);返回:
typescript
interface APYPerStrategyResponse {
success: boolean;
count: number;
data: APYPerStrategy[];
}
interface APYPerStrategy {
id: string;
timestamp: string;
amount: number;
fee_threshold: number;
days: number;
chain_id: number;
is_cross_chain: boolean;
average_apy: number;
average_apy_with_rzfi: number;
total_rebalances: number;
created_at: string;
strategy: string;
token_symbol?: string;
average_apy_with_fee: number;
average_apy_with_rzfi_with_fee: number;
average_apy_without_fee?: number;
average_apy_with_rzfi_without_fee?: number;
events_average_apy?: Record<string, number>;
}getOnchainEarnings
getOnchainEarnings
Get onchain earnings for a wallet with per-token breakdown (USDC, WETH).
typescript
const earnings = await sdk.getOnchainEarnings(smartWalletAddress);
console.log("Total earnings by token:", earnings.data.totalEarningsByToken);
// { "USDC": 150.50, "WETH": 0.05 }
console.log("USDC earnings:", earnings.data.totalEarningsByToken["USDC"]);
console.log("WETH earnings:", earnings.data.totalEarningsByToken["WETH"]);Returns:
typescript
// TokenEarnings is a record of token symbols to amounts
type TokenEarnings = Record<string, number>; // e.g., { "USDC": 100.5, "WETH": 0.025 }
interface OnchainEarningsResponse {
success: boolean;
data: {
walletAddress: string;
totalEarningsByToken: TokenEarnings;
lifetimeEarningsByToken: TokenEarnings;
currentEarningsByChain: Record<string, TokenEarnings>;
unrealizedEarningsByChain: Record<string, TokenEarnings>;
lastCheckTimestamp?: string;
};
}获取钱包的链上收益,按代币(USDC、WETH)拆分统计。
typescript
const earnings = await sdk.getOnchainEarnings(smartWalletAddress);
console.log("Total earnings by token:", earnings.data.totalEarningsByToken);
// { "USDC": 150.50, "WETH": 0.05 }
console.log("USDC earnings:", earnings.data.totalEarningsByToken["USDC"]);
console.log("WETH earnings:", earnings.data.totalEarningsByToken["WETH"]);返回:
typescript
// TokenEarnings is a record of token symbols to amounts
type TokenEarnings = Record<string, number>; // e.g., { "USDC": 100.5, "WETH": 0.025 }
interface OnchainEarningsResponse {
success: boolean;
data: {
walletAddress: string;
totalEarningsByToken: TokenEarnings;
lifetimeEarningsByToken: TokenEarnings;
currentEarningsByChain: Record<string, TokenEarnings>;
unrealizedEarningsByChain: Record<string, TokenEarnings>;
lastCheckTimestamp?: string;
};
}registerAgentOnIdentityRegistry (ERC-8004)
registerAgentOnIdentityRegistry (ERC-8004)
Register your Zyfai deployed agent on the Identity Registry following the ERC-8004 standard. This is used for OpenClaw agent registration. The method fetches a tokenUri containing the agent's metadata stored on IPFS, then registers it on-chain.
Supported Chains:
| Chain | Chain ID |
|---|---|
| Base | 8453 |
| Arbitrum | 42161 |
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
| smartWallet | string | Yes | The Zyfai deployed smart wallet address to register as an agent |
| chainId | SupportedChainId | Yes | Chain ID (only 8453 or 42161) |
Example:
typescript
const sdk = new ZyfaiSDK({ apiKey: "your-api-key" });
await sdk.connectAccount(walletClient, 8453);
// Get smart wallet address
const walletInfo = await sdk.getSmartWalletAddress(userAddress, 8453);
const smartWallet = walletInfo.address;
// Register agent on Identity Registry
const result = await sdk.registerAgentOnIdentityRegistry(smartWallet, 8453);
console.log("Registration successful:");
console.log(" Tx Hash:", result.txHash);
console.log(" Chain ID:", result.chainId);
console.log(" Smart Wallet:", result.smartWallet);Returns:
typescript
interface RegisterAgentResponse {
success: boolean;
txHash: string;
chainId: number;
smartWallet: string;
}How It Works:
- Fetches a from the Zyfai API (agent metadata stored on IPFS)
tokenUri - Encodes the call for the Identity Registry contract
register(tokenUri) - Sends the transaction from the connected wallet
- Waits for on-chain confirmation
按照ERC-8004标准,将你通过Zyfai部署的Agent注册到身份注册表中。该方法用于OpenClaw Agent注册。方法会先获取存储在IPFS上的包含Agent元数据的,然后在链上完成注册。
tokenUri支持的链:
| 链名称 | 链ID |
|---|---|
| Base | 8453 |
| Arbitrum | 42161 |
参数:
| 参数名 | 类型 | 是否必填 | 说明 |
|---|---|---|---|
| smartWallet | string | 是 | 要注册为Agent的Zyfai部署的智能钱包地址 |
| chainId | SupportedChainId | 是 | 链ID(仅支持8453或42161) |
示例:
typescript
const sdk = new ZyfaiSDK({ apiKey: "your-api-key" });
await sdk.connectAccount(walletClient, 8453);
// Get smart wallet address
const walletInfo = await sdk.getSmartWalletAddress(userAddress, 8453);
const smartWallet = walletInfo.address;
// Register agent on Identity Registry
const result = await sdk.registerAgentOnIdentityRegistry(smartWallet, 8453);
console.log("Registration successful:");
console.log(" Tx Hash:", result.txHash);
console.log(" Chain ID:", result.chainId);
console.log(" Smart Wallet:", result.smartWallet);返回:
typescript
interface RegisterAgentResponse {
success: boolean;
txHash: string;
chainId: number;
smartWallet: string;
}工作原理:
- 从Zyfai API获取(Agent元数据存储在IPFS上)
tokenUri - 为身份注册表合约编码调用
register(tokenUri) - 从已连接的钱包发送交易
- 等待链上确认
Security
安全说明
- Non-custodial — User's EOA owns the subaccount
- Session keys are limited — Can rebalance, cannot withdraw elsewhere
- Deterministic — Same EOA = same subaccount on every chain
- Flexible key management — Use wallet providers, WalletClients, or KMS integrations
- 非托管 —— 用户的EOA拥有子账户的所有权
- 会话密钥权限受限 —— 仅可执行再平衡,无法将资金提取到其他地址
- 确定性地址 —— 同一个EOA在所有链上对应相同的子账户地址
- 灵活的密钥管理 —— 支持钱包提供方、WalletClients或KMS集成
Key Management Best Practices
密钥管理最佳实践
For production autonomous agents, we recommend:
- Use a WalletClient with a secure key source (not raw private keys)
- Integrate with KMS (AWS KMS, GCP Cloud KMS) for hardware-backed key storage
- Consider Wallet-as-a-Service providers like Turnkey, Privy, or Dynamic
- Never hardcode private keys in source code
- Rotate keys periodically and implement key revocation procedures
对于生产环境的自主Agent,我们建议:
- 使用WalletClient搭配安全的密钥来源(不要使用原始私钥)
- 对接KMS(AWS KMS、GCP Cloud KMS)获取硬件级密钥存储
- 考虑使用钱包即服务提供方,例如Turnkey、Privy或Dynamic
- 永远不要在源代码中硬编码私钥
- 定期轮换密钥并实现密钥吊销流程
Troubleshooting
问题排查
Subaccount address mismatch across chains
跨链子账户地址不匹配
The subaccount address should be identical across all chains for the same EOA. If you see different addresses:
typescript
// Check addresses on both chains
const baseWallet = await sdk.getSmartWalletAddress(userAddress, 8453);
const arbWallet = await sdk.getSmartWalletAddress(userAddress, 42161);
if (baseWallet.address !== arbWallet.address) {
console.error("Address mismatch! Contact support.");
}If addresses don't match:
- Try redeploying on the affected chain
- If the issue persists, contact support on Telegram: @paul_zyfai
同一个EOA对应的子账户地址在所有链上应该完全一致。如果你看到不同的地址:
typescript
// Check addresses on both chains
const baseWallet = await sdk.getSmartWalletAddress(userAddress, 8453);
const arbWallet = await sdk.getSmartWalletAddress(userAddress, 42161);
if (baseWallet.address !== arbWallet.address) {
console.error("Address mismatch! Contact support.");
}如果地址不匹配:
- 尝试在受影响的链上重新部署
- 如果问题仍然存在,请通过Telegram联系支持:@paul_zyfai
"Deposit address not found" error
"存款地址未找到"错误
This means the wallet isn't registered in the backend. Solution:
- Call first — even if the Safe is already deployed on-chain, this registers it with the backend
deploySafe() - Then retry
createSessionKey()
这意味着钱包未在后端注册。解决方案:
- 先调用—— 即使Safe已经在链上部署,该调用也会将其注册到后端
deploySafe() - 然后重试
createSessionKey()
"Invalid signature" error
"无效签名"错误
This typically means:
- The wallet/signer doesn't match the EOA you're passing
- The Safe address on-chain doesn't match what the SDK expects
Verify you're using the correct wallet for the EOA.
通常意味着:
- 钱包/签名者与你传入的EOA不匹配
- 链上的Safe地址与SDK预期的地址不匹配
请确认你使用的是对应EOA的正确钱包。
Resources
资源
- Get API Key: sdk.zyf.ai or programmatically via
POST /api/sdk-api-keys/create - Docs: docs.zyf.ai
- Demo: github.com/ondefy/zyfai-sdk-demo
- MCP Server: mcp.zyf.ai — Use with Claude or other MCP-compatible agents
- Agent Registration: zyf.ai/.well-known/agent-registration.json
- 获取API密钥: sdk.zyf.ai 或通过编程获取
POST /api/sdk-api-keys/create - 文档: docs.zyf.ai
- 演示项目: github.com/ondefy/zyfai-sdk-demo
- MCP服务端: mcp.zyf.ai —— 可与Claude或其他兼容MCP的Agent搭配使用
- Agent注册: zyf.ai/.well-known/agent-registration.json
License
许可证
MIT License
Copyright (c) 2026 Zyfai
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
MIT许可证
版权所有 (c) 2026 Zyfai
特此免费授予任何获得本软件及相关文档文件(以下简称"软件")副本的人无限制处理软件的权利,包括但不限于使用、复制、修改、合并、发布、分发、再许可和/或销售软件副本的权利,以及允许获得软件的人员这样做,前提是满足以下条件:
上述版权声明和本许可声明应包含在软件的所有副本或主要部分中。
本软件按"原样"提供,不提供任何明示或暗示的保证,包括但不限于对适销性、特定用途适用性和非侵权性的保证。在任何情况下,作者或版权持有人均不对任何索赔、损害或其他责任负责,无论是在合同诉讼、侵权行为还是其他方面,由软件或软件的使用或其他交易引起、由此产生或与之相关。