> ## Documentation Index
> Fetch the complete documentation index at: https://docs.zyf.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Agent Quickstart

> Compact SDK reference for AI agents and LLMs. All essential methods in one page.

If you're an agent, use this quickstart guide to get started with the Zyfai SDK.

The SDK calls two production backends. Swagger: [Execution](https://api.zyf.ai/api-docs) (`https://api.zyf.ai/api/v1`), [DeFi data](https://defiapi.zyf.ai/api-docs/) (`https://defiapi.zyf.ai/api/v2`). Full table: [API Reference Overview](/docs/sdk/api/overview#backend-apis-openapi).

<Note>
  This page is the SDK reference for agents. Raw markdown:

  [**docs.zyf.ai/docs/sdk/agent-quickstart.md**](https://docs.zyf.ai/docs/sdk/agent-quickstart.md)

  To connect a chat client to the hosted MCP server, start at [zyf.ai/skill.md](https://zyf.ai/skill.md).
</Note>

Turn any Ethereum wallet into a yield-generating account.

## What This Does

When a user wants to **earn yield** on their crypto, their first `sendDeposit` call associates their EOA with a **pre-deployed Smart Account** (Safe) that already has a signed session key. Funds deposited into this subaccount are automatically optimized across DeFi protocols. The user stays in full control and can withdraw anytime. Their EOA is unchanged.

```text theme={null}
┌─────────────────┐      ┌──────────────────────┐
│   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
* First deposit assigns the Safe **live on Base, Arbitrum, and Mainnet** at once
* No separate deploy or session-key step: `sendDeposit` is the onboarding call

## The Flow

```text theme={null}
1. CONNECT   →  Authenticate the user's wallet
2. DEPOSIT   →  First deposit assigns pre-deployed Safe + session; starts earning
3. WITHDRAW  →  Pull funds back to wallet (anytime)
```

<Warning>
  On the first deposit, Zyfai assigns a pre-deployed Safe with a signed session key to the EOA. The Safe is available immediately on Base, Arbitrum, and Ethereum Mainnet. This does not impact the EOA itself.
</Warning>

## Prerequisites

* **API Key**: Get one programmatically (see below) or manually at [sma.zyf.ai](https://sma.zyf.ai)
* **Wallet Connection**: Either a wallet provider (browser) or secure key management (server)
* **Node.js 18+**

```bash theme={null}
npm install @zyfai/sdk viem
```

**Latest version:**

<a href="https://www.npmjs.com/package/@zyfai/sdk">
  <img src="https://img.shields.io/npm/v/@zyfai/sdk" alt="npm" />
</a>

### Programmatic API Key Creation (Agent-Native)

Agents can create their own API key without human intervention. You need the wallet address (public key) of the agent.

```bash theme={null}
POST https://sma.zyf.ai/api/sdk-api-keys/create
Content-Type: application/json

{
  "clientName": "my-agent",
  "walletAddress": "0x...",
  "email": "agent@example.com"
}
```

**Response:**

```json theme={null}
{
  "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 the `apiKey` securely. It cannot be retrieved later. The key is linked to the provided wallet address.

## Supported Chains and Assets

| Chain | ID | Assets |
| - | - | - |
| Ethereum Mainnet | 1 | USDC, WETH, EURC, USDT, PYUSD |
| Base | 8453 | USDC, WETH, EURC, NVDAc |
| Arbitrum | 42161 | USDC, WETH |

Decimals, least-unit amounts, and minimums (10,000 on Ethereum, 10 on Base and Arbitrum for stablecoins; WETH and NVDAc are a dollar minimum): [Supported Assets](/docs/sdk/supported-assets).

## Important: Always Use EOA Address

When calling SDK methods, **always pass the EOA address** (the user's wallet address) as `userAddress`, never the subaccount/Safe address. The SDK resolves the assigned Safe address for that EOA.

## Wallet Connection Options

The SDK supports multiple ways to connect a wallet. Choose based on your security requirements and deployment context.

### Option 1: Wallet Provider (Recommended for Browser/dApps)

Use an injected wallet provider like MetaMask. The private key never leaves the user's wallet.

```typescript theme={null}
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.

### Option 2: Viem WalletClient (Recommended for Server Agents)

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 theme={null}
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

### Option 3: Private Key String (Development Only)

Direct private key usage.

```typescript theme={null}
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.

### 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 |

## Step-by-Step

### 1. Connect to Zyfai

```typescript theme={null}
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. Deposit Funds

```typescript theme={null}
const userAddress = "0x..."; // User's EOA (NOT the subaccount address!)
const chainId = 8453; // Base

// First deposit assigns a pre-deployed Safe + session on Base, Arbitrum, and Mainnet.
// Optional strategy (default: "conservative") applies on first deposit only.
const sent = await sdk.sendDeposit(userAddress, chainId, "10000000", "USDC", "conservative");
await sdk.waitForDepositCredit(sent.registration.id, chainId);
await sdk.waitForDepositPosition(sent.registration.id, chainId);

// Deposit 0.5 WETH (18 decimals)
// IMPORTANT: User must have WETH, not ETH. Wrap ETH to WETH first if needed.
const sentWeth = await sdk.sendDeposit(userAddress, chainId, "500000000000000000", "WETH");
await sdk.waitForDepositCredit(sentWeth.registration.id, chainId);

// Deposit EURC (Mainnet / Base only)
const sentEurc = await sdk.sendDeposit(userAddress, chainId, "10000000", "EURC");
await sdk.waitForDepositCredit(sentEurc.registration.id, chainId);
```

Use the explicit model: `sendDeposit()` confirms and registers the transfer; `waitForDepositCredit()` resolves when the Safe balance is credited; `waitForDepositPosition()` resolves when allocation finishes or is skipped. Keep the deposit in a processing state while the lifecycle is `handover_pending`.

**Strategies** (first deposit only):

* `"conservative"`: Stable yield, lower risk (default)
* `"aggressive"`: Higher yield, higher risk
* `"yieldmaxxing"`: Adds async vaults. Required for NVDAc

Chains, decimals, and minimums: [Supported Assets](/docs/sdk/supported-assets).

After deposit you can inspect the assigned Safe:

```typescript theme={null}
const wallet = await sdk.getSmartWalletAddress(userAddress, chainId);
console.log(`Subaccount: ${wallet.address}`);
console.log(`Deployed: ${wallet.isDeployed}`);

const user = await sdk.getUserDetails();
console.log("Session key active:", user.hasActiveSessionKey);
```

### 3. Withdraw Funds

```typescript theme={null}
// 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");

// Partial WETH withdrawal (0.1 WETH)
await sdk.withdrawFunds(userAddress, chainId, "100000000000000000", "WETH");

// Withdraw EURC
await sdk.withdrawFunds(userAddress, chainId, undefined, "EURC");
```

Funds return to the user's EOA. After `withdrawFunds`, poll `result.lifecycle.id` with `waitForWithdrawSettlement` and `waitForWithdrawComplete` when async legs apply. Do not call `withdrawFunds` again for the same request.

### 4. Disconnect

```typescript theme={null}
await sdk.disconnectAccount();
```

## Complete Example

```typescript theme={null}
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);
  
  // First deposit assigns pre-deployed Safe + session (pass EOA as userAddress)
  const sent = await sdk.sendDeposit(userAddress, chainId, "100000000", "USDC", "conservative");
  await sdk.waitForDepositCredit(sent.registration.id, chainId);
  await sdk.waitForDepositPosition(sent.registration.id, chainId);
  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)
  const result = amount
    ? await sdk.withdrawFunds(userAddress, chainId, amount)
    : await sdk.withdrawFunds(userAddress, chainId);
  const withdrawalId = result.lifecycle?.id;
  if (withdrawalId) {
    const settled = await sdk.waitForWithdrawSettlement(withdrawalId, chainId);
    if (settled.status !== "completed") {
      await sdk.waitForWithdrawComplete(withdrawalId, chainId);
    }
  }
  console.log("Withdrawal complete");
  
  await sdk.disconnectAccount();
}
```

## API Reference

| Method | Params | Description |
| - | - | - |
| `connectAccount` | `(walletClientOrProvider, chainId)` | Authenticate with Zyfai |
| `getSmartWalletAddress` | `(userAddress, chainId)` | Get subaccount address & status |
| `sendDeposit` | `(userAddress, chainId, amount, asset, strategy?)` | Send, confirm, and register a deposit (first deposit onboard) |
| `waitForDepositCredit` | `(depositId, chainId, options?)` | Wait until the deposit is credited on the Safe |
| `waitForDepositPosition` | `(depositId, chainId, options?)` | Wait until allocation positioned or skipped |
| `withdrawFunds` | `(userAddress, chainId, amount?, assetType?)` | Withdraw USDC, WETH, or EURC (returns lifecycle id) |
| `waitForWithdrawSettlement` | `(withdrawalId, chainId, options?)` | Short poll until sync settles or async cooldown/claiming |
| `waitForWithdrawComplete` | `(withdrawalId, chainId, options?)` | Long poll until withdraw lifecycle `completed` |
| `getPositions` | `(userAddress, chainId?)` | Get active DeFi positions |
| `getAvailableProtocols` | `(chainId)` | Get available protocols & pools |
| `getAPYPerStrategy` | `(crossChain?, days?, strategy?, chainId?, tokenSymbol?)` | Get APY by strategy and token |
| `getUserDetails` | `(asset?)` | Get user details for one [supported asset](/docs/sdk/supported-assets) (default USDC) |
| `getOnchainEarnings` | `(walletAddress)` | Get earnings data by token |
| `updateUserProfile` | `(params)` | Update strategy, protocols, splitting per asset |
| `registerAgentOnIdentityRegistry` | `(smartWallet, chainId)` | Register agent on ERC-8004 Identity Registry |
| `disconnectAccount` | `()` | End session |

**Note:** All methods that take `userAddress` expect the **EOA address**, not the subaccount/Safe address.

## Data Methods

### 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 theme={null}
// 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 theme={null}
interface PositionsResponse {
  success: boolean;
  userAddress: string;
  positions: Position[];
}
```

### getAvailableProtocols

Get available DeFi protocols and pools for a specific chain with APY data.

```typescript theme={null}
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 theme={null}
interface ProtocolsResponse {
  success: boolean;
  chainId: SupportedChainId;
  protocols: Protocol[];
}
```

### getUserDetails

Get current authenticated user details including smart wallet, chains, protocols, and settings. Requires SIWE authentication.

```typescript theme={null}
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 `UpdateUserProfileResponse` (same as `updateUserProfile`).

### updateUserProfile

Update the authenticated user's profile settings including strategy, protocols, splitting, and cross-chain options. Requires SIWE authentication.

```typescript theme={null}
sdk.updateUserProfile(params: UpdateUserProfileRequest): Promise<UpdateUserProfileResponse>
```

**Parameters:**

```typescript theme={null}
interface UpdateUserProfileRequest {
  /** "conservative", "aggressive", or "yieldmaxxing" */
  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;
  /** Default "USDC". See Supported Assets. */
  asset?: "USDC" | "WETH" | "EURC" | "USDT" | "PYUSD" | "NVDAc";
}
```

**Note on `asset`:** Each asset has its own configuration. Use `asset: "WETH"` or `asset: "EURC"` to update that asset separately from USDC.

**Returns:**

```typescript theme={null}
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" | "EURC" | "USDT" | "PYUSD" | "NVDAc";
}
```

**Examples:**

```typescript theme={null}
// 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, **both** `crosschainStrategy` and `omniAccount` must be set to `true`. Never enable cross-chain settings by default.

```typescript theme={null}
// 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 `getAvailableProtocols(chainId)` to get valid protocol IDs before updating.
* **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 `minSplits` 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.
* **Cross-chain:** Requires **both** `crosschainStrategy: true` AND `omniAccount: 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.
* **Auto-compounding:** Enabled by default. When `true`, yields are reinvested automatically.
* Smart wallet address, chains, and `executorProxy` cannot be updated via this method.

### getAPYPerStrategy

Get global APY by strategy type, time period, chain, and token. Use this to compare expected returns between strategies before deploying. Prefer `average_apy_withFee` / `average_apy_with_rzfi_withFee` (gross × 0.9) for user-facing display. `@zyfai/sdk` 0.2.51 renamed these from `*_with_fee` / `*_without_fee`.

**Parameters:**

| Parameter | Type | Required | Description |
| - | - | - | - |
| crossChain | boolean | No | If `true`, returns APY for cross-chain strategies; if `false`, single-chain (default: `false`) |
| days | number | No | Period over which APY is calculated: `7`, `15`, `30`, `60` (default: `7`) |
| strategy | string | No | `"conservative"` (default), `"aggressive"`, or `"yieldmaxxing"` |
| chainId | number | No | Filter by specific chain ID (e.g., `8453` for Base) |
| tokenSymbol | string | No | `"USDC"`, `"WETH"`, `"EURC"`, `"USDT"`, `"PYUSD"`, or `"NVDAc"` |

**Example:**

```typescript theme={null}
// 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_withFee}%`);
console.log(`Aggressive 30d APY: ${aggressive.data[0]?.average_apy_withFee}%`);
```

**Returns:**

```typescript theme={null}
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_withFee: number;
  average_apy_with_rzfi_withFee: number;
  events_average_apy?: Record<string, number>;
  events_average_apy_withFee?: Record<string, number>;
  events_average_apy_with_rzfi_withFee?: Record<string, number>;
}
```

### getOnchainEarnings

Get onchain earnings for a wallet with total earnings by token.

```typescript theme={null}
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 theme={null}
export interface OnchainEarningsResponse {
  success: boolean;
  data: OnchainEarnings;
}

