import { r as __exportAll } from "./chunk-olfrzTEO.js";
import { b as generateId } from "./dist-uUUuTqM0.js";
import { m as actions } from "./module-RiCpyFsE.js";
import { t as gql } from "./lib-Cfs4Tyom.js";
import { i as withRetry, n as isValidEthereumAddress, r as validateEnvironment, t as convertToTransactionEntry } from "./alchemyHelpers-CvNseqvu.js";
import { BaseSubgraph } from "@powerhousedao/reactor-api";
//#region subgraphs/acc-txs-addon/schema.ts
const schema = gql`
  """
  Subgraph definition
  """
  type Mutation {
    AccountTransactions_getTransactionsFromAlchemy(
      address: EthereumAddress!
      fromBlock: String
    ): AccountTransactions_AlchemyTransactionsResult
    AccountTransactions_fetchTransactionsFromAlchemy(
      docId: PHID!
      address: EthereumAddress!
      fromBlock: String
    ): AccountTransactions_AlchemyFetchResult
  }

  """
  Alchemy Integration Types
  """
  type AccountTransactions_AlchemyTransactionsResult {
    success: Boolean!
    transactions: [AccountTransactions_TransactionData!]!
    message: String!
    transactionsCount: Int!
  }

  type AccountTransactions_AlchemyFetchResult {
    success: Boolean!
    transactionsAdded: Int!
    message: String!
  }

  type AccountTransactions_TransactionData {
    counterParty: EthereumAddress!
    amount: Amount_Currency!
    txHash: String!
    token: Currency!
    blockNumber: Int!
    uniqueId: String
    datetime: DateTime!
    accountingPeriod: String!
    from: EthereumAddress!
    to: EthereumAddress!
    direction: String!
  }
`;
//#endregion
//#region scripts/alchemy/alchemyTypes.ts
const SUPPORTED_TOKEN_CONTRACTS = {
	USDC: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
	USDT: "0xdac17f958d2ee523a2206206994597c13d831ec7",
	USDS: "0xdc035d45d973e3ec169d2276ddab16f1e407384f",
	SUSDS: "0xa3931d71877c0e7a3148cb7eb4463524fec27fbd",
	DAI: "0x6b175474e89094c44da98b954eedeac495271d0f",
	MKR: "0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2",
	SKY: "0x56072c95faa701256059aa122697b133aded9279",
	SPK: "0xc20059e0317de91738d13af027dfc4a50781b066",
	WETH: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
	EURe: "0x3231cb76718cdef2155fc47b5286d82e6eda273f"
};
const NATIVE_TOKENS = ["ETH"];
[...Object.keys(SUPPORTED_TOKEN_CONTRACTS), ...NATIVE_TOKENS];
//#endregion
//#region scripts/alchemy/alchemyClient.ts
var AlchemyClient = class {
	apiKey;
	network;
	baseURL;
	constructor(apiKey) {
		const env = validateEnvironment();
		this.apiKey = apiKey || env.apiKey;
		this.network = env.network;
		this.baseURL = `https://eth-${this.network}.g.alchemy.com/v2/${this.apiKey}`;
		console.log(`[AlchemyClient] Initialized for network: ${this.network}`);
	}
	/**
	* Fetch asset transfers using Alchemy's getAssetTransfers method
	*/
	async getAssetTransfers(params) {
		return withRetry(async () => {
			const data = {
				jsonrpc: "2.0",
				id: 0,
				method: "alchemy_getAssetTransfers",
				params: [params]
			};
			const response = await fetch(this.baseURL, {
				method: "POST",
				headers: { "Content-Type": "application/json" },
				body: JSON.stringify(data)
			});
			if (!response.ok) throw new Error(`Alchemy API error: ${response.status} - ${response.statusText}`);
			const result = await response.json();
			if ("error" in result) throw new Error(`Alchemy RPC error: ${result.error.message}`);
			return result;
		}, 3, 1e3, "getAssetTransfers");
	}
	/**
	* Get block timestamp using eth_getBlockByNumber
	*/
	async getBlockTimestamp(blockNumber) {
		return withRetry(async () => {
			const data = {
				jsonrpc: "2.0",
				id: 0,
				method: "eth_getBlockByNumber",
				params: [blockNumber, false]
			};
			const response = await fetch(this.baseURL, {
				method: "POST",
				headers: { "Content-Type": "application/json" },
				body: JSON.stringify(data)
			});
			if (!response.ok) throw new Error(`Alchemy API error: ${response.status} - ${response.statusText}`);
			const result = await response.json();
			if ("error" in result) throw new Error(`Alchemy RPC error: ${result.error.message}`);
			const timestampHex = result.result.timestamp;
			return parseInt(timestampHex, 16) * 1e3;
		}, 3, 1e3, `getBlockTimestamp(${blockNumber})`);
	}
	/**
	* Get timestamps for multiple blocks efficiently (batch unique blocks)
	*/
	async getBlockTimestamps(blockNumbers) {
		const uniqueBlocks = [...new Set(blockNumbers)];
		const timestampMap = /* @__PURE__ */ new Map();
		const BATCH_SIZE = 5;
		for (let i = 0; i < uniqueBlocks.length; i += BATCH_SIZE) {
			const promises = uniqueBlocks.slice(i, i + BATCH_SIZE).map(async (blockNum) => {
				try {
					const timestamp = await this.getBlockTimestamp(blockNum);
					timestampMap.set(blockNum, timestamp);
				} catch (error) {
					console.warn(`[AlchemyClient] Failed to fetch timestamp for block ${blockNum}, using current time:`, error.message);
					timestampMap.set(blockNum, Date.now());
				}
			});
			await Promise.all(promises);
		}
		return timestampMap;
	}
	/**
	* Get formatted transactions ready for document model creation
	*/
	async getFormattedTransactions(address, options = {}) {
		if (!isValidEthereumAddress(address)) throw new Error(`Invalid Ethereum address format: ${address}`);
		const { fromBlock = "0x0", includeERC20 = true, includeExternal = true, includeInternal = false, maxCount = 100 } = options;
		const categories = [];
		if (includeExternal) categories.push("external");
		if (includeInternal) categories.push("internal");
		if (includeERC20) categories.push("erc20");
		if (categories.length === 0) throw new Error("At least one transaction category must be included");
		const params = {
			fromBlock,
			fromAddress: address,
			category: categories,
			excludeZeroValue: true,
			maxCount: `0x${maxCount.toString(16)}`,
			...includeERC20 && { contractAddresses: Object.values(SUPPORTED_TOKEN_CONTRACTS) }
		};
		try {
			const transfers = (await this.getAssetTransfers(params)).result.transfers;
			const blockNumbers = transfers.map((t) => t.blockNum);
			const timestampMap = await this.getBlockTimestamps(blockNumbers);
			return transfers.map((transfer) => {
				return convertToTransactionEntry(transfer, address, timestampMap.get(transfer.blockNum));
			});
		} catch (error) {
			console.error(`[AlchemyClient] Failed to fetch transactions for address ${address}:`, error.message);
			throw new Error(`Failed to fetch transactions: ${error.message}`);
		}
	}
	/**
	* Get transactions where the address is the receiver (toAddress)
	*/
	async getTransactionsToAddress(address, options = {}) {
		if (!isValidEthereumAddress(address)) throw new Error(`Invalid Ethereum address format: ${address}`);
		const { fromBlock = "0x0", includeERC20 = true, includeExternal = true, includeInternal = false, maxCount = 100 } = options;
		const categories = [];
		if (includeExternal) categories.push("external");
		if (includeInternal) categories.push("internal");
		if (includeERC20) categories.push("erc20");
		if (categories.length === 0) throw new Error("At least one transaction category must be included");
		const params = {
			fromBlock,
			toAddress: address,
			category: categories,
			excludeZeroValue: true,
			maxCount: `0x${maxCount.toString(16)}`,
			...includeERC20 && { contractAddresses: Object.values(SUPPORTED_TOKEN_CONTRACTS) }
		};
		try {
			const transfers = (await this.getAssetTransfers(params)).result.transfers;
			const blockNumbers = transfers.map((t) => t.blockNum);
			const timestampMap = await this.getBlockTimestamps(blockNumbers);
			return transfers.map((transfer) => {
				return convertToTransactionEntry(transfer, address, timestampMap.get(transfer.blockNum));
			});
		} catch (error) {
			console.error(`[AlchemyClient] Failed to fetch transactions to address ${address}:`, error.message);
			throw new Error(`Failed to fetch transactions: ${error.message}`);
		}
	}
	/**
	* Fetch both outgoing and incoming transactions for an address
	*/
	async getAllTransactionsForAddress(address, options = {}) {
		try {
			const [outgoingTxs, incomingTxs] = await Promise.all([this.getFormattedTransactions(address, options), this.getTransactionsToAddress(address, options)]);
			const allTransactions = [...outgoingTxs, ...incomingTxs];
			const uniqueTransactions = allTransactions.filter((tx, index, self) => index === self.findIndex((t) => t.uniqueId === tx.uniqueId));
			const summary = {
				outgoing: outgoingTxs.length,
				incoming: incomingTxs.length,
				unique: uniqueTransactions.length
			};
			console.log(`[AlchemyClient] Fetched ${summary.outgoing} outgoing + ${summary.incoming} incoming = ${summary.unique} unique transactions (${allTransactions.length - summary.unique} duplicates removed)`);
			return {
				transactions: uniqueTransactions,
				summary
			};
		} catch (error) {
			console.error(`[AlchemyClient] Failed to fetch all transactions for address ${address}:`, error.message);
			throw error;
		}
	}
};
let _alchemyClient = null;
const alchemyClient = { get instance() {
	if (!_alchemyClient) _alchemyClient = new AlchemyClient();
	return _alchemyClient;
} };
//#endregion
//#region subgraphs/acc-txs-addon/resolvers.ts
const getResolvers = (subgraph) => {
	const { reactorClient } = subgraph;
	return { Mutation: {
		AccountTransactions_getTransactionsFromAlchemy: async (_, args) => {
			const { address, fromBlock } = args;
			console.log(`[Resolver] getTransactionsFromAlchemy called for address:`, address);
			try {
				const { transactions } = await alchemyClient.instance.getAllTransactionsForAddress(address, {
					fromBlock: fromBlock || "0x0",
					includeERC20: true,
					includeExternal: true,
					includeInternal: false,
					maxCount: 1e3
				});
				console.log(`[Resolver] Successfully fetched ${transactions.length} transactions`);
				return {
					success: true,
					transactions: transactions.map((tx) => ({
						counterParty: tx.counterParty,
						amount: `${tx.amount.value} ${tx.amount.unit}`,
						txHash: tx.txHash,
						token: tx.token,
						blockNumber: tx.blockNumber,
						uniqueId: tx.uniqueId || null,
						datetime: tx.datetime,
						accountingPeriod: tx.accountingPeriod,
						from: tx.from,
						to: tx.to,
						direction: tx.direction
					})),
					message: `Successfully fetched ${transactions.length} transactions from Alchemy`,
					transactionsCount: transactions.length
				};
			} catch (error) {
				console.error(`[Resolver] Error fetching transactions:`, error);
				return {
					success: false,
					transactions: [],
					message: `Error fetching transactions: ${error instanceof Error ? error.message : "Unknown error"}`,
					transactionsCount: 0
				};
			}
		},
		AccountTransactions_fetchTransactionsFromAlchemy: async (_, args) => {
			const { docId, address, fromBlock } = args;
			console.log(`[Resolver] fetchTransactionsFromAlchemy called:`, {
				docId,
				address,
				fromBlock
			});
			const doc = await reactorClient.get(docId);
			if (!doc) throw new Error(`Document with id ${docId} not found`);
			try {
				const { transactions } = await alchemyClient.instance.getAllTransactionsForAddress(address, {
					fromBlock: fromBlock || "0x0",
					includeERC20: true,
					includeExternal: true,
					includeInternal: false,
					maxCount: 1e3
				});
				if (!transactions || transactions.length === 0) return {
					success: true,
					transactionsAdded: 0,
					message: "No new transactions found. All transactions are up to date."
				};
				const existingTransactions = doc.state.global.transactions || [];
				const existingUniqueIds = new Set(existingTransactions.map((tx) => tx.details?.uniqueId).filter((id) => id != null));
				let successfullyAdded = 0;
				let skippedDuplicates = 0;
				for (const tx of transactions) {
					if (tx.uniqueId && existingUniqueIds.has(tx.uniqueId)) {
						skippedDuplicates++;
						console.log(`[Resolver] Skipping duplicate transaction with uniqueId: ${tx.uniqueId}`);
						continue;
					}
					try {
						await reactorClient.execute(docId, "main", [actions.addTransaction({
							id: generateId(),
							counterParty: tx.counterParty,
							amount: tx.amount,
							datetime: tx.datetime,
							txHash: tx.txHash,
							token: tx.token,
							blockNumber: tx.blockNumber,
							uniqueId: tx.uniqueId || null,
							accountingPeriod: tx.accountingPeriod,
							direction: tx.direction,
							budget: null
						})]);
						successfullyAdded++;
						if (tx.uniqueId) existingUniqueIds.add(tx.uniqueId);
					} catch (addError) {
						console.error(`[Resolver] Failed to add transaction ${tx.txHash}:`, addError instanceof Error ? addError.message : addError);
					}
				}
				console.log(`[Resolver] Summary: ${successfullyAdded} added, ${skippedDuplicates} skipped (duplicates)`);
				if (successfullyAdded === 0) return {
					success: true,
					transactionsAdded: 0,
					message: skippedDuplicates > 0 ? `No new transactions found. All ${skippedDuplicates} transaction(s) already exist in the document.` : "No new transactions found. All transactions are up to date."
				};
				return {
					success: true,
					transactionsAdded: successfullyAdded,
					message: skippedDuplicates > 0 ? `Successfully added ${successfullyAdded} new transaction(s) (${skippedDuplicates} skipped - already exist)` : `Successfully added ${successfullyAdded} new transaction(s)`
				};
			} catch (error) {
				console.error("[Resolver] Error fetching transactions from Alchemy:", error);
				throw new Error(`Failed to fetch transactions: ${error instanceof Error ? error.message : "Unknown error"}`);
			}
		}
	} };
};
//#endregion
//#region subgraphs/acc-txs-addon/index.ts
var acc_txs_addon_exports = /* @__PURE__ */ __exportAll({ AccTxsAddonSubgraph: () => AccTxsAddonSubgraph });
var AccTxsAddonSubgraph = class extends BaseSubgraph {
	name = "acc-txs-addon";
	typeDefs = schema;
	resolvers = getResolvers(this);
	additionalContextFields = {};
	async onSetup() {}
	async onDisconnect() {}
};
//#endregion
export { acc_txs_addon_exports as n, AccTxsAddonSubgraph as t };

//# sourceMappingURL=acc-txs-addon-D-kLSuqR.js.map