import { guardedFetch } from "./endpoint-allowlist.mjs";
import { M as formatUnits } from "../../chain-dTUyiOxD.mjs";
import { TOKENS, YEARN } from "../lib/contract-registry.mjs";
//#region extensions/crypto/src/services/yield-service.ts
/**
* Yield Aggregation Service — find and execute yield opportunities across DeFi.
*
* Data source: DeFiLlama yields API (yields.llama.fi/pools).
* Execution: Yearn V3 vaults on Base (direct contract calls, no SDK).
*
* Supported protocols for deposits:
*   - Yearn V3 vaults on Base (ERC-4626 standard)
*   - Aave V3 on Base (via defi_lend tool — this service only searches)
*
* No new dependencies. Uses guardedFetch for HTTP, viem for contract calls.
*/
const YEARN_VAULTS_BASE = {
	"yvUSDC": {
		address: YEARN.base.yvUSDC,
		asset: "USDC",
		decimals: 6
	},
	"yvWETH": {
		address: YEARN.base.yvWETH,
		asset: "WETH",
		decimals: 18
	},
	"yvDAI": {
		address: YEARN.base.yvDAI,
		asset: "DAI",
		decimals: 18
	}
};
const BASE_TOKENS = {
	USDC: TOKENS.base.USDC,
	WETH: TOKENS.base.WETH,
	DAI: TOKENS.base.DAI
};
const ERC4626_ABI = [
	{
		name: "deposit",
		type: "function",
		stateMutability: "nonpayable",
		inputs: [{
			name: "assets",
			type: "uint256"
		}, {
			name: "receiver",
			type: "address"
		}],
		outputs: [{
			name: "shares",
			type: "uint256"
		}]
	},
	{
		name: "withdraw",
		type: "function",
		stateMutability: "nonpayable",
		inputs: [
			{
				name: "assets",
				type: "uint256"
			},
			{
				name: "receiver",
				type: "address"
			},
			{
				name: "owner",
				type: "address"
			}
		],
		outputs: [{
			name: "shares",
			type: "uint256"
		}]
	},
	{
		name: "redeem",
		type: "function",
		stateMutability: "nonpayable",
		inputs: [
			{
				name: "shares",
				type: "uint256"
			},
			{
				name: "receiver",
				type: "address"
			},
			{
				name: "owner",
				type: "address"
			}
		],
		outputs: [{
			name: "assets",
			type: "uint256"
		}]
	},
	{
		name: "balanceOf",
		type: "function",
		stateMutability: "view",
		inputs: [{
			name: "owner",
			type: "address"
		}],
		outputs: [{
			name: "",
			type: "uint256"
		}]
	},
	{
		name: "convertToAssets",
		type: "function",
		stateMutability: "view",
		inputs: [{
			name: "shares",
			type: "uint256"
		}],
		outputs: [{
			name: "",
			type: "uint256"
		}]
	},
	{
		name: "convertToShares",
		type: "function",
		stateMutability: "view",
		inputs: [{
			name: "assets",
			type: "uint256"
		}],
		outputs: [{
			name: "",
			type: "uint256"
		}]
	},
	{
		name: "maxDeposit",
		type: "function",
		stateMutability: "view",
		inputs: [{
			name: "receiver",
			type: "address"
		}],
		outputs: [{
			name: "",
			type: "uint256"
		}]
	},
	{
		name: "totalAssets",
		type: "function",
		stateMutability: "view",
		inputs: [],
		outputs: [{
			name: "",
			type: "uint256"
		}]
	},
	{
		name: "asset",
		type: "function",
		stateMutability: "view",
		inputs: [],
		outputs: [{
			name: "",
			type: "address"
		}]
	}
];
const ERC20_APPROVE_ABI = [{
	name: "approve",
	type: "function",
	stateMutability: "nonpayable",
	inputs: [{
		name: "spender",
		type: "address"
	}, {
		name: "amount",
		type: "uint256"
	}],
	outputs: [{
		name: "",
		type: "bool"
	}]
}, {
	name: "allowance",
	type: "function",
	stateMutability: "view",
	inputs: [{
		name: "owner",
		type: "address"
	}, {
		name: "spender",
		type: "address"
	}],
	outputs: [{
		name: "",
		type: "uint256"
	}]
}];
function normalizeChain(input) {
	return {
		base: "Base",
		ethereum: "Ethereum",
		eth: "Ethereum",
		mainnet: "Ethereum",
		arbitrum: "Arbitrum",
		arb: "Arbitrum",
		optimism: "Optimism",
		op: "Optimism",
		polygon: "Polygon",
		matic: "Polygon"
	}[input.toLowerCase()] ?? input;
}
var YieldService = class {
	poolsCache = [];
	poolsCacheTimestamp = 0;
	POOLS_CACHE_TTL = 3e5;
	/**
	* Search DeFiLlama yield pools with optional filters.
	* Returns top results sorted by APY descending.
	*/
	async searchPools(params = {}) {
		let filtered = await this.fetchPools();
		if (params.chain) {
			const normalizedChain = normalizeChain(params.chain);
			filtered = filtered.filter((p) => p.chain === normalizedChain);
		}
		if (params.asset) {
			const upper = params.asset.toUpperCase();
			filtered = filtered.filter((p) => p.symbol.toUpperCase().includes(upper));
		}
		if (params.project) {
			const lower = params.project.toLowerCase();
			filtered = filtered.filter((p) => p.project.toLowerCase().includes(lower));
		}
		const minTvl = params.minTvl ?? 1e5;
		filtered = filtered.filter((p) => p.tvlUsd >= minTvl);
		if (params.minApy !== void 0) filtered = filtered.filter((p) => p.apy >= params.minApy);
		if (params.stableOnly) filtered = filtered.filter((p) => p.stablecoin);
		if (params.singleExposure) filtered = filtered.filter((p) => p.exposure === "single" || p.ilRisk === "no");
		filtered.sort((a, b) => b.apy - a.apy);
		const limit = params.limit ?? 20;
		return filtered.slice(0, limit);
	}
	/**
	* Get top yields for a specific asset on a specific chain.
	*/
	async topYieldsForAsset(asset, chain = "Base", limit = 10) {
		return this.searchPools({
			chain,
			asset,
			limit,
			minTvl: 5e4
		});
	}
	/**
	* Resolve a vault name or address to a known Yearn V3 vault.
	*/
	resolveVault(input) {
		const upper = input.toUpperCase();
		for (const [name, vault] of Object.entries(YEARN_VAULTS_BASE)) if (name.toUpperCase() === upper || vault.asset.toUpperCase() === upper) return {
			name,
			...vault
		};
		const lower = input.toLowerCase();
		for (const [name, vault] of Object.entries(YEARN_VAULTS_BASE)) if (vault.address.toLowerCase() === lower) return {
			name,
			...vault
		};
		return null;
	}
	/**
	* Get all known Yearn V3 vaults.
	*/
	getAvailableVaults() {
		return Object.entries(YEARN_VAULTS_BASE).map(([name, v]) => ({
			name,
			address: v.address,
			asset: v.asset,
			decimals: v.decimals
		}));
	}
	/**
	* Deposit into a Yearn V3 vault (ERC-4626).
	*/
	async deposit(vaultAddress, assetAddress, amount, userAddress, walletClient, publicClient) {
		await this.ensureApproval(assetAddress, vaultAddress, amount, userAddress, walletClient, publicClient);
		const hash = await walletClient.writeContract({
			address: vaultAddress,
			abi: ERC4626_ABI,
			functionName: "deposit",
			args: [amount, userAddress]
		});
		await publicClient.waitForTransactionReceipt({ hash });
		let shares = "0";
		try {
			shares = (await publicClient.readContract({
				address: vaultAddress,
				abi: ERC4626_ABI,
				functionName: "balanceOf",
				args: [userAddress]
			})).toString();
		} catch {}
		return {
			hash,
			shares
		};
	}
	/**
	* Withdraw from a Yearn V3 vault (ERC-4626).
	* If isMax, redeems all shares. Otherwise withdraws specific asset amount.
	*/
	async withdraw(vaultAddress, amount, userAddress, walletClient, publicClient, isMax, decimals) {
		let hash;
		if (isMax) {
			const sharesBalance = await publicClient.readContract({
				address: vaultAddress,
				abi: ERC4626_ABI,
				functionName: "balanceOf",
				args: [userAddress]
			});
			if (sharesBalance === 0n) throw new Error("No shares to redeem in this vault.");
			hash = await walletClient.writeContract({
				address: vaultAddress,
				abi: ERC4626_ABI,
				functionName: "redeem",
				args: [
					sharesBalance,
					userAddress,
					userAddress
				]
			});
		} else hash = await walletClient.writeContract({
			address: vaultAddress,
			abi: ERC4626_ABI,
			functionName: "withdraw",
			args: [
				amount,
				userAddress,
				userAddress
			]
		});
		await publicClient.waitForTransactionReceipt({ hash });
		return {
			hash,
			assetsReturned: isMax ? "all" : formatUnits(amount, decimals)
		};
	}
	/**
	* Get vault positions for a user across all known vaults.
	*/
	async getPositions(userAddress, publicClient) {
		const positions = [];
		for (const [name, vault] of Object.entries(YEARN_VAULTS_BASE)) try {
			const sharesBalance = await publicClient.readContract({
				address: vault.address,
				abi: ERC4626_ABI,
				functionName: "balanceOf",
				args: [userAddress]
			});
			if (sharesBalance === 0n) continue;
			let assetsValue;
			try {
				assetsValue = await publicClient.readContract({
					address: vault.address,
					abi: ERC4626_ABI,
					functionName: "convertToAssets",
					args: [sharesBalance]
				});
			} catch {
				assetsValue = sharesBalance;
			}
			positions.push({
				vault: name,
				vaultAddress: vault.address,
				asset: vault.asset,
				shares: formatUnits(sharesBalance, vault.decimals),
				assetsValue: formatUnits(assetsValue, vault.decimals),
				chain: "base"
			});
		} catch {}
		await this.enrichPositionsWithApy(positions);
		return positions;
	}
	/**
	* Get the underlying token address for a vault's asset.
	*/
	getTokenAddress(asset) {
		return BASE_TOKENS[asset.toUpperCase()] ?? null;
	}
	async fetchPools() {
		if (this.poolsCache.length > 0 && Date.now() - this.poolsCacheTimestamp < this.POOLS_CACHE_TTL) return this.poolsCache;
		try {
			const response = await guardedFetch("https://yields.llama.fi/pools", {
				headers: { Accept: "application/json" },
				signal: AbortSignal.timeout(15e3)
			});
			if (!response.ok) return this.poolsCache;
			this.poolsCache = ((await response.json()).data ?? []).map((p) => ({
				pool: p.pool ?? "",
				chain: p.chain ?? "",
				project: p.project ?? "",
				symbol: p.symbol ?? "",
				tvlUsd: p.tvlUsd ?? 0,
				apyBase: p.apyBase ?? null,
				apyReward: p.apyReward ?? null,
				apy: p.apy ?? p.apyBase ?? 0,
				rewardTokens: p.rewardTokens ?? null,
				underlyingTokens: p.underlyingTokens ?? null,
				ilRisk: p.ilRisk ?? null,
				stablecoin: p.stablecoin ?? false,
				exposure: p.exposure ?? null,
				poolMeta: p.poolMeta ?? null
			}));
			this.poolsCacheTimestamp = Date.now();
			return this.poolsCache;
		} catch {
			return this.poolsCache;
		}
	}
	async enrichPositionsWithApy(positions) {
		if (positions.length === 0) return;
		const pools = await this.fetchPools();
		for (const pos of positions) {
			const match = pools.find((p) => p.chain === "Base" && p.project.includes("yearn") && p.symbol.toUpperCase().includes(pos.asset.toUpperCase()));
			if (match) pos.apy = match.apy;
		}
	}
	async ensureApproval(tokenAddress, spender, amount, owner, walletClient, publicClient) {
		if (await publicClient.readContract({
			address: tokenAddress,
			abi: ERC20_APPROVE_ABI,
			functionName: "allowance",
			args: [owner, spender]
		}) < amount) {
			const approveHash = await walletClient.writeContract({
				address: tokenAddress,
				abi: ERC20_APPROVE_ABI,
				functionName: "approve",
				args: [spender, amount]
			});
			await publicClient.waitForTransactionReceipt({ hash: approveHash });
		}
	}
};
let _instance = null;
function getYieldService() {
	if (!_instance) _instance = new YieldService();
	return _instance;
}
function resetYieldService() {
	_instance = null;
}
//#endregion
export { ERC4626_ABI, YEARN_VAULTS_BASE, YieldService, getYieldService, resetYieldService };

//# sourceMappingURL=yield-service.mjs.map