export interface OnchainEarnings {
  walletAddress: string;
  totalEarningsByToken: TokenEarnings;
  lastCheckTimestamp?: string;
  lastLogDate?: Record<string, string | null>;
}

// TokenEarnings is a record of token symbols to amounts
type TokenEarnings = Record<string, number>;  // e.g., { "USDC": 100.5, "WETH": 0.025 }
```

### 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 theme={null}
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 theme={null}
interface RegisterAgentResponse {
  success: boolean;
  txHash: string;
  chainId: number;
  smartWallet: string;
}
```

**How It Works:**

1. Fetches a `tokenUri` from the Zyfai API (agent metadata stored on IPFS)
2. Encodes the `register(tokenUri)` call for the Identity Registry contract
3. Sends the transaction from the connected wallet
4. Waits for on-chain confirmation

## 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

### Key Management Best Practices

For **production autonomous agents**, we recommend:

1. **Use a WalletClient** with a secure key source (not raw private keys)
2. **Integrate with KMS** (AWS KMS, GCP Cloud KMS) for hardware-backed key storage
3. **Consider Wallet-as-a-Service** providers like Turnkey, Privy, or Dynamic
4. **Never hardcode** private keys in source code
5. **Rotate keys** periodically and implement key revocation procedures

## Troubleshooting

