> ## Documentation Index
> Fetch the complete documentation index at: https://zyfai.mintlify.site/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.

<Tip title="Raw File Access">
  This page is also available as a raw markdown file:

  **[docs.zyf.ai/Skill.md](https://docs.zyf.ai/Skill.md)**
</Tip>

Turn any Ethereum wallet into a yield-generating account.

## What This Does

When a user wants to **earn yield** on their crypto, their first `depositFunds` 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.

```
┌─────────────────┐      ┌──────────────────────┐
│   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 — `depositFunds` is the onboarding call

## The Flow

```
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 title="First deposit = pre-deployed account">
  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@0.2.45 viem
```

**Latest version:** `0.2.45`

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

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

## 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.
await sdk.depositFunds(userAddress, chainId, "10000000", "USDC", "conservative");

// 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");

// Deposit EURC (Mainnet / Base only)
await sdk.depositFunds(userAddress, chainId, "10000000", "EURC");
```

Funds move from EOA → Subaccount and start earning yield immediately.

**Strategies** (first deposit only):

* `"conservative"` — Stable yield, lower risk (default)
* `"aggressive"` — Higher yield, higher risk

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. Withdrawals are processed asynchronously.

### 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)
  await sdk.depositFunds(userAddress, chainId, "100000000", "USDC", "conservative");
  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

| Method                            | Params                                                    | Description                                         |
| --------------------------------- | --------------------------------------------------------- | --------------------------------------------------- |
| `connectAccount`                  | `(walletClientOrProvider, chainId)`                       | Authenticate with Zyfai                             |
| `getSmartWalletAddress`           | `(userAddress, chainId)`                                  | Get subaccount address & status                     |
| `depositFunds`                    | `(userAddress, chainId, amount, asset, strategy?)`        | Deposit USDC, WETH, or EURC (first deposit onboard) |
| `withdrawFunds`                   | `(userAddress, chainId, amount?, assetType?)`             | Withdraw USDC, WETH, or EURC                        |
| `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 USDC, WETH, or EURC            |
| `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 {
  /** 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 settings for: "USDC" (default), "WETH", or "EURC" */
  asset?: "USDC" | "WETH" | "EURC";
}
```

**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";
}
```

**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.

**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       | Strategy risk profile: `"conservative"` or `"aggressive"` (default: `"conservative"`)          |
| chainId     | number  | No       | Filter by specific chain ID (e.g., `8453` for Base)                                            |
| tokenSymbol | string  | No       | Filter by token: `"USDC"`, `"WETH"`, or `"EURC"`                                               |

**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}%`);
console.log(`Aggressive 30d APY: ${aggressive.data[0]?.average_apy}%`);
```

**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_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

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 `depositFunds` 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 `depositFunds` — 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)
