# Zenji (ZenSats) - DeFi Carry Trade Vaults # https://zensats.app # llms.txt -machine-readable documentation for AI agents # Standard: https://llmstxt.org # Last updated: 2026-04-04 ## What is Zenji? Zenji is a set of ERC-4626 yield vaults on Ethereum mainnet. Each vault accepts a single collateral asset (WBTC, wstETH, or XAUT), borrows stablecoins against it, and deploys that capital into yield strategies. Depositors earn the spread between the borrowing cost and the yield -an automated carry trade. All vaults are fully ERC-4626 compliant. Any tooling that supports ERC-4626 (deposit, withdraw, redeem, mint) works with Zenji vaults. ## Quick Start for AI Agents If you are an AI agent looking to deposit into Zenji vaults, start here: 1. Call GET https://zensats.app/api/agent/vaults to get all active vaults with live TVL, APR, share prices, contract addresses, and step-by-step deposit instructions. 2. The response includes `howToDeposit` for each vault with the exact contract calls needed. 3. The response includes `onChainViewFunctions` listing every view function you can call directly on the smart contracts. That single endpoint gives you everything you need to evaluate and execute a deposit. ## Network Chain: Ethereum Mainnet (chainId: 1) ## Active Vaults ### 1. WBTC Vault (wbtc-pmusd) - Vault address: 0x617A6877f0a55D1eF2B64b5861A2bB5Fe6FEB739 - Deposit asset: WBTC (0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599, 8 decimals) - Strategy: Borrow USDT on Aave V3 -> swap to crvUSD via Curve -> LP in RAAC crvUSD/pmUSD pool -> stake in StakeDAO - Strategy address: 0x73B753F63175F003881Dc39710d40c8E2F027FD8 - Share token decimals: 8 (same as underlying) - ViewHelper: 0x0aDC622F11D8fA2F3E4709D037e95010a651471A - LoanManager: 0x25a1b8262f9644F00Fc80F11eF8cc2Ea1b74BDE3 ### 2. wstETH Vault (wsteth-pmusd) - Vault address: 0xbaEc8343B610A5ee7Ca2c5b93507AC7def98E2B1 - Deposit asset: wstETH (0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0, 18 decimals) - Strategy: Borrow USDT on Aave V3 against wstETH -> swap to crvUSD via Curve -> LP in RAAC crvUSD/pmUSD pool -> stake in StakeDAO - Strategy address: 0x48c166Ad4E0fa3B344fb7efb93Fdee4bf7d9e861 - Share token decimals: 18 (same as underlying) - ViewHelper: 0xc99e21f05722230E7B5A26f9De1Db415080c872E - LoanManager: 0x10a0Cec186B5B5365E9D8b671cE423b4eCbDeeCc ### 3. XAUT Vault (xaut-pmusd) - Vault address: 0x7d5281D590Fb0647aDc7d8494a2c8Fb8C2B23cBD - Deposit asset: XAUT (0x68749665FF8D2d112Fa859AA293F07A622782F38, 6 decimals) - Strategy: Borrow USDT on Aave V3 against Tether Gold -> swap to crvUSD via Curve -> LP in RAAC crvUSD/pmUSD pool -> stake in StakeDAO - Strategy address: 0xE2B44aE759446eF4C0151A2669122b4794Aee7C0 - Share token decimals: 6 (same as underlying) - ViewHelper: 0x40c2D606Ce9E060Bef7374951BaE4Ca6906517BD - LoanManager: 0x55aE1060c67eB78E6f655dfcDE4386bbB063bEB5 ## Agent API Endpoint GET https://zensats.app/api/agent/vaults Returns a JSON object with all active vaults, their current state, and deposit/withdraw instructions. This is the recommended starting point for any AI agent. Response structure: ```json { "protocol": "Zenji (ZenSats)", "description": "...", "network": "Ethereum Mainnet (chainId: 1)", "timestamp": "2026-04-04T12:00:00.000Z", "vaults": [ { "slug": "wbtc-pmusd", "vaultAddress": "0x...", "viewHelperAddress": "0x...", "loanManagerAddress": "0x...", "oracleAddress": "0x...", "chainId": 1, "depositAsset": { "symbol": "WBTC", "address": "0x...", "decimals": 8 }, "strategy": "Borrow USDT on Aave V3 -> ...", "standard": "ERC-4626", "state": { "totalAssets": "raw uint256", "totalAssetsFormatted": "human readable", "totalSupply": "raw uint256", "pricePerShare": "raw uint256", "pricePerShareFormatted": "human readable", "tvlUsd": 12345.67, "assetPriceUsd": 84000.50, "currentLtvPct": 70.5, "apr24hBps": "500", "apr7dBps": "480", "snapshotTimestamp": 1712188800 }, "howToDeposit": { "step1_approve": "Call approve(vaultAddress, amount) on ...", "step2_deposit": "Call deposit(amount, receiverAddress) on ...", "preview": "Call previewDeposit(amount) on ..." }, "howToWithdraw": { "redeem": "Call redeem(shares, receiver, owner) on ...", "withdraw": "Call withdraw(assets, receiver, owner) on ...", "checkMax": "Call maxRedeem(owner) or maxWithdraw(owner) on ..." }, "onChainViewFunctions": { "vault": ["totalAssets() -> uint256", "..."], "viewHelper": ["getHealth(address vault) -> int256", "..."], "loanManager": ["getCurrentLTV() -> uint256", "..."] } } ] } ``` APR values are in basis points (100 bps = 1%). A value of "500" means 5% APR. ## How to Deposit (step by step) Prerequisites: The caller must hold the vault's deposit asset (WBTC, wstETH, or XAUT) on Ethereum mainnet. Step 1 -Check if deposits are open: Call maxDeposit(address) on the vault. Pass any address. If it returns 0, deposits are paused. Step 2 -Approve the vault to spend your tokens: Call approve(vaultAddress, amount) on the deposit asset's ERC-20 contract. The amount must be in the asset's native decimals (8 for WBTC, 18 for wstETH, 6 for XAUT). Step 3 -Deposit: Call deposit(uint256 assets, address receiver) on the vault contract. - assets: amount of the deposit asset (in native decimals) - receiver: address that will receive the vault share tokens Returns: uint256 sharesMinted Alternative -Mint exact shares: Call mint(uint256 shares, address receiver) on the vault contract. Returns: uint256 assetsUsed To preview how many shares you'll get: Call previewDeposit(uint256 assets) -> uint256 shares ## How to Withdraw (step by step) Option A -Redeem by share amount (recommended): Call redeem(uint256 shares, address receiver, address owner) on the vault. - shares: number of vault shares to burn - receiver: address that receives the collateral - owner: address whose shares are burned (must be caller or have allowance) Returns: uint256 assetsReturned Option B -Withdraw by asset amount: Call withdraw(uint256 assets, address receiver, address owner) on the vault. - assets: amount of collateral to withdraw (in native decimals) - receiver: address that receives the collateral - owner: address whose shares are burned Returns: uint256 sharesBurned To check max withdrawable: Call maxWithdraw(address owner) -> uint256 maxAssets Call maxRedeem(address owner) -> uint256 maxShares ## How to Harvest Yield Yield from the strategy accrues over time and needs to be harvested periodically. Anyone can call this -it's permissionless. Call harvestYield() on the vault contract. No arguments needed. - This claims rewards from the yield strategy (e.g., CRV rewards from StakeDAO), sells them, and compounds them back into the strategy. - Reverts if emergencyMode is active or if no yield strategy is set. - Emits YieldHarvested(caller, harvestedAmount). Example for each vault: - WBTC Vault: call harvestYield() on 0x617A6877f0a55D1eF2B64b5861A2bB5Fe6FEB739 - wstETH Vault: call harvestYield() on 0xbaEc8343B610A5ee7Ca2c5b93507AC7def98E2B1 - XAUT Vault: call harvestYield() on 0x7d5281D590Fb0647aDc7d8494a2c8Fb8C2B23cBD ## How to Claim Merkle Rewards (StakeDAO) The strategies stake LP tokens in StakeDAO, which distributes rewards via Merkle proofs. These rewards need to be claimed separately. Step 1 -Check available rewards: Call GET https://zensats.app/api/merkle/:strategyAddress Pass the strategy address for the vault you want to claim for: - WBTC strategy: 0x73B753F63175F003881Dc39710d40c8E2F027FD8 - wstETH strategy: 0x48c166Ad4E0fa3B344fb7efb93Fdee4bf7d9e861 - XAUT strategy: 0xE2B44aE759446eF4C0151A2669122b4794Aee7C0 Returns an array of claimable rewards with merkle proofs. Step 2 -Claim rewards: Call execute(bytes[] claimsData) on the Merkle executor contract: 0x0f542fA75c871EB1b93Ef881b73e46acF733392f Each entry in claimsData is ABI-encoded: (merkleContract, account, token, amount, proof) prefixed with the claim function selector. Note: Merkle rewards are external to the vault -they go to the strategy address. Harvesting (harvestYield) is the separate process that compounds strategy value into the vault's PPS. ## How to Trigger Rebalance If the vault's LTV drifts outside its deadband (target LTV +/- spread), anyone can trigger a rebalance: Call rebalance() on the vault contract. No arguments needed. - Adjusts the borrowed amount to bring LTV back to target. - Reverts if rebalance is not needed (LTV within deadband). - Check isRebalanceNeeded(vaultAddress) on the ViewHelper first. Rebalance bounty: The vault has a configurable bounty that pays the caller a percentage of accrued yield fees as a reward for triggering rebalance(). Check rebalanceBountyRate() on the vault (1e18 precision, default 20%). The bounty is paid in the debt asset (e.g., crvUSD). Note: The performance fee switch is currently turned off (feeRate = 0) for growth, so no bounty accumulates at this time. When the fee switch is enabled, calling rebalance() becomes a paid opportunity. Check feeRate() on the vault -if it returns 0, no bounty will be paid. Currently, rebalancing is handled automatically by Chainlink Automation keepers, but the function is fully permissionless -anyone can call it. ## On-Chain View Functions (Smart Contracts) You can read vault state directly from the Ethereum smart contracts without using the API. ### Vault Contract Functions - totalAssets() -> uint256: Total collateral managed by the vault (in asset decimals) - totalSupply() -> uint256: Total vault shares outstanding - balanceOf(address) -> uint256: Share balance of an address - convertToAssets(uint256 shares) -> uint256: Current asset value of shares - convertToShares(uint256 assets) -> uint256: Shares equivalent of assets - maxDeposit(address) -> uint256: Maximum depositable (0 = paused) - maxWithdraw(address owner) -> uint256: Max assets withdrawable by owner - maxRedeem(address owner) -> uint256: Max shares redeemable by owner - previewDeposit(uint256 assets) -> uint256: Expected shares for deposit - previewRedeem(uint256 shares) -> uint256: Expected assets for redemption - idle() -> bool: True if vault is in idle mode (deposits held, not deployed) - emergencyMode() -> bool: True if vault is in emergency mode (withdrawals only) - depositCap() -> uint256: Maximum total deposits allowed - targetLtv() -> uint256: Target loan-to-value ratio (1e18 precision, divide by 1e16 for %) - feeRate() -> uint256: Performance fee rate (1e18 precision, divide by 1e16 for %) - MIN_DEPOSIT() -> uint256: Minimum deposit amount in asset decimals ### ViewHelper Contract Functions (pass vault address as parameter) - getHealth(address vault) -> int256: Vault health factor - isRebalanceNeeded(address vault) -> bool: Whether LTV is outside deadband - getTotalCollateralValue(address vault) -> uint256: Total value in collateral units - getUserValue(address vault, address user) -> uint256: User's position value in collateral units ### LoanManager Contract Functions - getCurrentLTV() -> uint256: Current loan-to-value (1e18 precision, divide by 1e16 for %) - getCurrentDebt() -> uint256: Outstanding debt in debt asset decimals - getCurrentCollateral() -> uint256: Collateral supplied in asset decimals ## Additional API Endpoints Base URL: https://zensats.app - GET /api/agent/vaults -All vaults with live state and deposit instructions (start here) - GET /api/vault/:address/latest -Latest snapshot for a specific vault - GET /api/vault/:address/history?interval=1h -Historical snapshots - GET /api/vault/:address/metrics?period=7d -Performance metrics (APR, price change) - GET /api/vault/:address/user/:user/profit -User profit/loss and position data - GET /api/vault/:address/holders -Current vault share holders - GET /api/rates -Current protocol borrowing/lending rates Note: API endpoints use the vault contract address (e.g., 0x617A6877...), not the slug. ## Important Notes - All vaults are on Ethereum mainnet only (chainId 1). - Vaults use a leverage strategy. Your deposit is used as collateral to borrow stablecoins. The vault manages the LTV ratio automatically. - Withdrawals may require the vault to unwind positions (repay debt, remove collateral). Large withdrawals relative to vault size may need multiple blocks. - Share tokens are transferable ERC-20 tokens. They can be held, transferred, or used in other DeFi protocols. - The vault charges a performance fee only on yield generated, not on deposits. - If idle() returns true, deposits are accepted but not yet deployed into the strategy. - If emergencyMode() returns true, only withdrawals are allowed -this is irreversible. - Minimum deposit amounts may apply. Check MIN_DEPOSIT() on the vault contract. ## ERC-4626 Compatibility These vaults are fully ERC-4626 compliant. Any ERC-4626 aggregator, router, or SDK (e.g., Yearn Router, ERC4626Router) can integrate with them using the standard interface. No custom integration is needed for basic deposit/withdraw functionality. ## Contract Verification All contracts are verified on Etherscan. You can read the full source code and ABI there: - WBTC Vault: https://etherscan.io/address/0x617A6877f0a55D1eF2B64b5861A2bB5Fe6FEB739#code - wstETH Vault: https://etherscan.io/address/0xbaEc8343B610A5ee7Ca2c5b93507AC7def98E2B1#code - XAUT Vault: https://etherscan.io/address/0x7d5281D590Fb0647aDc7d8494a2c8Fb8C2B23cBD#code