### Subaccount address mismatch across chains

After first deposit, the assigned Safe address should be **identical** across Base, Arbitrum, and Mainnet for the same EOA. If you see different addresses:

```typescript theme={null}
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:** contact support on Telegram: [@paul\_zyfai](https://t.me/paul_zyfai)

### "Safe not available" / deposit failed on first use

The Safe is assigned on the first successful `sendDeposit` call. If deposit fails:

1. Confirm the EOA is connected via `connectAccount`
2. Confirm the chain and asset are supported (EURC: Mainnet/Base only)
3. Retry `sendDeposit` only if no transfer was broadcast; otherwise use `getDepositStatus` or `logDeposit` for the existing transaction. Do not call any separate deploy or session-key method.

### "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.

## Resources

* **Get API Key:** [sma.zyf.ai](https://sma.zyf.ai) or programmatically via `POST /api/sdk-api-keys/create`
* **Docs:** [docs.zyf.ai](https://docs.zyf.ai)
* **Demo:** [github.com/ondefy/zyfai-sdk-demo](https://github.com/ondefy/zyfai-sdk-demo)
* **MCP Server:** [mcp.zyf.ai](https://mcp.zyf.ai/mcp) (use with Claude or other MCP-compatible agents)
* **Agent Registration:** [zyf.ai/.well-known/agent-registration.json](https://www.zyf.ai/.well-known/agent-registration.json)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.