@owney/sdk
v0.7.28
Published
A TypeScript SDK for interacting with DeFi yield agents. Supports multiple agents through a unified interface.
Readme
@owney/sdk
A TypeScript SDK for interacting with DeFi yield agents. Supports multiple agents through a unified interface.
Installation
npm install @owney/sdkQuick Start
import { OwneySDK } from "@owney/sdk";
// Create the SDK instance
const sdk = new OwneySDK({ apiKey: "your-owney-api-key" });
// Connect with an EIP-1193 wallet provider
await sdk.connect(provider);
// Activate agents on Base (fetches agent keys on first call)
await sdk.activateAgent(8453);
// Get balances across all agents
const balances = await sdk.getBalances();SDK Functions
new OwneySDK(config)
Create a new SDK instance.
| Param | Type | Description |
| --------------------- | ---------------------------------------------- | ------------------------------------------------------------------------------------------- |
| config.apiKey | string | Your Owney API key, used to authenticate with the routing API and fetch agent-specific keys |
| config.zyfaiRpcUrls | { 8453?: string; 42161?: string } (optional) | Per-chain RPC URL overrides used by Zyfai SDK network calls |
import { OwneySDK } from "@owney/sdk";
const sdk = new OwneySDK({ apiKey: "your-owney-api-key" });Custom Zyfai RPC URLs
Use zyfaiRpcUrls to override Zyfai SDK RPC endpoints (for example, to avoid public endpoint rate limits).
const sdk = new OwneySDK({
apiKey: "your-owney-api-key",
zyfaiRpcUrls: {
8453: "https://base-mainnet.g.alchemy.com/v2/<key>",
42161: "https://arb-mainnet.g.alchemy.com/v2/<key>",
},
});Notes:
- These overrides are used by Zyfai SDK flows only.
- If omitted, Zyfai SDK uses its default RPC endpoints.
- Other app-level RPC clients (for example, direct viem calls outside Zyfai) are configured separately.
connect(provider)
Establish connection state. Must be called before any wallet-dependent operation.
| Param | Type | Description |
| ---------- | ----------------- | ---------------------------------------------- |
| provider | EIP-1193 provider | Wallet provider (e.g. MetaMask, WalletConnect) |
await sdk.connect(window.ethereum);disconnect()
Disconnect from all agents and clear connection state.
await sdk.disconnect();isConnected()
Check if the SDK has an active connection.
if (sdk.isConnected()) {
// safe to call wallet-dependent methods
}getActiveChainId()
Get the currently active chain ID, set by activateAgent.
Returns: number | null
const chainId = sdk.getActiveChainId();
if (chainId) {
console.log(`Active on chain ${chainId}`);
}activateAgent(chainId, agentId?)
Activate the user's smart wallet for the specified agents, or all chain-compatible agents if omitted. Smart wallets are pre-deployed by the underlying providers, so activation only performs authentication and profile syncing — no deploy or session-key signatures. Saves the chainId for use by all subsequent SDK calls.
| Param | Type | Description |
| --------- | ---------------------- | ----------------------------------------------------------------------------- |
| chainId | number | Target chain ID (e.g. 8453 for Base, 42161 for Arbitrum) |
| agentId | AgentId[] (optional) | Agents to activate. Omit to activate all agents that support the given chain. |
// Activate specific agents on Base
await sdk.activateAgent(8453, ["zyfai"]);
// Activate all agents that support Arbitrum
await sdk.activateAgent(42161);deposit(options)
Deposit funds into a specific agent, or split equally across all agents if agentId is omitted. Validates that the asset is supported by the target agent(s) on the active chain, and per-agent minimum deposit amounts (defined per chain+asset on agent.supportedAssets[].assets[].minDepositAmount) are taken into account. When splitting across agents, depositCallback is invoked once per final valid agent with that agent's split amount and smart wallet address — expect multiple wallet prompts.
| Param | Type | Description |
| ------------------------- | -------------------- | --------------------------------------------------------------- |
| options.amount | string | Amount in smallest unit (e.g. "100000000" for 100 USDC) |
| options.asset | string | Asset symbol (e.g. "USDC") |
| options.depositCallback | DepositCallback | Callback that performs the token transfer and returns a tx hash |
| options.agentId | AgentId (optional) | Target agent. Omit to split equally across all agents. |
Zyfai's native transfer path uses sendDeposit(). It returns after the token
transfer confirms and the deposit is registered, while balance credit may still
be queued. Balance reads reflect subsequent credit and allocation; the deposit
call does not wait for waitForDepositCredit().
A successful deposit result confirms the transfer; it does not guarantee that
the funds are already credited or earning yield.
Returns: OwneyDepositResult (single agent) or OwneyMultiDepositResult (all agents)
// Deposit to a specific agent
const result = await sdk.deposit({
agentId: "zyfai",
amount: "100000000",
asset: "USDC",
depositCallback: async (smartWallet, chainId, amount) => {
const txHash = await transferTokens(smartWallet, amount);
return txHash;
},
});
console.log(result.txHash); // "0x..."
console.log(result.smartWallet); // "0x..."
console.log(result.amount); // "100000000"
// Split equally across all agents
const results = await sdk.deposit({
amount: "100000000",
asset: "USDC",
depositCallback: async (smartWallet, chainId, amount) => {
return await transferTokens(smartWallet, amount);
},
});
console.log(results.agentResults.zyfai.amount); // "100000000"withdraw(options)
Withdraw funds from a specific agent, or all agents that support the active chain+asset if agentId is omitted. Validates that the asset is supported.
When agentId is omitted with a partial amount, the SDK splits the requested amount proportionally across eligible agents based on each agent's per-asset balance. If an agent's withdraw fails, its share is redistributed to remaining not-yet-attempted agents (capped by their headroom).
Full withdrawals (no amount) iterate every eligible agent and continue on per-agent failures — successful agents still return their results. Per-agent failures (e.g. backend returns success: false) surface as WITHDRAW_FAILED from the agent and are caught at the aggregation layer.
Throws:
WITHDRAW_INSUFFICIENT_BALANCE— total available across agents is less thanamount.WITHDRAW_ALL_FAILED— every agent's withdraw call failed (full or partial). Carries per-agent failure messages viaerror.details.agentErrors(aRecord<agentId, string>).WITHDRAW_PARTIAL_FAILURE— partial withdrawals only: some agents succeeded but total withdrawn is less thanamount. Successful txs surfaced viaerror.details.partialResults; per-agent failures viaerror.details.agentErrors.WITHDRAW_FAILED— a single agent's underlying withdraw call returned an unsuccessful response. Includeserror.agentIdand the backend message inerror.details.response.
Any error originating from a specific agent also has error.agentId set and a [<code>][agent:<id>] prefix on error.message.
| Param | Type | Description |
| ----------------- | -------------------- | --------------------------------------------- |
| options.asset | string | Asset symbol (e.g. "USDC") |
| options.amount | string (optional) | Amount to withdraw. Omit for full withdrawal. |
| options.agentId | AgentId (optional) | Target agent. Omit to withdraw from all. |
Returns: AgentWithdrawResult (single agent) or OwneyWithdrawResult (all agents)
// Partial withdrawal from one agent
const result = await sdk.withdraw({
asset: "USDC",
agentId: "zyfai",
amount: "50000000",
});
console.log(result.type); // "partial"
console.log(result.amount); // "50000000"
// Partial withdrawal split proportionally across all agents
// e.g. zyfai=$5, amount=$4 -> zyfai withdraws $4
const split = await sdk.withdraw({ asset: "USDC", amount: "4000000" });
console.log(split.agentResult.zyfai.amount); // "4000000"
// Full withdrawal from all agents
const results = await sdk.withdraw({ asset: "USDC" });
console.log(results.agentResult.zyfai.type); // "full"getBalances(agentId?)
Get the user's balances for a specific agent, or aggregated across all agents. Balances are shown after any applicable performance fees have been deducted.
| Param | Type | Description |
| --------- | -------------------- | ------------------------------------ |
| agentId | "zyfai" (optional) | Agent to query. Omit for all agents. |
Returns: AgentBalance (single agent) or OwneyBalances (all agents)
// Single agent
const balance = await sdk.getBalances("zyfai");
console.log(balance.totalBalance); // "150.25"
console.log(balance.totalBalanceAsset); // "usdc" — unit of totalBalance
console.log(balance.smartWallet); // "0x..."
console.log(balance.tokens); // [{ chain: "BASE", chainId: 8453, asset: "USDC", amount: "150.25" }]
// All agents
const allBalances = await sdk.getBalances();
console.log(allBalances.totalBalance); // "150.25"
console.log(allBalances.totalBalanceAsset); // "usdc" — unit of totalBalance
console.log(allBalances.agentBalances.zyfai.totalBalance); // "150.25"
console.log(allBalances.agentBalances.zyfai.totalBalanceAsset); // "usdc"getEarnings(agentId?)
Get the user's on-chain earnings for a specific agent, or aggregated across all agents. Earnings are shown after any applicable performance fees have been deducted.
| Param | Type | Description |
| --------- | -------------------- | ------------------------------------ |
| agentId | "zyfai" (optional) | Agent to query. Omit for all agents. |
Returns: AgentEarnings (single agent) or OwneyEarnings (all agents)
// Single agent
const earnings = await sdk.getEarnings("zyfai");
console.log(earnings.smartWallet); // "0x..."
console.log(earnings.lifetimeEarnings); // 42.5
console.log(earnings.tokens);
// [
// { chain: "BASE", chainId: 8453, asset: "USDC", amount: "30.0" },
// { chain: "ARBITRUM", chainId: 42161, asset: "USDC", amount: "12.5" },
// ]
// All agents
const allEarnings = await sdk.getEarnings();
console.log(allEarnings.totalEarnings); // "85.0"
console.log(allEarnings.agentEarnings.zyfai.lifetimeEarnings); // 42.5
console.log(allEarnings.agentEarnings.zyfai.tokens); // per-chain, per-asset breakdownrefreshEarnings(agentId?)
Refresh earnings for agents whose earnings data may be outdated. If the latest snapshot is less than one hour old, the SDK returns it without recalculating.
| Param | Type | Description |
| --------- | -------------------- | ------------------------------------ |
| agentId | "zyfai" (optional) | Agent to refresh. Omit for all agents. |
Returns: AgentEarnings (single agent) or OwneyEarnings (all agents)
// Single agent
const refreshed = await sdk.refreshEarnings("zyfai");
// All agents
const allRefreshed = await sdk.refreshEarnings();getAccountApy({ agentId?, days })
Get the weighted APY for the user's account. When querying all agents, the total APY is a balance-weighted average.
| Param | Type | Description |
| --------- | ------------------------ | ------------------------------------------------ |
| agentId | "zyfai" (optional) | Agent to query. Omit for balance-weighted total. |
| days | "7D" \| "14D" \| "30D" | Lookback period |
Returns: AccountAgentApy (single agent) or OwneyAccountApy (all agents)
// Single agent
const apy = await sdk.getAccountApy({ agentId: "zyfai", days: "30D" });
console.log(apy.walletAddress); // "0x..."
console.log(apy.weightedApyAfterFee); // 5.2
// Per chain + token breakdown, when the agent reports it
console.log(apy.weightedApyAfterFeeDetails?.apyPerAsset[8453]?.USDC); // 5.4
// Same per-(chain, asset) breakdown surfaced under a unified key
console.log(apy.apyByChainAndAsset?.[8453]?.USDC); // 5.4
// All agents (balance-weighted)
const allApy = await sdk.getAccountApy({ days: "30D" });
console.log(allApy.totalApy); // "5.6"
console.log(allApy.agentApy.zyfai.weightedApyAfterFee); // 6.0
// Per (chain, asset), balance-weighted across all agents.
// Cells with 0/missing APY for a given agent are excluded from that cell.
console.log(allApy.apyByChainAndAsset[8453]?.USDC); // 5.3
console.log(allApy.apyByChainAndAsset[42161]?.WETH); // 4.7getHistory({ agentId?, filters? })
Get transaction history for a specific agent, or all agents. When querying all agents, results are merged into a single list sorted by date (newest first). Each entry includes the originating agent and a transactions array with typed on-chain transaction details.
Results are cursor-paginated. The first call (no cursor) anchors a stable snapshot at the current time; subsequent pages within that same snapshot are consistent. Each new cursorless call captures a fresh snapshot — if you need to preserve scroll position while paging, always carry forward the cursor returned by the previous call rather than starting a new cursorless fetch.
Per-chain filtering: The Zyfai backend no longer accepts a
chainIdfilter directly. The SDK performs a bounded internal drain (up to 5 backend round-trips per logical page) to fill the requestedlimitwith entries matching the active chain. This is transparent to callers — thegetHistoryAPI is unchanged.
| Param | Type | Description |
| ------------------ | -------------------- | ------------------------------------------------------- |
| agentId | "zyfai" (optional) | Agent to query. Omit for all agents. |
| filters.fromDate | string (optional) | Start date (YYYY-MM-DD) |
| filters.toDate | string (optional) | End date (YYYY-MM-DD) |
| filters.limit | number (optional) | Max entries returned per call. Defaults to 10. |
| filters.cursor | string (optional) | Opaque cursor returned by a previous getHistory call. |
Returns: OwneyAgentHistory
// Single agent with filters
const history = await sdk.getHistory({
agentId: "zyfai",
filters: { fromDate: "2025-01-01" },
});
console.log(history.hasMore); // true | false
console.log(history.data[0].agent); // "zyfai"
console.log(history.data[0].action); // "Rebalance"
console.log(history.data[0].date); // "2025-03-15T12:00:00Z"
console.log(history.data[0].transactions[0].txHashes); // ["0x..."]
console.log(history.data[0].transactions[0].amount); // "100.00"
console.log(history.data[0].transactions[0].tokenSymbol); // "USDC"
// Rebalance logs show fund movement between protocols
console.log(history.data[0].rebalanceLog[0].fromProtocol); // "Aave V3"
console.log(history.data[0].rebalanceLog[0].toProtocol); // "Morpho Blue"
console.log(history.data[0].rebalanceLog[0].tokenSymbol); // "USDC"
console.log(history.data[0].rebalanceLog[0].amount); // "100.00"
console.log(history.data[0].rebalanceLog[0].status); // "success"
// All agents (merged and sorted by date)
const allHistory = await sdk.getHistory();
console.log(allHistory.hasMore); // true | false
console.log(allHistory.data[0].agent); // "zyfai" (most recent entry)
console.log(allHistory.data[0].action); // "Rebalance"
// Paginate through all history (per-chain, anchored to a single snapshot)
const allEntries: AgentHistoryEntry[] = [];
let cursor: string | undefined;
do {
const page = await sdk.getHistory({
filters: { cursor, limit: 50 },
});
allEntries.push(...page.data);
cursor = page.hasMore ? page.nextCursor : undefined;
} while (cursor);getUserProfile(agentId?)
Get the user's profile for a specific agent, or all agents.
| Param | Type | Description |
| --------- | -------------------- | ------------------------------------ |
| agentId | "zyfai" (optional) | Agent to query. Omit for all agents. |
Returns: AgentUserProfile (single agent) or OwneyUserProfile (all agents)
// Single agent
const profile = await sdk.getUserProfile("zyfai");
console.log(profile.address); // "0x..."
console.log(profile.smartWallet); // "0x..."
console.log(profile.chains); // [8453]
console.log(profile.hasActiveSessionKey); // true
console.log(profile.protocols); // ["aave", "compound"]
// All agents
const allProfiles = await sdk.getUserProfile();
console.log(allProfiles.agentUserProfile.zyfai.smartWallet); // "0x..."getAgentApy({ agentId?, days, tokenSymbol?, chainId? })
Get the agent's average APY performance over a time period. Does not require a wallet connection.
| Param | Type | Description |
| ------------- | ------------------------ | ------------------------------------------------------------------------------------------------------------------- |
| agentId | "zyfai" (optional) | Agent to query. Omit for all agents. |
| days | "7D" \| "14D" \| "30D" | Lookback period |
| tokenSymbol | string (optional) | Asset symbol (e.g. "USDC"). Scopes the upstream request to a specific asset when the agent's backend supports it. |
| chainId | number (optional) | Chain id. Typically paired with tokenSymbol for per-asset+chain APY. |
Returns: AgentApy (single agent) or OwneyAgentApy (all agents)
The agent populates detailedApys.apyPerAsset with a per-chain, per-token
breakdown when available. tokenSymbol / chainId only affect whether the
upstream query is scoped — the breakdown is built from whatever the agent
returns.
// Single agent
const apy = await sdk.getAgentApy({ agentId: "zyfai", days: "30D" });
console.log(apy.averageApy); // 8.5
console.log(apy.detailedApys?.apyPerAsset[8453]?.USDC); // 8.7
// All agents
const allApy = await sdk.getAgentApy({ days: "30D" });
console.log(allApy.agentApy.zyfai.averageApy); // 8.5
// Scope the upstream query to USDC on Base
const usdcBaseApy = await sdk.getAgentApy({
days: "30D",
tokenSymbol: "USDC",
chainId: 8453,
});
console.log(usdcBaseApy.agentApy.zyfai.averageApy); // APY scoped to USDC on BaseTypes
Configuration
type AgentId = "zyfai";
type Asset = string;
type DailyApyDays = "7D" | "14D" | "30D";
type OwneySupportedChainId = 8453 | 42161;
type OwneySupportedChains = "BASE" | "ARBITRUM";
type OwneySupportedTokens = "USDC" | "WETH";
type AgentSupportedAsset = {
readonly symbol: string;
/** Minimum deposit in the asset's smallest unit (e.g. wei, "0" for none). */
readonly minDepositAmount: string;
};
type AgentSupportedAssets = {
readonly chainId: number;
readonly chain?: string;
readonly assets: readonly AgentSupportedAsset[];
};
interface OwneySDKConfig {
apiKey: string;
zyfaiRpcUrls?: {
8453?: string;
42161?: string;
};
}
interface ConnectionState {
provider: any;
walletAddress: `0x${string}`;
chainId: number | null;
}
type HistoryFilters = {
fromDate?: string; // YYYY-MM-DD
toDate?: string; // YYYY-MM-DD
/** Max entries returned per call. Defaults to 10. */
limit?: number;
/** Opaque cursor returned by a previous getHistory call. */
cursor?: string;
};
type HistoryOptions = {
agentId?: AgentId;
filters?: HistoryFilters;
};
type DepositOptions = {
amount: string;
asset: Asset;
depositCallback: DepositCallback;
agentId?: AgentId;
};
type WithdrawOptions = {
asset: Asset;
amount?: string;
agentId?: AgentId;
};
type AccountApyOptions = {
agentId?: AgentId;
days: DailyApyDays;
};
type AgentsApyOptions = {
agentId?: AgentId;
days: DailyApyDays;
tokenSymbol?: string; // e.g. "USDC", "WETH"
chainId?: number; // typically paired with tokenSymbol
};Response Types
interface OwneyDepositResult {
txHash: string;
smartWallet: string;
amount: string;
}
interface OwneyMultiDepositResult {
agentResults: Record<string, OwneyDepositResult>;
}
interface AgentWithdrawResult {
txHash?: string;
type: "full" | "partial";
amount: string;
}
interface OwneyWithdrawResult {
agentResult: Record<AgentId, AgentWithdrawResult>;
}
interface OwneyToken {
chain: OwneySupportedChains;
chainId: OwneySupportedChainId;
asset: OwneySupportedTokens;
amount: string;
}
interface AgentBalance {
smartWallet?: `0x${string}`;
totalBalance: string;
totalBalanceAsset: string; // unit of totalBalance, currently always "usdc"
tokens: OwneyToken[];
}
interface OwneyBalances {
totalBalance: string;
totalBalanceAsset: string; // unit of totalBalance, currently always "usdc"
agentBalances: Record<AgentId, AgentBalance>;
}
interface AgentEarnings {
smartWallet: `0x${string}`;
lifetimeEarnings: number;
tokens: OwneyToken[];
}
interface OwneyEarnings {
totalEarnings: string;
agentEarnings: Record<AgentId, AgentEarnings>;
}
type ApyByChainAndAsset = Partial<
Record<OwneySupportedChainId, Partial<Record<OwneySupportedTokens, number>>>
>;
interface AccountAgentApy {
walletAddress: string;
weightedApyAfterFee?: number;
weightedApyAfterFeeDetails?: AgentApyDetails;
apyByChainAndAsset?: ApyByChainAndAsset;
}
interface OwneyAccountApy {
totalApy: string;
agentApy: Record<AgentId, AccountAgentApy>;
apyByChainAndAsset: ApyByChainAndAsset;
}
type HistoryAction = "Rebalance" | "Deposit" | "Top up" | "Withdraw" | "Earned";
interface HistoryTransaction {
txHashes: string[];
chainId?: number;
tokenSymbol?: string;
amount?: string;
}
interface RebalanceLog {
fromProtocol: string;
toProtocol: string;
tokenSymbol: string;
amount: string;
status: "success" | "failed";
}
interface AgentHistoryEntry {
agent: AgentId;
action: HistoryAction;
date: string;
oldApy: string | null;
newApy: string | null;
transactions: HistoryTransaction[];
rebalanceLog: RebalanceLog[];
}
interface OwneyAgentHistory {
data: AgentHistoryEntry[];
/** True iff a subsequent page exists; pair with `nextCursor`. */
hasMore: boolean;
/** Opaque cursor for the next page. Present iff `hasMore === true`. */
nextCursor?: string;
}
interface AgentUserProfile {
address: string;
smartWallet: string;
chains: number[];
strategy?: string;
hasActiveSessionKey: boolean;
protocols: string[];
}
interface OwneyUserProfile {
agentUserProfile: Record<AgentId, AgentUserProfile>;
}
interface AgentApy {
averageApy: number;
detailedApys?: AgentApyDetails;
}
interface AgentApyDetails {
// Per-chain, per-token APY breakdown. Only populated for the chain/token
// pairs that appear in SupportedAssets — entries may be missing when the
// upstream agent doesn't report APY for a given chain or token.
apyPerAsset: Partial<
Record<OwneySupportedChainId, Partial<Record<OwneySupportedTokens, number>>>
>;
}
interface OwneyAgentApy {
agentApy: Record<AgentId, AgentApy>;
}Callback Types
type DepositCallback = (
smartWalletAddress: string,
chainId: number,
amount: string,
) => Promise<`0x${string}`> | `0x${string}`;Error Handling
All errors thrown by the SDK are instances of OwneyError (which extends Error), each carrying a stable code string, optional details object, and optional agentId identifying the originating agent. error.message is always prefixed with [<code>] (and [<code>][agent:<id>] when agent-scoped) so the code is visible in browser console logs without inspecting the error object.
import { OwneyError } from "@owney/sdk";
try {
await sdk.getBalances();
} catch (error) {
if (error instanceof OwneyError) {
switch (error.code) {
case "NOT_CONNECTED":
// prompt user to connect wallet
break;
case "NO_ACTIVE_CHAIN":
// prompt user to activate an agent
break;
case "BALANCE_ALL_FAILED":
// all agents failed to return balances
break;
default:
console.error(`[${error.code}] ${error.message}`, error.details);
}
}
}type OwneyErrorCode =
// Connection & wallet
| "NOT_CONNECTED" // Wallet-dependent method called before connect()
| "NO_ACTIVE_CHAIN" // Method called before activateAgent()
| "WALLET_NO_ACCOUNTS" // Provider returned no accounts during connect()
| "WALLET_ADDRESS_REQUIRED" // Wallet address missing during agent auth
// Agent resolution
| "AGENT_NOT_FOUND" // Unknown agent ID passed
| "AGENT_CHAIN_INCOMPATIBLE" // Agent(s) don't support the given chain
| "AGENT_EMPTY_LIST" // Empty agentId array passed to activateAgent()
// Chain
| "CHAIN_UNSUPPORTED" // Chain ID not in supported list (8453, 42161)
| "CHAIN_NO_COMPATIBLE_AGENTS" // No agents support the given chain
// Asset
| "ASSET_UNSUPPORTED" // Agent doesn't support the asset on the active chain
| "ASSET_NO_COMPATIBLE_AGENTS" // No agents support the asset on the active chain
// Deposit
| "DEPOSIT_AMOUNT_BELOW_MINIMUM" // Amount below an agent's required minimum deposit for the chain+asset
| "DEPOSIT_CALLBACK_REQUIRED" // Agent requires a depositCallback
| "DEPOSIT_CALLBACK_INVALID" // depositCallback didn't return a tx hash
| "DEPOSIT_NO_PERMITTED_TOKENS" // No permitted tokens available for deposit
// Withdraw
| "WITHDRAW_NO_PERMITTED_TOKENS" // No permitted tokens available for withdrawal
| "WITHDRAW_INSUFFICIENT_BALANCE" // Requested amount exceeds total available across agents
| "WITHDRAW_ALL_FAILED" // Every eligible agent's withdraw call failed
| "WITHDRAW_PARTIAL_FAILURE" // Some agents succeeded; total withdrawn < requested
| "WITHDRAW_FAILED" // Agent backend returned an unsuccessful withdraw response
// API
| "API_ROUTING_ERROR" // Routing API returned a non-OK HTTP status
| "API_ROUTING_FAILED" // Routing API returned { success: false }
| "API_NO_AGENTS" // No supported agents returned by routing API
// Aggregation
| "BALANCE_ALL_FAILED" // All agents failed to return balances
| "ALLOCATION_ALL_FAILED" // All agents failed to return allocation data
// Validation
| "VALIDATION_INVALID_DAYS"; // Invalid days value (expected "7D", "14D", or "30D")
class OwneyError extends Error {
readonly code: OwneyErrorCode;
readonly details?: Record<string, unknown>;
readonly agentId?: string;
}The three convenience subclasses extend OwneyError and remain backwards-compatible:
| Class | Code | Extra properties |
| ----------------------------- | -------------------------- | ---------------------------------------- |
| NotConnectedError | NOT_CONNECTED | — |
| AgentNotFoundError | AGENT_NOT_FOUND | — |
| AgentChainIncompatibleError | AGENT_CHAIN_INCOMPATIBLE | incompatibleAgents, connectedChainId |
Cursor errors
InvalidHistoryCursorError is thrown by getHistory when the supplied cursor is invalid. It extends Error directly — not OwneyError — so a catch block that only checks instanceof OwneyError will not catch it. Catch it separately:
import { InvalidHistoryCursorError } from "@owney/sdk";
try {
const page = await sdk.getHistory({ filters: { cursor: storedCursor } });
} catch (error) {
if (error instanceof InvalidHistoryCursorError) {
// Stored cursor is stale or malformed — reset pagination.
storedCursor = undefined;
// Refetch first page...
} else {
throw error;
}
}InvalidHistoryCursorError carries a stable code property (value: "invalid_cursor") and is thrown when:
- The supplied cursor is an empty string.
- The cursor is not valid base64 JSON.
- The cursor payload has the wrong shape (e.g. a cursor from a different pagination context).
Recovery: catch InvalidHistoryCursorError, clear your stored cursor, and refetch from page 1.
| Class | code | Extends |
| --------------------------- | ------------------ | ------- |
| InvalidHistoryCursorError | "invalid_cursor" | Error |
License
ELv2
