From b41c1285347ab120526977b1b3e30816b0514fb0 Mon Sep 17 00:00:00 2001 From: tony Date: Mon, 16 Mar 2026 13:28:13 +0700 Subject: [PATCH 01/10] update cardanoscan --- apps/long-short-backend/src/provider/cardanoscan.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/long-short-backend/src/provider/cardanoscan.ts b/apps/long-short-backend/src/provider/cardanoscan.ts index 20cb535..95330be 100644 --- a/apps/long-short-backend/src/provider/cardanoscan.ts +++ b/apps/long-short-backend/src/provider/cardanoscan.ts @@ -90,8 +90,8 @@ export type GetTransactionListOptions = { export class CardanoscanProvider { public static readonly MAINNET_URL = "https://api.cardanoscan.io/api/v1"; public static readonly PREVIEW_URL = "https://api-preview.cardanoscan.io/api/v1"; - public static readonly PAGE_SIZE = 1; - public static readonly MAX_PAGE = 2; + public static readonly PAGE_SIZE = 3; + public static readonly MAX_PAGE = 3; private readonly baseUrl: string; private readonly apiKey: string; From 2879c4386543a2e07f5ce9b47c04d0d7afb68e65 Mon Sep 17 00:00:00 2001 From: tony Date: Mon, 23 Mar 2026 17:06:21 +0700 Subject: [PATCH 02/10] wwip --- .../src/api/state-machine.ts | 472 +++++++++++++++++- .../src/services/position-service.ts | 29 +- 2 files changed, 462 insertions(+), 39 deletions(-) diff --git a/apps/long-short-backend/src/api/state-machine.ts b/apps/long-short-backend/src/api/state-machine.ts index 864b25f..7ba13ce 100644 --- a/apps/long-short-backend/src/api/state-machine.ts +++ b/apps/long-short-backend/src/api/state-machine.ts @@ -41,6 +41,9 @@ export namespace StateMachine { SHORT_SELL = "SHORT_SELL", SHORT_BUY = "SHORT_BUY", SHORT_REPAY = "SHORT_REPAY", + SHORT_REPAY_FRACTION = "SHORT_REPAY_FRACTION", + SHORT_WITHDRAW_FRACTION = "SHORT_WITHDRAW_FRACTION", + SHORT_BUY_FREED = "SHORT_BUY_FREED", SHORT_WITHDRAW = "SHORT_WITHDRAW", } @@ -991,28 +994,64 @@ export namespace StateMachine { }; }; + const checkShortRepayFunds = async ( + networkEnv: NetworkEnvironment, + userAddress: string, + marketConfig: MarketConfig, + availableAssetB: number, + ): Promise<{ canFullRepay: boolean; loan: LiqwidProviderV2.Loan; loanAmountRaw: bigint }> => { + const apiConfig = LiqwidProviderV2.createConfig(networkEnv); + const userAddr = Address.fromBech32(userAddress); + const pkh = userAddr.toPubKeyHash()?.keyHash.hex; + invariant(pkh, "Failed to extract public key hash from user address"); + const loansResult = await LiqwidProviderV2.Data.loansForUser(apiConfig, [pkh]); + if (loansResult.type === "err") { + throw new Error(`Failed to fetch loans before short repay: ${loansResult.error.message}`); + } + const loan = loansResult.value.find((l) => l.marketId === marketConfig.borrowMarketIdShort); + invariant(loan, `No active loan found for market ${marketConfig.borrowMarketIdShort}`); + const loanAmountRaw = BigInt(Math.round(loan.amount * 1_000_000)); + return { canFullRepay: BigInt(availableAssetB) >= loanAmountRaw, loan, loanAmountRaw }; + }; + /** - * Build SHORT_REPAY transaction: Repay asset B loan to Liqwid and redeem qADA collateral + * Build SHORT_REPAY transaction: Full repay — pay off entire loan and redeem all collateral. + * Fetches loan from Liqwid API to get current UTXO and collateral info. */ export const handleShortRepay = async (options: HandleBuildTxOptions): Promise => { - const { order, marketConfig, userAddress, networkEnv, utxos, loanTxId, loanOutputIndex, collateralAmount } = - options; + const { order, marketConfig, userAddress, networkEnv, utxos } = options; invariant(order.orderType === ShortOrderType.SHORT_REPAY, "Invalid order type for handleShortRepay"); invariant(order.assetIn, "assetIn is required for SHORT_REPAY order"); invariant(order.amountIn, "amountIn is required for SHORT_REPAY order"); - invariant(loanTxId, "loanTxId is required for SHORT_REPAY order"); - invariant(loanOutputIndex !== undefined, "loanOutputIndex is required for SHORT_REPAY order"); - invariant(collateralAmount, "collateralAmount is required for SHORT_REPAY order"); const apiConfig = LiqwidProviderV2.createConfig(networkEnv); - // Format loanUtxoId as "{txHash}-{outputIndex}" - const loanUtxoId = `${loanTxId}-${loanOutputIndex}`; + // Fetch current loan from API (always up-to-date, even after modifyBorrow) + const userAddr = Address.fromBech32(userAddress); + const pkh = userAddr.toPubKeyHash()?.keyHash.hex; + invariant(pkh, "Failed to extract public key hash from user address"); + const loansResult = await LiqwidProviderV2.Data.loansForUser(apiConfig, [pkh]); + if (loansResult.type === "err") { + throw new Error(`Failed to fetch loans for full repay: ${loansResult.error.message}`); + } + const loan = loansResult.value.find((l) => l.marketId === marketConfig.borrowMarketIdShort); + invariant(loan, `No active loan found for market ${marketConfig.borrowMarketIdShort}`); - // Format collateral ID: use assetAQTokenRaw (qADA) for SHORT - const qTokenParts = marketConfig.assetAQTokenRaw.split("."); - const qTokenPolicyId = qTokenParts[0]; - const collateralId = `${marketConfig.borrowMarketIdShort}.${qTokenPolicyId}`; + const loanUtxoId = `${loan.transactionId}-${loan.transactionIndex}`; + + const collateral = loan.collaterals[0]; + invariant(collateral, "Loan has no collateral"); + invariant(collateral.market, "Loan collateral has no market info"); + const qTokenAmountRaw = Math.round(collateral.qTokenAmount * 1_000_000); + const collateralId = `${collateral.market.id}.${collateral.id}`; + + logger.info("handleShortRepay: full repay", { + loanId: loan.id, + loanAmount: loan.amount, + qTokenAmountRaw, + loanUtxoId, + collateralId, + }); const buildTxResult = await LiqwidProviderV2.Transactions.repayLoan(apiConfig, { address: userAddress, @@ -1021,7 +1060,7 @@ export namespace StateMachine { collaterals: [ { id: collateralId, - amount: Number(collateralAmount), + amount: qTokenAmountRaw, }, ], }); @@ -1048,6 +1087,365 @@ export namespace StateMachine { }; }; + // ============================================================================ + // FRACTIONAL SHORT REPAY FLOW + // ============================================================================ + // + // Triggered when SHORT_BUY output (asset B received) < current loan amount. + // + // Full cycle until position is fully closed: + // + // SHORT_BUY + // │ (asset B received < loan) + // ▼ + // SHORT_REPAY_FRACTION ──── modifyBorrow(newDebt, keepCollateral) + // │ + // ▼ + // SHORT_WITHDRAW_FRACTION ── modifyBorrow(sameDebt, reducedCollateral, redeemCollateral=true) → ADA + // │ + // ▼ + // SHORT_BUY_FREED ────────── buy asset B with freed ADA (A_TO_B swap) + // │ + // ├── asset B received < remaining loan? ──► loop back to SHORT_REPAY_FRACTION + // │ + // └── asset B received >= remaining loan? ──► SHORT_REPAY (full repay) + // │ + // SHORT_WITHDRAW + // ============================================================================ + + /** + * Build SHORT_REPAY_FRACTION: Partial debt repay via modifyBorrow. + * Self-contained — fetches current loan from Liqwid API. + * Reduces debt by available asset B while keeping full collateral (qADA) unchanged. + * Step 2 (SHORT_WITHDRAW_FRACTION) reduces collateral proportionally in a separate tx. + */ + export const handleShortRepayFraction = async (options: HandleBuildTxOptions): Promise => { + const { order, marketConfig, userAddress, networkEnv, utxos } = options; + invariant( + order.orderType === ShortOrderType.SHORT_REPAY_FRACTION, + "Invalid order type for handleShortRepayFraction", + ); + invariant(order.assetIn, "assetIn is required for SHORT_REPAY_FRACTION order"); + invariant(order.amountIn, "amountIn (asset B available to repay) is required for SHORT_REPAY_FRACTION order"); + + const apiConfig = LiqwidProviderV2.createConfig(networkEnv); + + // Fetch current loan from API (always up-to-date, even after prior modifyBorrow calls) + const userAddr = Address.fromBech32(userAddress); + const pkh = userAddr.toPubKeyHash()?.keyHash.hex; + invariant(pkh, "Failed to extract public key hash from user address"); + const loansResult = await LiqwidProviderV2.Data.loansForUser(apiConfig, [pkh]); + if (loansResult.type === "err") { + throw new Error(`Failed to fetch loans for short partial repay: ${loansResult.error.message}`); + } + const loan = loansResult.value.find((l) => l.marketId === marketConfig.borrowMarketIdShort); + invariant(loan, `No active loan found for market ${marketConfig.borrowMarketIdShort}`); + + const loanUtxoId = `${loan.transactionId}-${loan.transactionIndex}`; + + // Fetch market to get minimum borrow amount constraint. + const marketResult = await LiqwidProviderV2.Data.market( + apiConfig, + marketConfig.borrowMarketIdShort as LiqwidProviderV2.MarketId, + ); + if (marketResult.type === "err") { + throw new Error(`Failed to fetch market for short partial repay: ${marketResult.error.message}`); + } + invariant(marketResult.value, `Market ${marketConfig.borrowMarketIdShort} not found`); + const minValueRaw = Math.round(marketResult.value.parameters.minValue * 1_000_000); + + // loan.amount is human-readable; convert to raw units + const currentDebtRaw = Math.round(loan.amount * 1_000_000); + const availableRaw = Number(order.amountIn); // raw units of asset B + const collateral = loan.collaterals[0]; + invariant(collateral, "Loan has no collateral — cannot compute partial repay"); + const qTokenAmountRaw = Math.round(collateral.qTokenAmount * 1_000_000); + + // Remaining debt must stay >= minValue so the position stays protocol-legal. + const newDebt = Math.max(currentDebtRaw - availableRaw, minValueRaw); + + invariant(collateral.market, "Loan collateral has no market info"); + const collateralId = `${collateral.market.id}.${collateral.id}`; + + logger.info("handleShortRepayFraction", { + currentDebtRaw, + availableRaw, + minValueRaw, + qTokenAmountRaw, + newDebt, + loanId: loan.id, + collateralId, + }); + + // Step 1 of fractional close: reduce debt only, keep FULL collateral unchanged. + const buildTxResult = await LiqwidProviderV2.Transactions.repayLoanFraction(apiConfig, { + address: userAddress, + utxos, + loanUtxoId, + amount: newDebt, + collaterals: [{ id: collateralId, amount: qTokenAmountRaw }], + }); + + if (buildTxResult.type === "err") { + throw new Error(`Failed to build short repay-fraction transaction: ${buildTxResult.error.message}`); + } + + const txRaw = buildTxResult.value; + const ECSL = RustModule.getE; + const eTx = ECSL.Transaction.from_hex(txRaw); + const txBody = eTx.body(); + const ttl = txBody.ttl(); + invariant(Maybe.isJust(ttl), "TTL must be set in the transaction body"); + safeFreeRustObjects(eTx, txBody); + + const validTo = getTimeFromSlotMagic(networkEnv, ttl); + const txId = LiqwidProviderV2.getTxHash(txRaw); + + return { txRaw, txId, validTo: validTo.getTime() }; + }; + + /** + * Wait for SHORT_REPAY_FRACTION tx confirmation. + * + * SHORT_REPAY_FRACTION is a Liqwid modifyBorrow tx that reduces debt while keeping + * full collateral. Asset B is consumed as repayment — no tokens returned to user. + * + * On confirmation → transition to SHORT_WITHDRAW_FRACTION: + * amountIn = originalDebtRaw (for proportional collateral calc in handleShortWithdrawFraction) + */ + export const waitingShortRepayFraction = async (options: WaitingOptions): Promise => { + const { marketConfig, txHash, userAddress, cardanoscanProvider, originalDebtLovelace } = options; + invariant(originalDebtLovelace, "originalDebtLovelace is required for waitingShortRepayFraction"); + + const txFoundOnChain = await cardanoscanProvider.findTransactionByHash( + userAddress, + txHash, + CardanoscanProvider.PAGE_SIZE, + CardanoscanProvider.MAX_PAGE, + ); + + if (txFoundOnChain) { + return { + isConfirmed: true, + nextOrderType: ShortOrderType.SHORT_WITHDRAW_FRACTION, + assetIn: marketConfig.assetA.toString(), // placeholder (handler fetches loan from API) + amountIn: originalDebtLovelace, // original debt for proportional collateral calc + assetOut: marketConfig.assetA.toString(), // ADA will be received after withdraw + }; + } + + return { isConfirmed: false }; + }; + + /** + * Build SHORT_WITHDRAW_FRACTION: Reduce collateral via modifyBorrow after a partial repay. + * Keeps the same debt level, reduces qADA collateral proportionally, and redeems freed qADA + * to ADA (sent to wallet for buying more asset B in SHORT_BUY_FREED). + */ + export const handleShortWithdrawFraction = async (options: HandleBuildTxOptions): Promise => { + const { order, marketConfig, userAddress, networkEnv, utxos } = options; + invariant( + order.orderType === ShortOrderType.SHORT_WITHDRAW_FRACTION, + "Invalid order type for handleShortWithdrawFraction", + ); + + const apiConfig = LiqwidProviderV2.createConfig(networkEnv); + + // Fetch current loan state (after SHORT_REPAY_FRACTION: debt reduced, collateral unchanged) + const userAddr = Address.fromBech32(userAddress); + const pkh = userAddr.toPubKeyHash()?.keyHash.hex; + invariant(pkh, "Failed to extract public key hash from user address"); + const loansResult = await LiqwidProviderV2.Data.loansForUser(apiConfig, [pkh]); + if (loansResult.type === "err") { + throw new Error(`Failed to fetch loans for short withdraw fraction: ${loansResult.error.message}`); + } + const loan = loansResult.value.find((l) => l.marketId === marketConfig.borrowMarketIdShort); + invariant(loan, `No active loan found for market ${marketConfig.borrowMarketIdShort}`); + + // Use loan UTXO info from the API (always up-to-date after modifyBorrow calls) + const loanUtxoId = `${loan.transactionId}-${loan.transactionIndex}`; + + const currentDebtRaw = Math.round(loan.amount * 1_000_000); + const collateral = loan.collaterals[0]; + invariant(collateral, "Loan has no collateral"); + const qTokenAmountRaw = Math.round(collateral.qTokenAmount * 1_000_000); + + // order.amountIn stores the original debt (before SHORT_REPAY_FRACTION) for proportional calc. + const originalDebtRaw = order.amountIn ? Number(order.amountIn) : currentDebtRaw; + const proportionalCollateral = Math.floor(qTokenAmountRaw * (currentDebtRaw / originalDebtRaw)); + + invariant(collateral.market, "Loan collateral has no market info"); + const collateralId = `${collateral.market.id}.${collateral.id}`; + + logger.info("handleShortWithdrawFraction", { + currentDebtRaw, + originalDebtRaw, + qTokenAmountRaw, + proportionalCollateral, + freedCollateral: qTokenAmountRaw - proportionalCollateral, + loanId: loan.id, + collateralId, + }); + + // Step 2: keep same debt, reduce collateral proportionally, redeem freed qADA to ADA. + const buildTxResult = await LiqwidProviderV2.Transactions.repayLoanFraction(apiConfig, { + address: userAddress, + utxos, + loanUtxoId, + amount: currentDebtRaw, + collaterals: [{ id: collateralId, amount: proportionalCollateral }], + redeemCollateral: true, + }); + + if (buildTxResult.type === "err") { + throw new Error(`Failed to build short withdraw-fraction transaction: ${buildTxResult.error.message}`); + } + + const txRaw = buildTxResult.value; + const ECSL = RustModule.getE; + const eTx = ECSL.Transaction.from_hex(txRaw); + const txBody = eTx.body(); + const ttl = txBody.ttl(); + invariant(Maybe.isJust(ttl), "TTL must be set in the transaction body"); + safeFreeRustObjects(eTx, txBody); + + const validTo = getTimeFromSlotMagic(networkEnv, ttl); + const txId = LiqwidProviderV2.getTxHash(txRaw); + + return { txRaw, txId, validTo: validTo.getTime() }; + }; + + /** + * Wait for SHORT_WITHDRAW_FRACTION tx confirmation. + * Finds ADA received (freed qADA redeemed to underlying) and transitions to SHORT_BUY_FREED. + */ + export const waitingShortWithdrawFraction = async (options: WaitingOptions): Promise => { + const { marketConfig, txHash, userAddress, cardanoscanProvider } = options; + + const txFoundOnChain = await cardanoscanProvider.findTransactionByHash( + userAddress, + txHash, + CardanoscanProvider.PAGE_SIZE, + CardanoscanProvider.MAX_PAGE, + ); + + if (txFoundOnChain) { + const userAddressHex = userAddress.toHex(); + + for (const output of txFoundOnChain.outputs) { + if (output.address === userAddressHex) { + // For SHORT, freed collateral is qADA → redeemed to ADA (native lovelace in output.value) + const amountOut = BigInt(output.value); + return { + isConfirmed: true, + nextOrderType: ShortOrderType.SHORT_BUY_FREED, + assetIn: marketConfig.assetA.toString(), // ADA + amountIn: amountOut.toString(), + assetOut: marketConfig.assetB.toString(), // asset B to buy + amountOut: amountOut.toString(), + }; + } + } + + throw new Error(`SHORT_WITHDRAW_FRACTION tx confirmed (${txHash}) but could not find ADA output for user`); + } + + return { isConfirmed: false }; + }; + + /** + * Build SHORT_BUY_FREED: Buy asset B with freed ADA. Same DEX swap as handleShortBuy. + */ + export const handleShortBuyFreed = async (options: HandleBuildTxOptions): Promise => { + invariant(options.order.orderType === ShortOrderType.SHORT_BUY_FREED, "Invalid order type for handleShortBuyFreed"); + return handleShortBuy({ ...options, order: { ...options.order, orderType: ShortOrderType.SHORT_BUY } }); + }; + + /** + * Wait for SHORT_BUY_FREED order output to be spent (DEX consumed). + * + * On confirmation, checks if enough asset B to fully repay the remaining loan. + * If yes → SHORT_REPAY (full repay). If no → loop back to SHORT_REPAY_FRACTION. + */ + export const waitingShortBuyFreed = async (options: WaitingOptions): Promise => { + const { marketConfig, txHash, orderOutputIndex, userAddress, cardanoscanProvider, assetOut, networkEnv } = options; + invariant(orderOutputIndex !== undefined, "orderOutputIndex is required for waitingShortBuyFreed"); + invariant(assetOut, "assetOut is required for waitingShortBuyFreed"); + + const userAddressHex = userAddress.toHex(); + + const spendingTx = await cardanoscanProvider.findTransactionHasSpent( + userAddress, + txHash, + orderOutputIndex, + CardanoscanProvider.PAGE_SIZE, + CardanoscanProvider.MAX_PAGE, + ); + + if (spendingTx) { + const assetOutUnit = assetOut.toBlockFrostString(); + + for (const output of spendingTx.outputs) { + if (output.address === userAddressHex) { + if (output.tokens && output.tokens.length > 0) { + const matchingToken = output.tokens.find((token) => token.assetId === assetOutUnit); + if (matchingToken) { + const amountOut = BigInt(matchingToken.value); + + // Check if we now have enough asset B to fully repay the remaining loan. + const { canFullRepay, loanAmountRaw } = await checkShortRepayFunds( + networkEnv, + userAddress.bech32, + marketConfig, + Number(amountOut), + ); + + logger.info("waitingShortBuyFreed: canFullRepay check", { + available: amountOut.toString(), + loanAmountRaw: loanAmountRaw.toString(), + canFullRepay, + }); + + if (canFullRepay) { + // Enough asset B → transition to SHORT_REPAY (full repay) + return { + isConfirmed: true, + nextOrderType: ShortOrderType.SHORT_REPAY, + assetIn: marketConfig.assetB.toString(), + amountIn: amountOut.toString(), + assetOut: marketConfig.assetAQTokenRaw, + amountOut: amountOut.toString(), + }; + } + + // Still not enough → loop: create another fractional cycle + return { + isConfirmed: true, + nextOrderType: ShortOrderType.SHORT_REPAY_FRACTION, + assetIn: marketConfig.assetB.toString(), + amountIn: amountOut.toString(), + assetOut: marketConfig.assetB.toString(), + amountOut: amountOut.toString(), + additionalOrders: [ + { orderType: ShortOrderType.SHORT_REPAY_FRACTION }, + { orderType: ShortOrderType.SHORT_WITHDRAW_FRACTION }, + { orderType: ShortOrderType.SHORT_BUY_FREED }, + ], + originalDebtLovelace: loanAmountRaw.toString(), + }; + } + } + } + } + + throw new Error( + `SHORT_BUY_FREED output spent (tx: ${spendingTx.hash}) but could not find matching output with asset ${assetOut.toString()}`, + ); + } + + return { isConfirmed: false }; + }; + /** * Build SHORT_WITHDRAW transaction: Withdraw asset A (ADA) from Liqwid */ @@ -1630,10 +2028,10 @@ export namespace StateMachine { /** * Wait for SHORT_BUY order output to be spent (consumed by DEX) - * Extract asset B received and transition to SHORT_REPAY + * Extract asset B received and check if enough for full repay or fractional flow */ export const waitingShortBuy = async (options: WaitingOptions): Promise => { - const { marketConfig, txHash, orderOutputIndex, userAddress, cardanoscanProvider, assetOut } = options; + const { marketConfig, txHash, orderOutputIndex, userAddress, cardanoscanProvider, assetOut, networkEnv } = options; invariant(orderOutputIndex !== undefined, "orderOutputIndex is required for waitingShortBuy"); invariant(assetOut, "assetOut is required for waitingShortBuy"); @@ -1656,13 +2054,47 @@ export namespace StateMachine { const matchingToken = output.tokens.find((token) => token.assetId === assetOutUnit); if (matchingToken) { const amountOut = BigInt(matchingToken.value); + + // Check if we have enough asset B to fully repay the loan + const { canFullRepay, loanAmountRaw } = await checkShortRepayFunds( + networkEnv, + userAddress.bech32, + marketConfig, + Number(amountOut), + ); + + logger.info("waitingShortBuy: canFullRepay check", { + available: amountOut.toString(), + loanAmountRaw: loanAmountRaw.toString(), + canFullRepay, + }); + + if (canFullRepay) { + // Happy path: enough asset B → full repay + return { + isConfirmed: true, + nextOrderType: ShortOrderType.SHORT_REPAY, + assetIn: assetOut.toString(), + amountIn: amountOut.toString(), + assetOut: marketConfig.assetAQTokenRaw, // qADA to be redeemed + amountOut: amountOut.toString(), + }; + } + + // Insufficient asset B → start fractional repay cycle return { isConfirmed: true, - nextOrderType: ShortOrderType.SHORT_REPAY, + nextOrderType: ShortOrderType.SHORT_REPAY_FRACTION, assetIn: assetOut.toString(), amountIn: amountOut.toString(), - assetOut: marketConfig.assetAQTokenRaw, // qADA to be redeemed + assetOut: marketConfig.assetB.toString(), amountOut: amountOut.toString(), + additionalOrders: [ + { orderType: ShortOrderType.SHORT_REPAY_FRACTION }, + { orderType: ShortOrderType.SHORT_WITHDRAW_FRACTION }, + { orderType: ShortOrderType.SHORT_BUY_FREED }, + ], + originalDebtLovelace: loanAmountRaw.toString(), }; } } @@ -1777,6 +2209,9 @@ export namespace StateMachine { [ShortOrderType.SHORT_SELL]: handleShortSell, [ShortOrderType.SHORT_BUY]: handleShortBuy, [ShortOrderType.SHORT_REPAY]: handleShortRepay, + [ShortOrderType.SHORT_REPAY_FRACTION]: handleShortRepayFraction, + [ShortOrderType.SHORT_WITHDRAW_FRACTION]: handleShortWithdrawFraction, + [ShortOrderType.SHORT_BUY_FREED]: handleShortBuyFreed, [ShortOrderType.SHORT_WITHDRAW]: handleShortWithdraw, }; @@ -1798,6 +2233,9 @@ export namespace StateMachine { [ShortOrderType.SHORT_SELL]: waitingShortSell, [ShortOrderType.SHORT_BUY]: waitingShortBuy, [ShortOrderType.SHORT_REPAY]: waitingShortRepay, + [ShortOrderType.SHORT_REPAY_FRACTION]: waitingShortRepayFraction, + [ShortOrderType.SHORT_WITHDRAW_FRACTION]: waitingShortWithdrawFraction, + [ShortOrderType.SHORT_BUY_FREED]: waitingShortBuyFreed, [ShortOrderType.SHORT_WITHDRAW]: waitingShortWithdraw, }; } diff --git a/apps/long-short-backend/src/services/position-service.ts b/apps/long-short-backend/src/services/position-service.ts index 6c2a914..6e4b495 100644 --- a/apps/long-short-backend/src/services/position-service.ts +++ b/apps/long-short-backend/src/services/position-service.ts @@ -219,8 +219,11 @@ export class PositionService { positionAmountIn: position.amountIn, }; - // For LONG_REPAY_FRACTION, pass the original debt (stored in amountOut during creation) - if (waitingOrder.orderType === StateMachine.LongOrderType.LONG_REPAY_FRACTION) { + // For fractional repay flows, pass the original debt (stored in amountOut during creation) + if ( + waitingOrder.orderType === StateMachine.LongOrderType.LONG_REPAY_FRACTION || + waitingOrder.orderType === StateMachine.ShortOrderType.SHORT_REPAY_FRACTION + ) { waitingOptions.originalDebtLovelace = waitingOrder.amountOut ?? undefined; } @@ -453,30 +456,12 @@ export class PositionService { amountBorrow: position.amountBorrow, }; - // LONG_REPAY and LONG_REPAY_FRACTION: no extra options needed — handlers fetch - // loan data directly from the Liqwid API (always up-to-date, even after modifyBorrow). + // LONG_REPAY, LONG_REPAY_FRACTION, SHORT_REPAY, SHORT_REPAY_FRACTION: no extra options needed — + // handlers fetch loan data directly from the Liqwid API (always up-to-date, even after modifyBorrow). // LONG_WITHDRAW and SHORT_WITHDRAW: handlers compute withdraw amount dynamically // from UTxO qToken balance and market exchange rate (no DB lookup needed). - // For SHORT_REPAY, we need the loan transaction ID, output index, and collateral amount from SHORT_BORROW - if (order.orderType === StateMachine.ShortOrderType.SHORT_REPAY) { - const borrowOrder = await OrderRepository.getOrderByPositionAndType( - this.db, - position.id, - StateMachine.ShortOrderType.SHORT_BORROW, - ); - if (!borrowOrder?.createdTxId) { - return { success: false, error: "SHORT_BORROW order not found or not confirmed yet" }; - } - if (!borrowOrder.amountIn) { - return { success: false, error: "SHORT_BORROW order amountIn (collateral amount) not set" }; - } - buildOptions.loanTxId = borrowOrder.createdTxId; - buildOptions.loanOutputIndex = borrowOrder.createdTxIndex ?? 0; - buildOptions.collateralAmount = borrowOrder.amountIn; // qADA amount used as collateral - } - const txResult = await buildFn(buildOptions); // Update order built_tx fields From f023d228c28acb208e5f588851a6a9ebdcdc80ac Mon Sep 17 00:00:00 2001 From: tony Date: Mon, 23 Mar 2026 17:06:26 +0700 Subject: [PATCH 03/10] debug --- .../src/api/state-machine.ts | 44 ++++++++++++------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/apps/long-short-backend/src/api/state-machine.ts b/apps/long-short-backend/src/api/state-machine.ts index 7ba13ce..f29ab63 100644 --- a/apps/long-short-backend/src/api/state-machine.ts +++ b/apps/long-short-backend/src/api/state-machine.ts @@ -1101,23 +1101,25 @@ export namespace StateMachine { // SHORT_REPAY_FRACTION ──── modifyBorrow(newDebt, keepCollateral) // │ // ▼ - // SHORT_WITHDRAW_FRACTION ── modifyBorrow(sameDebt, reducedCollateral, redeemCollateral=true) → ADA - // │ - // ▼ - // SHORT_BUY_FREED ────────── buy asset B with freed ADA (A_TO_B swap) + // SHORT_BUY_FREED ────────── buy asset B with user's remaining ADA (A_TO_B swap) // │ // ├── asset B received < remaining loan? ──► loop back to SHORT_REPAY_FRACTION // │ // └── asset B received >= remaining loan? ──► SHORT_REPAY (full repay) // │ // SHORT_WITHDRAW + // + // Note: Unlike LONG fractional flow, SHORT skips WITHDRAW_FRACTION because + // Liqwid's modifyBorrow with redeemCollateral=true requires MarketParams for + // the collateral market (qADA), which may not be available. Instead, collateral + // stays locked until full repay, and the user's existing ADA buys more asset B. // ============================================================================ /** * Build SHORT_REPAY_FRACTION: Partial debt repay via modifyBorrow. * Self-contained — fetches current loan from Liqwid API. * Reduces debt by available asset B while keeping full collateral (qADA) unchanged. - * Step 2 (SHORT_WITHDRAW_FRACTION) reduces collateral proportionally in a separate tx. + * After confirmation, transitions to SHORT_BUY_FREED to buy more asset B with user's ADA. */ export const handleShortRepayFraction = async (options: HandleBuildTxOptions): Promise => { const { order, marketConfig, userAddress, networkEnv, utxos } = options; @@ -1210,12 +1212,11 @@ export namespace StateMachine { * SHORT_REPAY_FRACTION is a Liqwid modifyBorrow tx that reduces debt while keeping * full collateral. Asset B is consumed as repayment — no tokens returned to user. * - * On confirmation → transition to SHORT_WITHDRAW_FRACTION: - * amountIn = originalDebtRaw (for proportional collateral calc in handleShortWithdrawFraction) + * On confirmation → transition to SHORT_BUY_FREED: + * Buy more asset B with user's remaining ADA to cover remaining loan. */ export const waitingShortRepayFraction = async (options: WaitingOptions): Promise => { - const { marketConfig, txHash, userAddress, cardanoscanProvider, originalDebtLovelace } = options; - invariant(originalDebtLovelace, "originalDebtLovelace is required for waitingShortRepayFraction"); + const { marketConfig, txHash, userAddress, cardanoscanProvider } = options; const txFoundOnChain = await cardanoscanProvider.findTransactionByHash( userAddress, @@ -1225,12 +1226,25 @@ export namespace StateMachine { ); if (txFoundOnChain) { + // After partial repay, transition to SHORT_BUY_FREED to buy more asset B + // with user's remaining ADA. The handler will use whatever ADA the user has. + const userAddressHex = userAddress.toHex(); + + // Find ADA balance from tx outputs to pass as amountIn for the buy + let adaBalance = 0n; + for (const output of txFoundOnChain.outputs) { + if (output.address === userAddressHex) { + adaBalance += BigInt(output.value); + } + } + return { isConfirmed: true, - nextOrderType: ShortOrderType.SHORT_WITHDRAW_FRACTION, - assetIn: marketConfig.assetA.toString(), // placeholder (handler fetches loan from API) - amountIn: originalDebtLovelace, // original debt for proportional collateral calc - assetOut: marketConfig.assetA.toString(), // ADA will be received after withdraw + nextOrderType: ShortOrderType.SHORT_BUY_FREED, + assetIn: marketConfig.assetA.toString(), // ADA + amountIn: adaBalance.toString(), + assetOut: marketConfig.assetB.toString(), // asset B to buy + amountOut: adaBalance.toString(), }; } @@ -1428,7 +1442,6 @@ export namespace StateMachine { amountOut: amountOut.toString(), additionalOrders: [ { orderType: ShortOrderType.SHORT_REPAY_FRACTION }, - { orderType: ShortOrderType.SHORT_WITHDRAW_FRACTION }, { orderType: ShortOrderType.SHORT_BUY_FREED }, ], originalDebtLovelace: loanAmountRaw.toString(), @@ -2091,7 +2104,6 @@ export namespace StateMachine { amountOut: amountOut.toString(), additionalOrders: [ { orderType: ShortOrderType.SHORT_REPAY_FRACTION }, - { orderType: ShortOrderType.SHORT_WITHDRAW_FRACTION }, { orderType: ShortOrderType.SHORT_BUY_FREED }, ], originalDebtLovelace: loanAmountRaw.toString(), @@ -2210,7 +2222,6 @@ export namespace StateMachine { [ShortOrderType.SHORT_BUY]: handleShortBuy, [ShortOrderType.SHORT_REPAY]: handleShortRepay, [ShortOrderType.SHORT_REPAY_FRACTION]: handleShortRepayFraction, - [ShortOrderType.SHORT_WITHDRAW_FRACTION]: handleShortWithdrawFraction, [ShortOrderType.SHORT_BUY_FREED]: handleShortBuyFreed, [ShortOrderType.SHORT_WITHDRAW]: handleShortWithdraw, }; @@ -2234,7 +2245,6 @@ export namespace StateMachine { [ShortOrderType.SHORT_BUY]: waitingShortBuy, [ShortOrderType.SHORT_REPAY]: waitingShortRepay, [ShortOrderType.SHORT_REPAY_FRACTION]: waitingShortRepayFraction, - [ShortOrderType.SHORT_WITHDRAW_FRACTION]: waitingShortWithdrawFraction, [ShortOrderType.SHORT_BUY_FREED]: waitingShortBuyFreed, [ShortOrderType.SHORT_WITHDRAW]: waitingShortWithdraw, }; From 1946261b76e2e2310e0064075ff72d62291ce318 Mon Sep 17 00:00:00 2001 From: tony Date: Tue, 24 Mar 2026 10:26:32 +0700 Subject: [PATCH 04/10] ready for production --- Dockerfile.interface | 49 - apps/example/package.json | 10 +- .../documents/api-endpoints.md | 70 + .../documents/configuration.md | 56 + .../documents/database-schema.md | 86 + .../documents/external-integrations.md | 57 + apps/long-short-backend/documents/overview.md | 41 + .../documents/services-and-repositories.md | 77 + .../documents/state-machine.md | 118 + apps/long-short-backend/package.json | 8 +- apps/long-short-backend/src/api/server.ts | 48 +- apps/web/.gitignore | 36 - apps/web/app/api/liqwid/graphql/route.ts | 33 - apps/web/app/api/liqwid/supply/route.ts | 29 - apps/web/app/atoms/utils.ts | 41 - apps/web/app/atoms/walletAtom.ts | 231 - apps/web/app/components/deposit-withdraw.tsx | 314 -- apps/web/app/components/eternl-connector.tsx | 96 - .../margin-trading-position-tab.tsx | 926 ---- .../components/margin-trading-trade-tab.tsx | 440 -- apps/web/app/components/margin-trading.tsx | 43 - .../app/components/nitro-wallet-connector.tsx | 252 - .../components/short-trading-position-tab.tsx | 731 --- .../components/short-trading-trade-tab.tsx | 280 -- apps/web/app/components/short-trading.tsx | 43 - apps/web/app/config/index.ts | 11 - apps/web/app/constants/storage.ts | 6 - apps/web/app/favicon.ico | Bin 25931 -> 0 bytes apps/web/app/fonts/GeistMonoVF.woff | Bin 67864 -> 0 bytes apps/web/app/fonts/GeistVF.woff | Bin 66268 -> 0 bytes apps/web/app/globals.css | 50 - apps/web/app/layout.tsx | 37 - apps/web/app/lib/helpers.ts | 55 - apps/web/app/lib/use-nitro-wallet.ts | 252 - apps/web/app/lib/use-position.ts | 576 --- apps/web/app/lib/use-wallet.ts | 68 - apps/web/app/lib/utils.ts | 13 - apps/web/app/lib/wallet-utils.ts | 86 - apps/web/app/page.tsx | 92 - apps/web/eslint.config.js | 4 - apps/web/next.config.js | 37 - apps/web/package.json | 40 - apps/web/public/file-text.svg | 3 - apps/web/public/globe.svg | 10 - apps/web/public/next.svg | 1 - apps/web/public/turborepo-dark.svg | 19 - apps/web/public/turborepo-light.svg | 19 - apps/web/public/vercel.svg | 10 - apps/web/public/window.svg | 3 - apps/web/tsconfig.json | 12 - docker-compose.yml | 13 - package.json | 13 +- packages/cip/package.json | 6 +- packages/eslint-config/README.md | 3 - packages/eslint-config/base.js | 32 - packages/eslint-config/next.js | 49 - packages/eslint-config/package.json | 24 - packages/eslint-config/react-internal.js | 39 - packages/felis/package.json | 26 +- packages/ledger-core/package.json | 20 +- packages/ledger-utils/package.json | 13 +- packages/minswap-build-tx/package.json | 8 +- packages/minswap-dex-v1/package.json | 12 +- packages/minswap-dex-v2/package.json | 6 +- packages/minswap-lending-market/package.json | 18 +- packages/minswap-stableswap/package.json | 14 +- packages/provider/package.json | 14 +- packages/splash/package.json | 12 +- packages/sundaeswap-v1/package.json | 10 +- packages/sundaeswap-v3/package.json | 12 +- packages/syncer/package.json | 20 +- packages/tx-builder/package.json | 14 +- packages/ui/eslint.config.mjs | 4 - packages/ui/package.json | 26 - packages/ui/src/button.tsx | 20 - packages/ui/src/card.tsx | 27 - packages/ui/src/code.tsx | 11 - packages/ui/tsconfig.json | 8 - packages/wingriders-v1/package.json | 12 +- packages/wingriders-v2/package.json | 12 +- pnpm-lock.yaml | 4056 ++--------------- turbo.json | 18 +- 82 files changed, 939 insertions(+), 9152 deletions(-) delete mode 100644 Dockerfile.interface create mode 100644 apps/long-short-backend/documents/api-endpoints.md create mode 100644 apps/long-short-backend/documents/configuration.md create mode 100644 apps/long-short-backend/documents/database-schema.md create mode 100644 apps/long-short-backend/documents/external-integrations.md create mode 100644 apps/long-short-backend/documents/overview.md create mode 100644 apps/long-short-backend/documents/services-and-repositories.md create mode 100644 apps/long-short-backend/documents/state-machine.md delete mode 100644 apps/web/.gitignore delete mode 100644 apps/web/app/api/liqwid/graphql/route.ts delete mode 100644 apps/web/app/api/liqwid/supply/route.ts delete mode 100644 apps/web/app/atoms/utils.ts delete mode 100644 apps/web/app/atoms/walletAtom.ts delete mode 100644 apps/web/app/components/deposit-withdraw.tsx delete mode 100644 apps/web/app/components/eternl-connector.tsx delete mode 100644 apps/web/app/components/margin-trading-position-tab.tsx delete mode 100644 apps/web/app/components/margin-trading-trade-tab.tsx delete mode 100644 apps/web/app/components/margin-trading.tsx delete mode 100644 apps/web/app/components/nitro-wallet-connector.tsx delete mode 100644 apps/web/app/components/short-trading-position-tab.tsx delete mode 100644 apps/web/app/components/short-trading-trade-tab.tsx delete mode 100644 apps/web/app/components/short-trading.tsx delete mode 100644 apps/web/app/config/index.ts delete mode 100644 apps/web/app/constants/storage.ts delete mode 100644 apps/web/app/favicon.ico delete mode 100644 apps/web/app/fonts/GeistMonoVF.woff delete mode 100644 apps/web/app/fonts/GeistVF.woff delete mode 100644 apps/web/app/globals.css delete mode 100644 apps/web/app/layout.tsx delete mode 100644 apps/web/app/lib/helpers.ts delete mode 100644 apps/web/app/lib/use-nitro-wallet.ts delete mode 100644 apps/web/app/lib/use-position.ts delete mode 100644 apps/web/app/lib/use-wallet.ts delete mode 100644 apps/web/app/lib/utils.ts delete mode 100644 apps/web/app/lib/wallet-utils.ts delete mode 100644 apps/web/app/page.tsx delete mode 100644 apps/web/eslint.config.js delete mode 100644 apps/web/next.config.js delete mode 100644 apps/web/package.json delete mode 100644 apps/web/public/file-text.svg delete mode 100644 apps/web/public/globe.svg delete mode 100644 apps/web/public/next.svg delete mode 100644 apps/web/public/turborepo-dark.svg delete mode 100644 apps/web/public/turborepo-light.svg delete mode 100644 apps/web/public/vercel.svg delete mode 100644 apps/web/public/window.svg delete mode 100644 apps/web/tsconfig.json delete mode 100644 packages/eslint-config/README.md delete mode 100644 packages/eslint-config/base.js delete mode 100644 packages/eslint-config/next.js delete mode 100644 packages/eslint-config/package.json delete mode 100644 packages/eslint-config/react-internal.js delete mode 100644 packages/ui/eslint.config.mjs delete mode 100644 packages/ui/package.json delete mode 100644 packages/ui/src/button.tsx delete mode 100644 packages/ui/src/card.tsx delete mode 100644 packages/ui/src/code.tsx delete mode 100644 packages/ui/tsconfig.json diff --git a/Dockerfile.interface b/Dockerfile.interface deleted file mode 100644 index 286ec11..0000000 --- a/Dockerfile.interface +++ /dev/null @@ -1,49 +0,0 @@ -# Stage 1: Build -FROM node:22-slim AS builder - -# Install pnpm -RUN npm install -g pnpm - -# Set working directory -WORKDIR /app - -# Copy monorepo files -COPY package.json pnpm-lock.yaml pnpm-workspace.yaml turbo.json ./ - -# Copy all packages and apps -COPY packages ./packages -COPY apps ./apps - -# Install dependencies -RUN pnpm install --frozen-lockfile - -# Build all packages using Turbo -RUN pnpm build - -# Stage 2: Runtime -FROM node:22-slim - -# Install pnpm -RUN npm install -g pnpm - -# Set working directory -WORKDIR /app - -# Copy package.json files and turbo config -COPY package.json pnpm-lock.yaml pnpm-workspace.yaml turbo.json ./ - -# Copy built packages and apps from builder -COPY --from=builder /app/packages ./packages -COPY --from=builder /app/apps/web ./apps/web - -# Install production dependencies only -RUN pnpm install --prod --frozen-lockfile - -# Expose port -EXPOSE 3002 - -# Set environment to production -ENV NODE_ENV=production - -# Start the application -CMD ["pnpm", "--filter=web", "start"] diff --git a/apps/example/package.json b/apps/example/package.json index ac23920..c8e9f47 100644 --- a/apps/example/package.json +++ b/apps/example/package.json @@ -12,25 +12,23 @@ "check-circular-imports": "dpdm --no-warning --no-tree src" }, "devDependencies": { - "@repo/eslint-config": "workspace:*", "@repo/typescript-config": "workspace:*", "@types/json-bigint": "^1.0.4", "@types/node": "^22.15.3", - "dpdm": "^3.14.0", - "eslint": "^9.31.0", + "dpdm": "^3.15.1", "typescript": "5.8.2" }, "dependencies": { "@cardano-ogmios/client": "^6.11.0", - "@minswap/tiny-invariant": "^1.2.0", + "@minswap/felis-dex-v2": "workspace:*", "@minswap/felis-ledger-core": "workspace:*", "@minswap/felis-ledger-utils": "workspace:*", - "@minswap/felis-dex-v2": "workspace:*", + "@minswap/felis-provider": "workspace:*", "@minswap/felis-sundaeswap-v1": "workspace:*", "@minswap/felis-sundaeswap-v3": "workspace:*", "@minswap/felis-syncer": "workspace:*", - "@minswap/felis-provider": "workspace:*", "@minswap/felis-tx-builder": "workspace:*", + "@minswap/tiny-invariant": "^1.2.0", "socket.io-client": "^4.8.3" } } diff --git a/apps/long-short-backend/documents/api-endpoints.md b/apps/long-short-backend/documents/api-endpoints.md new file mode 100644 index 0000000..6b49665 --- /dev/null +++ b/apps/long-short-backend/documents/api-endpoints.md @@ -0,0 +1,70 @@ +# API Endpoints + +## Routes + +| Endpoint | Method | Auth | Purpose | +|----------|--------|------|---------| +| `/health` | GET | No | Health check, silent logging | +| `/metadata` | GET | No | List all enabled markets + Liqwid APYs | +| `/position/get` | GET | No | Fetch user's open position by address | +| `/position/create` | POST | CIP-8 | Create new leveraged position | +| `/position/build-tx` | POST | CIP-8 | Build next transaction in order sequence | +| `/position/close` | POST | CIP-8 | Initiate closing of open position | +| `/liqwid/submit` | POST | CIP-8 | Submit signed Liqwid transaction | + +## Authentication (CIP-8) + +Authenticated endpoints require a signed payload: + +```json +{ + "data": { /* order data */ }, + "user_address": "addr1q...", + "witness": { + "key": "a40101...", + "signature": "844da2..." + } +} +``` + +Verification flow (in `src/api/helper.ts`): +- SHA256 hash of `JSON.stringify(data)` must match the CIP-8 signature +- The `key` field is a COSEKey hex +- The `signature` field is a COSESign1 hex + +## Endpoint Details + +### GET /metadata + +Returns all enabled market configs with Liqwid APY data. No authentication required. + +### GET /position/get + +Query params: `user_address` + +Returns the user's open position (if any) for the given address. + +### POST /position/create + +Creates a new leveraged position. Inserts the position record and pre-creates all order steps for the position lifecycle. + +- LONG positions: 4 opening orders (LONG_BUY, LONG_SUPPLY, LONG_BORROW, LONG_BUY_MORE) +- SHORT positions: 3 opening orders (SHORT_SUPPLY, SHORT_BORROW, SHORT_SELL) + +### POST /position/build-tx + +Core endpoint. Builds the next transaction in the order sequence: +1. Checks for waiting orders (awaiting on-chain confirmation) +2. Finds next unhandled order +3. Builds or rebuilds the transaction as needed +4. Returns `tx_raw` for client to sign and submit + +### POST /position/close + +Initiates closing of an open position. Creates closing order steps: +- LONG: LONG_SELL, LONG_REPAY, LONG_WITHDRAW, LONG_SELL_ALL +- SHORT: SHORT_BUY, SHORT_REPAY, SHORT_WITHDRAW + +### POST /liqwid/submit + +Submits a signed Liqwid transaction to the blockchain. diff --git a/apps/long-short-backend/documents/configuration.md b/apps/long-short-backend/documents/configuration.md new file mode 100644 index 0000000..a41daa5 --- /dev/null +++ b/apps/long-short-backend/documents/configuration.md @@ -0,0 +1,56 @@ +# Configuration & Environment + +## Environment Variables + +| Variable | Required | Default | Description | +|----------|----------|---------|-------------| +| `DATABASE_URL` | Yes | -- | PostgreSQL connection string | +| `CARDANOSCAN_API_KEY` | Yes | -- | Cardanoscan API key | +| `NETWORK` | No | `"mainnet"` | `"mainnet"` or `"testnet_preview"` | +| `API_PORT` | No | `9999` | HTTP server port | +| `API_HOST` | No | `0.0.0.0` | HTTP server host | + +## Market Configuration + +Loaded from the `market_config` database table at startup and cached in memory. + +```typescript +type MarketConfig = { + marketId: string; // e.g. "ADA-NIGHT" + assetA: Asset; // Base asset (lovelace for ADA) + assetB: Asset; // Quote asset + ammLpAsset: string; // Minswap LP token + assetAQTokenTicker: string; // e.g. "qAda" + assetAQTokenRaw: string; // policyId of qToken + assetBQTokenTicker: string; // e.g. "qNIGHT" + assetBQTokenRaw: string; + longCollateralMarketId: string; // Liqwid market ID for long collateral + shortCollateralMarketId: string; // Liqwid market ID for short collateral + borrowMarketIdLong: string; // Liqwid market ID for long borrow + borrowMarketIdShort: string; // Liqwid market ID for short borrow + longLeverage: number; // e.g. 1.5 + shortLeverage: number; // e.g. 0.5 + minCollateral: bigint; // Minimum collateral in lovelace + enable: boolean; +}; +``` + +Hot reload available via `reloadMarketConfigs(db)`. + +## Dependencies + +Key runtime dependencies: + +- **Fastify** v5 -- HTTP server +- **Kysely** v0.28 -- PostgreSQL query builder / ORM +- **TypeBox** v0.34 -- JSON schema validation +- **pg** v8 -- PostgreSQL driver + +Cardano-specific: +- `@cardano-ogmios/client` -- Ogmios client +- `@emurgo/cardano-message-signing-nodejs` -- CIP-8 message signing +- Felis workspace packages (see external-integrations.md) + +## Docker + +PostgreSQL and Redis are available via `docker-compose.yml` at the repo root. diff --git a/apps/long-short-backend/documents/database-schema.md b/apps/long-short-backend/documents/database-schema.md new file mode 100644 index 0000000..b02ef97 --- /dev/null +++ b/apps/long-short-backend/documents/database-schema.md @@ -0,0 +1,86 @@ +# Database Schema + +Technology: **Kysely ORM + PostgreSQL** + +Migrations located in `.config/migrations/` (12 migration files). + +## Tables + +### `position` + +```sql +CREATE TABLE position ( + id BIGSERIAL PRIMARY KEY, + market_id VARCHAR(64) NOT NULL, -- FK -> market_config.market_id + user_address VARCHAR(128) NOT NULL, + side VARCHAR(8) NOT NULL, -- LONG | SHORT + status VARCHAR(16) NOT NULL DEFAULT 'PENDING', -- PENDING | OPEN | CLOSING | CLOSED + amount_in NUMERIC NOT NULL, -- Collateral (lovelace) + amount_borrow NUMERIC NOT NULL, -- Borrow amount + created_at TIMESTAMP NOT NULL DEFAULT now(), + closed_at TIMESTAMP +); + +-- Unique constraint: (user_address, market_id) WHERE closed_at IS NULL +-- Indexes: (market_id), (user_address), (closed_at) +``` + +### `order` + +```sql +CREATE TABLE "order" ( + id BIGSERIAL PRIMARY KEY, + position_id BIGINT NOT NULL, + order_type VARCHAR(32) NOT NULL, -- LONG_BUY, SHORT_SUPPLY, etc. + asset_in VARCHAR(128), + amount_in NUMERIC, + asset_out VARCHAR(128), + amount_out NUMERIC, + built_tx_id VARCHAR(64), -- Hash after local build + built_valid_to TIMESTAMP, -- Transaction expiry + created_tx_id VARCHAR(64), -- Hash when confirmed on-chain + created_tx_index INTEGER, -- Output index + waiting BOOLEAN DEFAULT FALSE -- True while awaiting confirmation +); + +-- Indexes: (position_id), (created_tx_id) +``` + +### `market_config` + +```sql +CREATE TABLE market_config ( + market_id VARCHAR(64) PRIMARY KEY, + asset_a VARCHAR(128) NOT NULL, + asset_b VARCHAR(128) NOT NULL, + amm_lp_asset VARCHAR(128) NOT NULL, + asset_a_q_token_ticker VARCHAR(32) NOT NULL, + asset_a_q_token_raw VARCHAR(128) NOT NULL, + asset_b_q_token_ticker VARCHAR(32) NOT NULL, + asset_b_q_token_raw VARCHAR(128) NOT NULL, + long_collateral_market_id VARCHAR(64) NOT NULL, + short_collateral_market_id VARCHAR(64), + borrow_market_id_long VARCHAR(64), + borrow_market_id_short VARCHAR(64), + long_leverage NUMERIC NOT NULL, + short_leverage NUMERIC NOT NULL, + min_collateral NUMERIC NOT NULL, + enable BOOLEAN DEFAULT TRUE +); + +-- Index: (enable) +``` + +## Migration Commands + +```bash +pnpm --filter=long-short-backend run migrate:latest # Apply all pending migrations +pnpm --filter=long-short-backend run migrate:down # Rollback last migration +pnpm --filter=long-short-backend run codegen # Regenerate src/database/db.d.ts +``` + +## Notes + +- Position IDs are `BIGSERIAL` -- always use `BigInt(row.id)` when mapping rows +- DB types are defined in `src/database/db.d.ts`, use `Generated` for columns with defaults +- `json-bigint` is used for JSON serialization with BigInt values (never native `JSON.stringify`) diff --git a/apps/long-short-backend/documents/external-integrations.md b/apps/long-short-backend/documents/external-integrations.md new file mode 100644 index 0000000..149d2e6 --- /dev/null +++ b/apps/long-short-backend/documents/external-integrations.md @@ -0,0 +1,57 @@ +# External Integrations + +## Cardanoscan Provider + +**File:** `src/provider/cardanoscan.ts` + +On-chain transaction queries to confirm built transactions. + +- `getTransactionList(options)` -- Fetch transactions for an address + +**URLs:** +- Mainnet: `https://api.cardanoscan.io/api/v1` +- Preview: `https://api-preview.cardanoscan.io/api/v1` + +**Auth:** Header `apiKey: {CARDANOSCAN_API_KEY}` + +**Note:** Use `address.toHex()` (not bech32) for Cardanoscan API calls. + +## Liqwid V2 Provider + +**Package:** `@minswap/felis-lending-market` + +Lending protocol integration for supply, borrow, repay, and withdraw operations. + +Key methods: +- `LiqwidProviderV2.Transactions.borrow()` -- Build borrow transaction +- `LiqwidProviderV2.Data.markets()` -- Fetch market APY data +- `LiqwidProviderV2.Data.loansForUser()` -- Fetch active loans + +Legacy `LiqwidProvider` (V1) used for supply transactions. + +## Minswap Aggregator + +**File:** `src/provider/minswap-aggregator.ts` + +Price estimation for swaps. + +- `estimate(request)` -- GET swap output amount + +**URLs:** +- Mainnet: `https://monorepo-mainnet-prod.minswap.org/aggregator/estimate` +- Preview: `https://aggr.dev-3.minswap.org/aggregator/estimate` + +Used to estimate SHORT borrow amounts: `amount_in * shortLeverage` ADA worth of asset B. + +## Felis Workspace Libraries + +Core Cardano and trading infrastructure from the monorepo: + +| Package | Purpose | +|---------|---------| +| `@minswap/felis-ledger-core` | Address, Asset, Utxo, NetworkEnvironment | +| `@minswap/felis-ledger-utils` | RustModule, Duration, Result, crypto | +| `@minswap/felis-tx-builder` | TxBuilder, CoinSelectionAlgorithm | +| `@minswap/felis-build-tx` | DEXOrderTransaction for Minswap orders | +| `@minswap/felis-dex-v2` | OrderV2, DexVersion, swap direction enums | +| `@minswap/felis-lending-market` | LiqwidProviderV2 for Liqwid lending | diff --git a/apps/long-short-backend/documents/overview.md b/apps/long-short-backend/documents/overview.md new file mode 100644 index 0000000..799e191 --- /dev/null +++ b/apps/long-short-backend/documents/overview.md @@ -0,0 +1,41 @@ +# Long-Short Backend Overview + +A **Fastify API** that orchestrates leveraged long/short positions on Cardano by coordinating DEX swaps (Minswap) with lending (Liqwid) through a multi-step order state machine. + +## Architecture + +``` +src/ +├── cmd/run-api.ts # Entry point +├── api/ +│ ├── server.ts # Fastify setup & route registration +│ ├── routes/ # position, liqwid, metadata endpoints +│ ├── schemas.ts # TypeBox request/response schemas +│ ├── helper.ts # CIP-8 authentication +│ └── state-machine.ts # Order building & waiting logic (CORE) +├── config/market.ts # Market config loading & caching +├── database/ # Kysely types, Postgres, Redis +├── provider/ # Cardanoscan, Kupo, Minswap Aggregator +├── repository/ # Position, Order, MarketConfig repos +├── services/position-service.ts # Business logic orchestration +└── utils/ # Logger, CIP-8 signature, helpers +``` + +## Startup Sequence + +Entry point: `src/cmd/run-api.ts` + +1. Validate environment: `DATABASE_URL`, `CARDANOSCAN_API_KEY`, `NETWORK` +2. Parse network: `"mainnet"` -> MAINNET; else -> TESTNET_PREVIEW +3. Load WASM: `await RustModule.load()` (required for crypto operations) +4. Connect to PostgreSQL via `newKyselyClient(DATABASE_URL)` +5. Load market configs: `loadMarketConfigs(db)` -> in-memory cache +6. Initialize providers: CardanoscanProvider, MinswapAggregatorProvider +7. Start Fastify server: `createApiServer({ port, host, db, networkEnv, cardanoscanProvider })` + +Start commands: + +```bash +npm start # node --import tsx src/cmd/run-api.ts +npm run dev # node --import tsx --watch src/cmd/run-api.ts +``` diff --git a/apps/long-short-backend/documents/services-and-repositories.md b/apps/long-short-backend/documents/services-and-repositories.md new file mode 100644 index 0000000..3eba7a2 --- /dev/null +++ b/apps/long-short-backend/documents/services-and-repositories.md @@ -0,0 +1,77 @@ +# Services & Repositories + +## PositionService + +**File:** `src/services/position-service.ts` + +Business logic orchestration layer. Main methods: + +### createPosition(input) +Creates a position record and pre-creates all order steps: +- LONG: 4 orders (LONG_BUY, LONG_SUPPLY, LONG_BORROW, LONG_BUY_MORE) +- SHORT: 3 orders (SHORT_SUPPLY, SHORT_BORROW, SHORT_SELL) + +### buildTx(input) +Core workflow: +1. Check for waiting order -> confirm or return waiting status +2. Find next unhandled order -> build or rebuild transaction +3. Return `tx_raw` for client signing + +### closePosition(input) +Initiates closing order sequence: +- LONG: LONG_SELL, LONG_REPAY, LONG_WITHDRAW, LONG_SELL_ALL +- SHORT: SHORT_BUY, SHORT_REPAY, SHORT_WITHDRAW + +### getPositionMetrics(position) +Calculates: entry_price, liquidation_price, interest, unrealized_pnl, health. + +--- + +## Repositories + +### OrderRepository + +**File:** `src/repository/order-repository.ts` + +| Method | Description | +|--------|-------------| +| `createOrder(s)` | Insert order(s) into DB | +| `getOrdersByPositionId` | Fetch all orders for a position | +| `getNextUnhandledOrder` | Find next order to execute (has asset_in, no created_tx_id) | +| `getWaitingOrder` | Find order with waiting = true | +| `updateOrderBuiltTx` | Set built_tx_id, built_valid_to after building | +| `updateOrderCreatedTx` | Set created_tx_id, created_tx_index when confirmed on-chain | +| `updateOrderNextDetails` | Update asset_in/amount_in from previous order's output | + +### PositionRepository + +**File:** `src/repository/position-repository.ts` + +| Method | Description | +|--------|-------------| +| `createPosition` | Insert position with status PENDING | +| `getOpenPositionByUser` | Find user's open position | +| `getOpenPositionByUserAndMarket` | Find position for specific market | +| `updatePositionStatus` | Transition status (auto-sets closed_at if CLOSED) | + +### MarketConfigRepository + +**File:** `src/repository/market-config-repository.ts` + +Handles CRUD for the `market_config` table. + +--- + +## Market Config Cache + +**File:** `src/config/market.ts` + +In-memory cache for market configurations: + +| Function | Description | +|----------|-------------| +| `loadMarketConfigs(db)` | Load all markets into cache (called at startup) | +| `getEnabledMarketConfigs()` | Returns enabled markets only | +| `getMarketConfig(marketId)` | Get single market from cache | +| `isSupportedMarket(marketId)` | Check if market is enabled | +| `reloadMarketConfigs(db)` | Hot reload cache | diff --git a/apps/long-short-backend/documents/state-machine.md b/apps/long-short-backend/documents/state-machine.md new file mode 100644 index 0000000..64a35d6 --- /dev/null +++ b/apps/long-short-backend/documents/state-machine.md @@ -0,0 +1,118 @@ +# Order State Machine + +## Position Status Flow + +``` +PENDING -> OPEN -> CLOSING -> CLOSED +``` + +## LONG Position Lifecycle + +### Opening Sequence (4 orders) + +``` +1. LONG_BUY -> Swap ADA -> Asset B (DEX swap A_TO_B) +2. LONG_SUPPLY -> Supply Asset B to Liqwid -> receive qB collateral +3. LONG_BORROW -> Borrow ADA using qB as collateral (Liqwid V2) +4. LONG_BUY_MORE -> Swap borrowed ADA -> Asset B (DEX swap A_TO_B) + -> Position status transitions to OPEN +``` + +### Closing Sequence (4 orders) + +``` +1. LONG_SELL -> Swap Asset B -> ADA (DEX swap B_TO_A) +2. LONG_REPAY -> Repay ADA loan, redeem qB collateral +3. LONG_WITHDRAW -> Withdraw Asset B from Liqwid +4. LONG_SELL_ALL -> Swap remaining Asset B -> ADA (DEX swap B_TO_A) + -> Position status transitions to CLOSED +``` + +## SHORT Position Lifecycle + +### Opening Sequence (3 orders) + +``` +1. SHORT_SUPPLY -> Supply ADA to Liqwid -> receive qADA collateral +2. SHORT_BORROW -> Borrow Asset B using qADA as collateral +3. SHORT_SELL -> Swap Asset B -> ADA (DEX swap B_TO_A) + -> Position status transitions to OPEN +``` + +### Closing Sequence (3 orders) + +``` +1. SHORT_BUY -> Swap ADA -> Asset B (DEX swap A_TO_B) to buy back +2. SHORT_REPAY -> Repay Asset B loan, redeem qADA collateral +3. SHORT_WITHDRAW -> Withdraw ADA from Liqwid + -> Position status transitions to CLOSED +``` + +## Transaction Lifecycle (per order) + +``` +1. build-tx called + -> StateMachine handler builds tx -> built_tx_id set + +2. Client signs & submits tx to blockchain + +3. Next build-tx call + -> Cardanoscan API finds tx on-chain + -> created_tx_id set, waiting = true + +4. Next build-tx call + -> waiting function checks if output is spent + -> If spent: extract amounts, update next order's asset_in/amount_in + -> If not spent: return "waiting" status with remaining time +``` + +## buildTx() Algorithm + +``` +1. Check for waiting order (created_tx_id set, waiting = true) + a. Call waiting function for order type (e.g., waitingLongBuy) + b. If confirmed: transition to next order or complete position + c. If not confirmed: return waiting message + +2. Find next unhandled order + (has assetIn/amountIn/assetOut, created_tx_id is null/empty) + +3. If order has built_tx_id: + a. Search for it on Cardanoscan + b. If found: set created_tx_id, return waiting + c. If not found & expired: rebuild + d. If not found & not expired: return waiting with time remaining + +4. Build new transaction + a. Call appropriate StateMachine handler + b. Set built_tx_id and built_valid_to + c. Return tx_raw for client to sign +``` + +## State Machine Handlers + +Located in `src/api/state-machine.ts`. Each handler returns `BuiltResult = { txRaw, txId, validTo }`. + +**LONG handlers:** +- `handleLongBuy` / `waitingLongBuy` +- `handleLongSupply` / `waitingLongSupply` +- `handleLongBorrow` / `waitingLongBorrow` +- `handleLongBuyMore` / `waitingLongBuyMore` +- `handleLongSell` / `waitingLongSell` +- `handleLongRepay` / `waitingLongRepay` +- `handleLongWithdraw` / `waitingLongWithdraw` + +**SHORT handlers:** +- `handleShortSupply` / `waitingShortSupply` +- `handleShortBorrow` / `waitingShortBorrow` +- `handleShortSell` / `waitingShortSell` +- `handleShortBuy` / `waitingShortBuy` +- `handleShortRepay` / `waitingShortRepay` +- `handleShortWithdraw` / `waitingShortWithdraw` + +## Leverage Calculation + +- **LONG**: `amountBorrow = amountIn * (longLeverage - 1) + 4_000_000` (lovelace) + - Example: 600 ADA at 1.5x -> borrow 304 ADA +- **SHORT**: `amountBorrow = aggregator.estimate(amountIn * shortLeverage)` (in asset B units) + - Example: 600 ADA at 0.5x -> estimate 300 ADA worth of asset B -> borrow that amount diff --git a/apps/long-short-backend/package.json b/apps/long-short-backend/package.json index d012608..7c10486 100644 --- a/apps/long-short-backend/package.json +++ b/apps/long-short-backend/package.json @@ -16,14 +16,12 @@ }, "devDependencies": { "@cardano-ogmios/schema": "^6.11.0", - "@repo/eslint-config": "workspace:*", "@repo/typescript-config": "workspace:*", "@types/crypto-js": "^4.2.2", "@types/json-bigint": "^1.0.4", "@types/node": "^22.15.3", "@types/pg": "^8.16.0", - "dpdm": "^3.14.0", - "eslint": "^9.31.0", + "dpdm": "^3.15.1", "kysely-codegen": "^0.19.0", "kysely-ctl": "^0.19.0", "tsx": "^4.19.4", @@ -34,6 +32,8 @@ "@cardano-ogmios/client": "^6.14.0", "@emurgo/cardano-message-signing-nodejs": "^1.0.1", "@fastify/cors": "^10.0.2", + "@fastify/helmet": "^13.0.2", + "@fastify/rate-limit": "^10.3.0", "@minswap/felis-build-tx": "workspace:*", "@minswap/felis-cip": "workspace:*", "@minswap/felis-dex-v1": "workspace:*", @@ -52,7 +52,7 @@ "fastify": "^5.2.2", "ioredis": "^5.9.0", "json-bigint": "^1.0.0", - "kysely": "^0.28.11", + "kysely": "^0.28.14", "p-timeout": "^7.0.1", "pg": "^8.16.3", "remeda": "^2.33.1", diff --git a/apps/long-short-backend/src/api/server.ts b/apps/long-short-backend/src/api/server.ts index 7170851..598ccf3 100644 --- a/apps/long-short-backend/src/api/server.ts +++ b/apps/long-short-backend/src/api/server.ts @@ -1,6 +1,8 @@ import cors from "@fastify/cors"; +import helmet from "@fastify/helmet"; +import rateLimit from "@fastify/rate-limit"; import type { NetworkEnvironment } from "@minswap/felis-ledger-core"; -import Fastify, { type FastifyInstance } from "fastify"; +import Fastify, { type FastifyError, type FastifyInstance } from "fastify"; import type { Kysely } from "kysely"; import { API_ENDPOINTS } from "../constants"; import type { DB } from "../database"; @@ -26,6 +28,20 @@ export async function createApiServer(options: ApiServerOptions): Promise { + // Validation errors (schema validation failures) + if (error.validation) { + return reply.status(400).send({ + success: false, + error: error.message, + }); + } + + // Rate limit errors + if (error.statusCode === 429) { + return reply.status(429).send({ + success: false, + error: "Too many requests, please try again later", + }); + } + + // Log unexpected errors + request.log.error({ err: error, url: request.url, method: request.method }, "Unhandled error"); + + const statusCode = error.statusCode && error.statusCode >= 400 ? error.statusCode : 500; + return reply.status(statusCode).send({ + success: false, + error: statusCode >= 500 ? "Internal server error" : error.message, + }); + }); + // Health check endpoint (disable logging to reduce noise) - fastify.get(API_ENDPOINTS.HEALTH, { logLevel: "silent" }, async () => { + fastify.get(API_ENDPOINTS.HEALTH, { logLevel: "silent", config: { rateLimit: false } }, async () => { return { status: "ok" }; }); diff --git a/apps/web/.gitignore b/apps/web/.gitignore deleted file mode 100644 index f886745..0000000 --- a/apps/web/.gitignore +++ /dev/null @@ -1,36 +0,0 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - -# dependencies -/node_modules -/.pnp -.pnp.js -.yarn/install-state.gz - -# testing -/coverage - -# next.js -/.next/ -/out/ - -# production -/build - -# misc -.DS_Store -*.pem - -# debug -npm-debug.log* -yarn-debug.log* -yarn-error.log* - -# env files (can opt-in for commiting if needed) -.env* - -# vercel -.vercel - -# typescript -*.tsbuildinfo -next-env.d.ts diff --git a/apps/web/app/api/liqwid/graphql/route.ts b/apps/web/app/api/liqwid/graphql/route.ts deleted file mode 100644 index 624ebae..0000000 --- a/apps/web/app/api/liqwid/graphql/route.ts +++ /dev/null @@ -1,33 +0,0 @@ -import type { NextRequest } from "next/server"; -import { NextResponse } from "next/server"; - -export async function POST(request: NextRequest) { - try { - const body = await request.json(); - - const response = await fetch("https://v2.api.preview.liqwid.dev/graphql", { - method: "POST", - headers: { - Accept: "*/*", - "Accept-Language": "en-US,en;q=0.9", - "Content-Type": "application/json", - "X-App-Source": "liqwid-app", - "X-Request-Id": `req_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`, - }, - body: JSON.stringify(body), - }); - - if (!response.ok) { - return NextResponse.json( - { error: `Liqwid API error: ${response.status} ${response.statusText}` }, - { status: response.status }, - ); - } - - const data = await response.json(); - return NextResponse.json(data); - } catch (error) { - console.error("Liqwid GraphQL proxy error:", error); - return NextResponse.json({ error: "Failed to proxy request to Liqwid API" }, { status: 500 }); - } -} diff --git a/apps/web/app/api/liqwid/supply/route.ts b/apps/web/app/api/liqwid/supply/route.ts deleted file mode 100644 index cb881c5..0000000 --- a/apps/web/app/api/liqwid/supply/route.ts +++ /dev/null @@ -1,29 +0,0 @@ -import type { NextRequest } from "next/server"; -import { NextResponse } from "next/server"; - -export async function POST(request: NextRequest) { - try { - const body = await request.json(); - - const response = await fetch("https://v2.api.preview.liqwid.dev/graphql", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(body), - }); - - if (!response.ok) { - return NextResponse.json( - { error: `Liqwid API error: ${response.status} ${response.statusText}` }, - { status: response.status }, - ); - } - - const data = await response.json(); - return NextResponse.json(data); - } catch (error) { - console.error("Liqwid API proxy error:", error); - return NextResponse.json({ error: "Failed to proxy request to Liqwid API" }, { status: 500 }); - } -} diff --git a/apps/web/app/atoms/utils.ts b/apps/web/app/atoms/utils.ts deleted file mode 100644 index f2c5e91..0000000 --- a/apps/web/app/atoms/utils.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { atomWithStorage } from "jotai/utils"; - -// Utility function to create atoms with localStorage handling -export function createAtomWithStorage( - key: string, - initialValue: T, - parse: (value: string) => T | null, - serialize: (value: T | null) => string, -) { - return atomWithStorage(key, initialValue, { - getItem(key, initialValue) { - if (typeof localStorage === "undefined") { - return initialValue; - } - const storedValue = localStorage.getItem(key); - if (!storedValue) { - return initialValue; - } - try { - return parse(storedValue); - } catch { - return initialValue; - } - }, - setItem(key, newValue) { - if (typeof localStorage === "undefined") { - return; - } - if (newValue === null) { - localStorage.removeItem(key); - } else { - localStorage.setItem(key, serialize(newValue)); - } - }, - removeItem(key) { - if (typeof localStorage !== "undefined") { - localStorage.removeItem(key); - } - }, - }); -} diff --git a/apps/web/app/atoms/walletAtom.ts b/apps/web/app/atoms/walletAtom.ts deleted file mode 100644 index 28169d4..0000000 --- a/apps/web/app/atoms/walletAtom.ts +++ /dev/null @@ -1,231 +0,0 @@ -import { XJSON } from "@minswap/felis-ledger-core"; -import type { LendingMarket } from "@minswap/felis-lending-market"; -import { atom } from "jotai"; -import type { Asset } from "../../../../packages/ledger-core/dist/asset"; -import { LocalStorageKey } from "../constants/storage"; -import type { Cip30Api, WalletInfo } from "../lib/wallet-utils"; -import { createAtomWithStorage } from "./utils"; - -export type WalletData = { - walletInfo: WalletInfo; - api: Cip30Api; -}; -export const walletAtom = atom(null); -export const setWalletAtom = atom(null, (_get, set, update: WalletData | null) => { - set(walletAtom, update); -}); -export const connectedWalletAtom = createAtomWithStorage( - LocalStorageKey.CONNECTED_WALLET, - false, - (value) => value === "true", - (value) => String(value), -); -export const setConnectedWalletAtom = atom(null, (_get, set, update: boolean) => { - set(connectedWalletAtom, update); -}); -export type NitroWalletData = { - walletInfo: WalletInfo & { rootAddress: string }; - privateKey: string; -}; -export const nitroWalletAtom = atom(null); -export const setNitroWalletAtom = atom(null, (_get, set, update: NitroWalletData | null) => { - set(nitroWalletAtom, update); -}); - -export enum LongPositionStatus { - STEP_1_BUY_LONG_ASSET = "buy_long_asset", - STEP_2_SUPPLY_TOKEN = "supply_token", - STEP_3_BORROW_TOKEN = "borrow_token", - STEP_BUY_MORE_LONG_ASSET = "buy_more_long_asset", - OPENING_POSITION = "opening_position", - STEP_4_SELL_LONG_ASSET = "sell_long_asset", - STEP_5_REPAY_ASSET = "repay_asset", - STEP_6_WITHDRAW_COLLATERAL = "withdraw_collateral", - STEP_SELL_ALL_LONG_ASSET = "sell_all_long_asset", - CLOSED_POSITION = "closed_position", -} -export type LongPositionState = { - positionId: string; - status: LongPositionStatus; - nitroWalletAddress: string; - leverage: number; - longAsset: Asset; - borrowAsset: Asset; - createdAt: number; - updatedAt: number; - amount: { - iTotalBorrow: bigint; // ada - iTotalBuy: bigint; // ada - iTotalOperationFee: bigint; // ada - mTotalPaidFee: bigint; // ada - mBought: bigint; // ada - mTotalLong: bigint; // MIN - mLongBalance: bigint; // MIN - mBorrowed: bigint; // ada - mSupplied: bigint; // qMIN - mRepaid: bigint; // ada - mWithdrawn: bigint; // MIN - }; - transactions: { - step: LongPositionStatus; - txHash: string; - }[]; - hasCallback?: number; - callbackExtra?: string; -}; - -export enum ShortPositionStatus { - STEP_1_SUPPLY_TOKEN = "step_1_supply_token", - STEP_2_BORROW_TOKEN = "step_2_borrow_token", - STEP_3_SHORT_TOKEN = "step_3_short_token", - OPENING_POSITION = "opening_position", - STEP_4_BUY_BACK_TOKEN = "step_4_buy_back_token", - STEP_5_REPAY_ASSET = "step_5_repay_asset", - STEP_6_WITHDRAW_COLLATERAL = "step_6_withdraw_collateral", - CLOSED_POSITION = "closed_position", -} - -export type ShortPositionState = { - positionId: string; - status: ShortPositionStatus; - nitroWalletAddress: string; - shortAsset: Asset; - createdAt: number; - updatedAt: number; - amount: { - iTotalSupplyL: bigint; - iBorrowAmountL: bigint; - iLiquidationPrice: number; - mSuppliedL: bigint; - mShortedL: bigint; - mShortedEstimateAda: bigint; - mBorrowedL: bigint; - mTradingPrice: number; - mClosedPrice: number; - mClosedAda: bigint; // ada required to buy back Short Token - mRepaidL: bigint; // same with borrow token - mWithdrawnL: bigint; // same with supply token - }; - transactions: { - step: ShortPositionStatus; - txHash: string; - }[]; - hasCallback?: number; - callbackExtra?: string; - liquidationCall?: boolean; -}; - -// Base atom that stores all positions from all wallets -const allLongPositionsAtom = createAtomWithStorage( - LocalStorageKey.LONG_POSITIONS, - [], - (value) => XJSON.parse(value) as LongPositionState[], - (value) => XJSON.stringify(value), -); - -// Derived atom that filters positions for the current nitro wallet -export const longPositionAtom = atom((get) => { - const nitroWallet = get(nitroWalletAtom); - const allPositions = get(allLongPositionsAtom) || []; - - // If no nitro wallet connected, return empty array - if (!nitroWallet?.walletInfo?.address?.bech32) { - return []; - } - - // Filter positions for current nitro wallet address - return allPositions.filter((position) => position.nitroWalletAddress === nitroWallet.walletInfo.address.bech32); -}); // Write atom to add/update positions - -export const setLongPositionAtom = atom( - null, - (get, set, update: LongPositionState[] | ((prev: LongPositionState[]) => LongPositionState[])) => { - const nitroWallet = get(nitroWalletAtom); - - // If no nitro wallet connected, don't update anything - if (!nitroWallet?.walletInfo?.address?.bech32) { - return; - } - - const allPositions = get(allLongPositionsAtom) || []; - const currentWalletAddress = nitroWallet.walletInfo.address.bech32; - - // Get new positions (either array or function result) - const newPositions = - typeof update === "function" - ? update(allPositions.filter((p) => p.nitroWalletAddress === currentWalletAddress)) - : update; - - // Remove old positions for current wallet and add new ones - const otherWalletsPositions = allPositions.filter((p) => p.nitroWalletAddress !== currentWalletAddress); - const updatedAllPositions = [...otherWalletsPositions, ...newPositions]; - - set(allLongPositionsAtom, updatedAllPositions); - }, -); - -// Base atom that stores all short positions from all wallets -const allShortPositionsAtom = createAtomWithStorage( - LocalStorageKey.SHORT_POSITIONS, - [], - (value) => XJSON.parse(value) as ShortPositionState[], - (value) => XJSON.stringify(value), -); - -// Derived atom that filters short positions for the current nitro wallet -export const shortPositionAtom = atom((get) => { - const nitroWallet = get(nitroWalletAtom); - const allPositions = get(allShortPositionsAtom) || []; - - // If no nitro wallet connected, return empty array - if (!nitroWallet?.walletInfo?.address?.bech32) { - return []; - } - - // Filter positions for current nitro wallet address - return allPositions.filter((position) => position.nitroWalletAddress === nitroWallet.walletInfo.address.bech32); -}); - -// Write atom to add/update short positions -export const setShortPositionAtom = atom( - null, - (get, set, update: ShortPositionState[] | ((prev: ShortPositionState[]) => ShortPositionState[])) => { - const nitroWallet = get(nitroWalletAtom); - - // If no nitro wallet connected, don't update anything - if (!nitroWallet?.walletInfo?.address?.bech32) { - return; - } - - const allPositions = get(allShortPositionsAtom) || []; - const currentWalletAddress = nitroWallet.walletInfo.address.bech32; - - // Get new positions (either array or function result) - const newPositions = - typeof update === "function" - ? update(allPositions.filter((p) => p.nitroWalletAddress === currentWalletAddress)) - : update; - - // Remove old positions for current wallet and add new ones - const otherWalletsPositions = allPositions.filter((p) => p.nitroWalletAddress !== currentWalletAddress); - const updatedAllPositions = [...otherWalletsPositions, ...newPositions]; - - set(allShortPositionsAtom, updatedAllPositions); - }, -); - -// Balance atoms -export const rootBalanceAtom = atom(0n); -export const setRootBalanceAtom = atom(null, (_get, set, update: bigint) => { - set(rootBalanceAtom, update); -}); - -export const nitroBalanceAtom = atom(0n); -export const setNitroBalanceAtom = atom(null, (_get, set, update: bigint) => { - set(nitroBalanceAtom, update); -}); - -export const minAdaPriceAtom = atom(null); -export const setMinAdaPriceAtom = atom(null, (_get, set, update: LendingMarket.PriceInAdaResponse | null) => { - set(minAdaPriceAtom, update); -}); diff --git a/apps/web/app/components/deposit-withdraw.tsx b/apps/web/app/components/deposit-withdraw.tsx deleted file mode 100644 index 0a306a6..0000000 --- a/apps/web/app/components/deposit-withdraw.tsx +++ /dev/null @@ -1,314 +0,0 @@ -"use client"; - -import { ArrowDownOutlined, ArrowUpOutlined } from "@ant-design/icons"; -import { RustModule } from "@minswap/felis-ledger-utils"; -import { NitroWallet } from "@minswap/felis-lending-market"; -import { TxComplete } from "@minswap/felis-tx-builder"; -import { Alert, App, Button, Card, Col, Form, Input, Modal, Row, Space, Statistic } from "antd"; -import { useAtomValue, useSetAtom } from "jotai"; -import { useEffect, useState } from "react"; -import { - nitroBalanceAtom, - nitroWalletAtom, - rootBalanceAtom, - setNitroBalanceAtom, - setRootBalanceAtom, - walletAtom, -} from "../atoms/walletAtom"; -import { CONFIG } from "../config"; -import { Helpers } from "../lib/helpers"; -import { Utils } from "../lib/utils"; - -export const DepositWithdraw = () => { - const { message } = App.useApp(); - const nitroWallet = useAtomValue(nitroWalletAtom); - const rootWallet = useAtomValue(walletAtom); - const rootBalance = useAtomValue(rootBalanceAtom); - const nitroBalance = useAtomValue(nitroBalanceAtom); - const setRootBalance = useSetAtom(setRootBalanceAtom); - const setNitroBalance = useSetAtom(setNitroBalanceAtom); - const [depositModal, setDepositModal] = useState(false); - const [withdrawModal, setWithdrawModal] = useState(false); - const [depositLoading, setDepositLoading] = useState(false); - const [withdrawLoading, setWithdrawLoading] = useState(false); - const [depositForm] = Form.useForm(); - const [withdrawForm] = Form.useForm(); - - // Initialize balances from wallet info - useEffect(() => { - if (rootWallet?.walletInfo?.balance) { - setRootBalance(rootWallet.walletInfo.balance); - } - if (nitroWallet?.walletInfo?.balance) { - setNitroBalance(nitroWallet.walletInfo.balance); - } - }, [rootWallet?.walletInfo?.balance, nitroWallet?.walletInfo?.balance, setRootBalance, setNitroBalance]); - - // Poll balances every 10 seconds - useEffect(() => { - if (!rootWallet?.walletInfo || !nitroWallet?.walletInfo) return; - - const pollBalances = async () => { - try { - const [rootBal, nitroBal] = await Promise.all([ - Helpers.fetchBalance(rootWallet.walletInfo.address.bech32), - Helpers.fetchBalance(nitroWallet.walletInfo.address.bech32), - ]); - setRootBalance(rootBal); - setNitroBalance(nitroBal); - } catch (err) { - console.error("Failed to poll balances:", err); - } - }; - - const interval = setInterval(pollBalances, 10000); // 10 seconds - return () => clearInterval(interval); - }, [rootWallet?.walletInfo, nitroWallet?.walletInfo, setRootBalance, setNitroBalance]); - - const handleDeposit = async (values: { amount: string }) => { - if (!rootWallet?.api || !rootWallet?.walletInfo || !nitroWallet?.walletInfo) { - message.error("Missing wallet information"); - return; - } - - setDepositLoading(true); - try { - const amountInLovelace = BigInt(Math.floor(Number(values.amount) * 1_000_000)); - // Get UTXOs from root wallet - const utxosRaw = await rootWallet.api.getUtxos(); - if (!utxosRaw || utxosRaw.length === 0) { - message.error("No UTXOs available in root wallet"); - return; - } - - // Build deposit transaction using NitroWallet.depositNitroFunds - const txHex = await NitroWallet.depositNitroFunds({ - nitroAddress: nitroWallet.walletInfo.address, - rootAddress: rootWallet.walletInfo.address, - amount: amountInLovelace, - networkEnv: CONFIG.networkEnv, - rootAddressUtxos: utxosRaw, - }); - - // Sign and submit transaction - const witnessSet = await rootWallet.api.signTx(txHex, true); - // Using TxComplete to assemble (aka: finalize Tx) - const tx = RustModule.getE.Transaction.from_hex(txHex); - const txComplete = new TxComplete(tx); - txComplete.assemble(witnessSet); - - // Complete Tx - const signedTx = txComplete.complete(); - const txHash = await rootWallet.api.submitTx(signedTx); - message.success(`Deposit successful! Tx: ${txHash}`); - - depositForm.resetFields(); - setDepositModal(false); - } catch (err) { - const errorMsg = err instanceof Error ? err.message : String(err); - message.error(`Deposit failed: ${errorMsg}`); - console.error("Deposit error:", err); - } finally { - setDepositLoading(false); - } - }; - - const handleWithdraw = async (values: { amount: string }) => { - if (!rootWallet?.api || !rootWallet?.walletInfo || !nitroWallet?.walletInfo || !nitroWallet?.privateKey) { - message.error("Missing wallet information"); - return; - } - - setWithdrawLoading(true); - try { - const amountInLovelace = BigInt(Math.floor(Number(values.amount) * 1_000_000)); - - // Get UTXOs from nitro wallet - const utxosRaw = await NitroWallet.fetchRawUtxos(nitroWallet.walletInfo.address.bech32, CONFIG.networkEnv); - if (!utxosRaw || utxosRaw.length === 0) { - message.error("No UTXOs available in nitro wallet"); - return; - } - - // Build withdraw transaction using NitroWallet.withdrawNitroFunds - const signedTx = await NitroWallet.withdrawNitroFunds({ - nitroAddress: nitroWallet.walletInfo.address, - rootAddress: rootWallet.walletInfo.address, - amount: amountInLovelace, - networkEnv: CONFIG.networkEnv, - nitroAddressUtxos: utxosRaw, - nitroPrivateKey: nitroWallet.privateKey, - }); - const txHash = await NitroWallet.submitTx(signedTx, CONFIG.networkEnv); - message.success(`Withdraw successful! Tx: ${txHash}`); - - withdrawForm.resetFields(); - setWithdrawModal(false); - } catch (err) { - const errorMsg = err instanceof Error ? err.message : String(err); - message.error(`Withdraw failed: ${errorMsg}`); - console.error("Withdraw error:", err); - } finally { - setWithdrawLoading(false); - } - }; - - const isReadyForDeposit = rootWallet?.walletInfo && nitroWallet?.walletInfo && rootBalance > 0n; - - const isReadyForWithdraw = rootWallet?.walletInfo && nitroWallet?.walletInfo && nitroBalance > 0n; - - return ( - - - - - - - - - - - - - - - {!rootWallet?.walletInfo || !nitroWallet?.walletInfo ? ( - - ) : null} - - - - - - - {/* Deposit Modal */} - { - setDepositModal(false); - depositForm.resetFields(); - }} - onOk={() => depositForm.submit()} - open={depositModal} - title="Deposit ADA to Nitro Wallet" - > - - -
- { - const amount = parseFloat(value); - const availableAda = Number(rootBalance) / 1_000_000; - if (amount > availableAda) { - return Promise.reject(new Error(`Insufficient balance. Available: ${availableAda.toFixed(2)} ADA`)); - } - return Promise.resolve(); - }, - }, - ]} - > - - - - - -
-
- - {/* Withdraw Modal */} - { - setWithdrawModal(false); - withdrawForm.resetFields(); - }} - onOk={() => withdrawForm.submit()} - open={withdrawModal} - title="Withdraw ADA from Nitro Wallet" - > - - -
- { - const amount = parseFloat(value); - const availableAda = Number(nitroBalance) / 1_000_000; - if (amount > availableAda) { - return Promise.reject(new Error(`Insufficient balance. Available: ${availableAda.toFixed(2)} ADA`)); - } - return Promise.resolve(); - }, - }, - ]} - > - - - - - -
-
-
- ); -}; diff --git a/apps/web/app/components/eternl-connector.tsx b/apps/web/app/components/eternl-connector.tsx deleted file mode 100644 index a82147c..0000000 --- a/apps/web/app/components/eternl-connector.tsx +++ /dev/null @@ -1,96 +0,0 @@ -"use client"; - -import { CopyOutlined, LogoutOutlined } from "@ant-design/icons"; -import { App, Button, Layout, Space, Tooltip } from "antd"; -import { useAtomValue } from "jotai"; -import { useEffect } from "react"; -import { rootBalanceAtom, walletAtom } from "../atoms/walletAtom"; -import { useNitroWallet } from "../lib/use-nitro-wallet"; -import { useWallet } from "../lib/use-wallet"; -import { Utils } from "../lib/utils"; - -export const EternlConnector = () => { - const { message } = App.useApp(); - const wallet = useWallet(); - const nitroWallet = useNitroWallet(); - const globalWallet = useAtomValue(walletAtom); - const rootBalance = useAtomValue(rootBalanceAtom); - - const handleCopyAddress = async (address: string) => { - try { - await navigator.clipboard.writeText(address); - message.success("Address copied!"); - } catch (_err) { - message.error("Failed to copy address"); - } - }; - - // Show error notification only once when error occurs - useEffect(() => { - if (wallet.error) { - message.error(wallet.error); - } - }, [wallet.error, message]); - - // Use global wallet atom for display, but still show if walletInfo exists - const displayWallet = globalWallet?.walletInfo || wallet.walletInfo; - - if (displayWallet) { - return ( - - - {rootBalance !== undefined && ( - - {rootBalance > 0 ? Utils.formatBalance(rootBalance) : displayWallet.balance} ADA - - )} - - - - - - - ); - } - - return ( - - - - ); -}; diff --git a/apps/web/app/components/margin-trading-position-tab.tsx b/apps/web/app/components/margin-trading-position-tab.tsx deleted file mode 100644 index 00f1ef5..0000000 --- a/apps/web/app/components/margin-trading-position-tab.tsx +++ /dev/null @@ -1,926 +0,0 @@ -"use client"; - -import { CloseOutlined, DollarOutlined, SwapOutlined, TrophyOutlined } from "@ant-design/icons"; -import { ADA, Asset, Utxo, XJSON } from "@minswap/felis-ledger-core"; -import { LendingMarket, type LiqwidProvider, NitroWallet } from "@minswap/felis-lending-market"; -import invariant from "@minswap/tiny-invariant"; -import { Alert, App, Button, Card, Col, Divider, Progress, Row, Space, Statistic, Tag } from "antd"; -import { useAtomValue, useSetAtom } from "jotai"; -import { useCallback, useEffect, useRef } from "react"; -import { - type LongPositionState, - LongPositionStatus, - longPositionAtom, - type NitroWalletData, - nitroWalletAtom, - setLongPositionAtom, - type WalletData, - walletAtom, -} from "../atoms/walletAtom"; -import { CONFIG } from "../config"; -import { Helpers } from "../lib/helpers"; -import { Utils } from "../lib/utils"; - -const getStatusColor = (status: LongPositionStatus) => { - switch (status) { - case LongPositionStatus.STEP_1_BUY_LONG_ASSET: - return "orange"; - case LongPositionStatus.STEP_2_SUPPLY_TOKEN: - return "processing"; - case LongPositionStatus.OPENING_POSITION: - return "success"; - case LongPositionStatus.STEP_3_BORROW_TOKEN: - return "purple"; - case LongPositionStatus.STEP_BUY_MORE_LONG_ASSET: - return "orange"; - case LongPositionStatus.STEP_4_SELL_LONG_ASSET: - return "magenta"; - case LongPositionStatus.STEP_5_REPAY_ASSET: - return "geekblue"; - case LongPositionStatus.STEP_6_WITHDRAW_COLLATERAL: - return "cyan"; - case LongPositionStatus.STEP_SELL_ALL_LONG_ASSET: - return "magenta"; - case LongPositionStatus.CLOSED_POSITION: - return "red"; - default: - return "default"; - } -}; - -const getStatusText = (status: LongPositionStatus) => { - switch (status) { - case LongPositionStatus.STEP_1_BUY_LONG_ASSET: - return "Buying Asset"; - case LongPositionStatus.STEP_2_SUPPLY_TOKEN: - return "Supplying Token"; - case LongPositionStatus.OPENING_POSITION: - return "Opening..."; - case LongPositionStatus.STEP_4_SELL_LONG_ASSET: - case LongPositionStatus.STEP_5_REPAY_ASSET: - case LongPositionStatus.STEP_6_WITHDRAW_COLLATERAL: - case LongPositionStatus.STEP_SELL_ALL_LONG_ASSET: - return `Closing Position... ${status.replace(/_/g, " ")}`; - case LongPositionStatus.CLOSED_POSITION: - return "Position Closed"; - default: - return status.replace(/_/g, " "); - } -}; - -const getProgressPercent = (position: LongPositionState) => { - const { - status, - amount: { iTotalBuy, mBought }, - } = position; - if (status === LongPositionStatus.OPENING_POSITION) { - // Show 100% when position is fully opened - return 100; - } - const progressPercent = (Number(Utils.formatAmount(mBought)) * 100) / Number(Utils.formatAmount(iTotalBuy)); - return Math.min(Math.max(progressPercent, 0), 100); -}; - -const calculatePnL = (position: LongPositionState): { pnl: number; pnlPercent: number } => { - // Simplified PnL calculation - in real implementation, you'd fetch current prices - const totalInvested = Number(position.amount.iTotalBuy) / 1_000_000; - const currentValue = (Number(position.amount.mBought) / 1_000_000) * 1.05; // Mock 5% gain - const pnl = currentValue - totalInvested; - const pnlPercent = (pnl / totalInvested) * 100; - return { pnl, pnlPercent }; -}; - -enum ExtraStatus { - EXTRA_STEP_BUY_MORE = "extra_step_buy_more", - EXTRA_STEP_SELL_ALL = "extra_step_sell_all", -} - -type StepBuyMoreExtra = { - extraStatus: ExtraStatus.EXTRA_STEP_BUY_MORE; - buyMoreAmount: bigint; - nextStatus: LongPositionStatus.OPENING_POSITION; - logStatus: LongPositionStatus.STEP_BUY_MORE_LONG_ASSET; -}; -type StepSellAllExtra = { - extraStatus: ExtraStatus.EXTRA_STEP_SELL_ALL; - nextStatus: LongPositionStatus.CLOSED_POSITION; - logStatus: LongPositionStatus.STEP_SELL_ALL_LONG_ASSET; -}; - -type InnerHandleFn = (input: { - position: LongPositionState; - wallet: WalletData; - nitroWallet: NitroWalletData; - extra?: StepBuyMoreExtra | StepSellAllExtra; -}) => Promise; -type HandlePositionInput = { - position: LongPositionState; - errorMessage: string; - successMessage: string; - innerFn: InnerHandleFn; -}; - -const handleStep1: InnerHandleFn = async ({ position, wallet, nitroWallet, extra }) => { - if (position.hasCallback) { - await Helpers.sleep(10000); - const balance = await NitroWallet.fetchBalance(nitroWallet.walletInfo.address.bech32, CONFIG.networkEnv); - console.log("step 1 callback", XJSON.stringify(balance, 2)); - const minToken = Asset.fromString(LendingMarket.mapMINToken[CONFIG.networkEnv]); - let ok = balance.has(minToken); - const txHash = position.transactions[position.transactions.length - 1]?.txHash; - if (txHash) { - const txConfirmed = await Helpers.checkTxConfirmed(txHash); - console.log({ txConfirmed, txHash }); - ok = ok && txConfirmed; - } - if (ok) { - return { - ...position, - amount: { - ...position.amount, - mLongBalance: balance.get(minToken), - mTotalLong: position.amount.mTotalLong + balance.get(minToken), - }, - // priority extra - status: - extra && extra.extraStatus === ExtraStatus.EXTRA_STEP_BUY_MORE - ? extra.nextStatus - : LongPositionStatus.STEP_2_SUPPLY_TOKEN, - hasCallback: undefined, - callbackExtra: undefined, - }; - } else { - // continue callback - return { ...position, hasCallback: position.hasCallback + 1 }; - } - } - const [utxos, priceData] = await Promise.all([ - NitroWallet.fetchRawUtxos(nitroWallet.walletInfo.address.bech32, CONFIG.networkEnv), - LendingMarket.fetchAdaMinPrice(CONFIG.networkEnv), - ]); - invariant(nitroWallet.walletInfo.balance, "Nitro wallet balance is undefined"); - let mBought: bigint; - if (extra && extra.extraStatus === ExtraStatus.EXTRA_STEP_BUY_MORE) { - mBought = extra.buyMoreAmount; - } else { - mBought = 300_000_000n; - // mBought = nitroWallet.walletInfo.balance - LendingMarket.OpeningLongPosition.OPERATION_FEE_ADA; - } - if (mBought <= 0n) { - return { - ...position, - status: - extra && extra.extraStatus === ExtraStatus.EXTRA_STEP_BUY_MORE - ? extra.nextStatus - : LongPositionStatus.STEP_2_SUPPLY_TOKEN, - }; - } - const txHash = await LendingMarket.OpeningLongPosition.step1CreateOrder({ - nitroWallet: { - address: nitroWallet.walletInfo.address, - privateKey: nitroWallet.privateKey, - utxos, - submitTx: wallet.api.submitTx.bind(wallet.api), - }, - priceInAdaResponse: priceData, - networkEnv: CONFIG.networkEnv, - amountIn: mBought, - }); - console.log("Step 1 tx hash:", txHash); - const logStep = - extra && extra.extraStatus === ExtraStatus.EXTRA_STEP_BUY_MORE - ? extra.logStatus - : LongPositionStatus.STEP_1_BUY_LONG_ASSET; - return { - ...position, - amount: { - ...position.amount, - mBought: position.amount.mBought + mBought, - }, - updatedAt: Date.now(), - transactions: [...position.transactions, { txHash, step: logStep }], - hasCallback: 1, - }; -}; - -const handleStep2: InnerHandleFn = async ({ position, nitroWallet }) => { - if (position.hasCallback) { - await Helpers.sleep(10000); - const balance = await NitroWallet.fetchBalance(nitroWallet.walletInfo.address.bech32, CONFIG.networkEnv); - console.log("step 2 callback", XJSON.stringify(balance, 2)); - const qMinToken = Asset.fromString("186cd98a29585651c89f05807a876cf26cdf47a7f86f70be3b9e4cc0"); - let ok = balance.has(qMinToken); - const txHash = position.transactions[position.transactions.length - 1]?.txHash; - if (txHash) { - const txConfirmed = await Helpers.checkTxConfirmed(txHash); - console.log({ txConfirmed, txHash }); - ok = ok && txConfirmed; - } - if (ok) { - return { - ...position, - status: LongPositionStatus.STEP_3_BORROW_TOKEN, - amount: { - ...position.amount, - mSupplied: position.amount.mSupplied + balance.get(qMinToken), - }, - hasCallback: undefined, - callbackExtra: undefined, - }; - } else { - // continue callback - return { ...position, hasCallback: position.hasCallback + 1 }; - } - } - const utxos = await Helpers.fetchRawUtxos(nitroWallet.walletInfo.address); - const txHash = await LendingMarket.OpeningLongPosition.step2SupplyMIN({ - nitroWallet: { - address: nitroWallet.walletInfo.address, - privateKey: nitroWallet.privateKey, - utxos, - }, - minAmount: position.amount.mLongBalance, - networkEnv: CONFIG.networkEnv, - }); - console.log("Step 2 tx hash:", txHash); - return { - ...position, - updatedAt: Date.now(), - amount: { - ...position.amount, - mLongBalance: 0n, - }, - transactions: [...position.transactions, { txHash, step: LongPositionStatus.STEP_2_SUPPLY_TOKEN }], - hasCallback: 1, - }; -}; - -const handleStep3: InnerHandleFn = async ({ position, nitroWallet }) => { - if (position.hasCallback) { - await Helpers.sleep(10000); - const balance = await NitroWallet.fetchBalance(nitroWallet.walletInfo.address.bech32, CONFIG.networkEnv); - console.log("step 3 callback", XJSON.stringify(balance, 2)); - const qMinToken = Asset.fromString("186cd98a29585651c89f05807a876cf26cdf47a7f86f70be3b9e4cc0"); - let ok = !balance.has(qMinToken); - const txHash = position.transactions[position.transactions.length - 1]?.txHash; - if (txHash) { - const txConfirmed = await Helpers.checkTxConfirmed(txHash); - console.log({ txConfirmed, txHash }); - ok = ok && txConfirmed; - } - if (ok) { - return { - ...position, - status: LongPositionStatus.STEP_BUY_MORE_LONG_ASSET, - hasCallback: undefined, - callbackExtra: undefined, - }; - } else { - // continue callback - return { ...position, hasCallback: position.hasCallback + 1 }; - } - } - const utxos = await Helpers.fetchRawUtxos(nitroWallet.walletInfo.address); - const balance = Utxo.sumValue(utxos.map(Utxo.fromHex)); - const qMinToken = Asset.fromString("186cd98a29585651c89f05807a876cf26cdf47a7f86f70be3b9e4cc0"); - const qMinAmount = balance.get(qMinToken); - const collateralAmount = await LendingMarket.OpeningLongPosition.calculateWithdrawAllAmount({ - networkEnv: CONFIG.networkEnv, - address: nitroWallet.walletInfo.address, - marketId: "MIN", - qTokenAmount: Number(qMinAmount), - }); - const collaterals: LiqwidProvider.LoanCalculationInput["collaterals"] = [ - { - id: "Ada.186cd98a29585651c89f05807a876cf26cdf47a7f86f70be3b9e4cc0", - amount: Number(collateralAmount / 1e6), - }, - ]; - const buildTxCollaterals: LiqwidProvider.BorrowCollateral[] = [ - { - id: "qMIN", - amount: Number(qMinAmount), - }, - ]; - const { txHash, borrowAmount } = await LendingMarket.OpeningLongPosition.borrowAda({ - nitroWallet: { - address: nitroWallet.walletInfo.address, - privateKey: nitroWallet.privateKey, - utxos, - }, - borrowMarketId: "Ada", - currentDebt: 0, - collaterals, - buildTxCollaterals, - networkEnv: CONFIG.networkEnv, - forceBorrowAmount: Number(position.amount.iTotalBorrow), - }); - console.log("Step 3 tx hash:", txHash, borrowAmount); - return { - ...position, - updatedAt: Date.now(), - amount: { - ...position.amount, - mSupplied: 0n, - mBorrowed: position.amount.mBorrowed + borrowAmount, - }, - transactions: [...position.transactions, { txHash, step: LongPositionStatus.STEP_3_BORROW_TOKEN }], - hasCallback: 1, - }; -}; - -const handleStep4: InnerHandleFn = async ({ position, nitroWallet, wallet, extra }) => { - if (position.hasCallback) { - await Helpers.sleep(10000); - const balance = await NitroWallet.fetchBalance(nitroWallet.walletInfo.address.bech32, CONFIG.networkEnv); - console.log("step 4 callback", XJSON.stringify(balance, 2)); - const minToken = Asset.fromString(LendingMarket.mapMINToken[CONFIG.networkEnv]); - if (!balance.has(minToken)) { - return { - ...position, - status: - extra && extra.extraStatus === ExtraStatus.EXTRA_STEP_SELL_ALL - ? extra.nextStatus - : LongPositionStatus.STEP_5_REPAY_ASSET, - hasCallback: undefined, - callbackExtra: undefined, - }; - } else { - // continue callback - return { ...position, hasCallback: position.hasCallback + 1 }; - } - } - const [utxos, priceData] = await Promise.all([ - NitroWallet.fetchRawUtxos(nitroWallet.walletInfo.address.bech32, CONFIG.networkEnv), - LendingMarket.fetchAdaMinPrice(CONFIG.networkEnv), - ]); - const balance = Utxo.sumValue(utxos.map(Utxo.fromHex)); - console.log("step 4 balance", XJSON.stringify(balance, 2)); - const minToken = Asset.fromString(LendingMarket.mapMINToken[CONFIG.networkEnv]); - console.log("sell", balance.get(minToken)); - if (balance.get(minToken) <= 0n) { - return { - ...position, - status: - extra && extra.extraStatus === ExtraStatus.EXTRA_STEP_SELL_ALL - ? extra.nextStatus - : LongPositionStatus.STEP_5_REPAY_ASSET, - }; - } - const txHash = await LendingMarket.OpeningLongPosition.sellLongAsset({ - nitroWallet: { - address: nitroWallet.walletInfo.address, - privateKey: nitroWallet.privateKey, - utxos, - submitTx: wallet.api.submitTx.bind(wallet.api), - }, - priceInAdaResponse: priceData, - networkEnv: CONFIG.networkEnv, - amountIn: balance.get(minToken), - }); - console.log("selling tx hash:", txHash); - const logStatus = - extra && extra.extraStatus === ExtraStatus.EXTRA_STEP_SELL_ALL - ? extra.logStatus - : LongPositionStatus.STEP_4_SELL_LONG_ASSET; - return { - ...position, - updatedAt: Date.now(), - transactions: [...position.transactions, { txHash, step: logStatus }], - hasCallback: 1, - }; -}; - -// repay, close loan, receive qToken -const handleStep5: InnerHandleFn = async ({ position, nitroWallet }) => { - if (position.hasCallback) { - await Helpers.sleep(10000); - const balance = await NitroWallet.fetchBalance(nitroWallet.walletInfo.address.bech32, CONFIG.networkEnv); - console.log("step 5 callback", XJSON.stringify(balance, 2)); - const qMinToken = Asset.fromString("186cd98a29585651c89f05807a876cf26cdf47a7f86f70be3b9e4cc0"); - let ok = balance.has(qMinToken); - const txHash = position.transactions[position.transactions.length - 1]?.txHash; - if (txHash) { - const txConfirmed = await Helpers.checkTxConfirmed(txHash); - console.log({ txConfirmed, txHash }); - ok = ok && txConfirmed; - } - if (ok) { - return { - ...position, - status: LongPositionStatus.STEP_6_WITHDRAW_COLLATERAL, - hasCallback: undefined, - callbackExtra: undefined, - }; - } - } - const utxos = await Helpers.fetchRawUtxos(nitroWallet.walletInfo.address); - const txHash = await LendingMarket.OpeningLongPosition.repayAllDebt({ - nitroWallet: { - address: nitroWallet.walletInfo.address, - privateKey: nitroWallet.privateKey, - utxos, - }, - networkEnv: CONFIG.networkEnv, - }); - return { - ...position, - updatedAt: Date.now(), - transactions: [...position.transactions, { txHash, step: LongPositionStatus.STEP_5_REPAY_ASSET }], - hasCallback: 1, - }; -}; - -// withdraw collateral, pay qMIN, receive MIN -const handleStep6: InnerHandleFn = async ({ position, nitroWallet }) => { - if (position.hasCallback) { - await Helpers.sleep(10000); - const balance = await NitroWallet.fetchBalance(nitroWallet.walletInfo.address.bech32, CONFIG.networkEnv); - console.log("step 6 callback", XJSON.stringify(balance, 2)); - const minToken = Asset.fromString(LendingMarket.mapMINToken[CONFIG.networkEnv]); - let ok = balance.has(minToken); - const txHash = position.transactions[position.transactions.length - 1]?.txHash; - if (txHash) { - const txConfirmed = await Helpers.checkTxConfirmed(txHash); - console.log({ txConfirmed, txHash }); - ok = ok && txConfirmed; - } - if (ok) { - return { - ...position, - status: LongPositionStatus.STEP_SELL_ALL_LONG_ASSET, - hasCallback: undefined, - callbackExtra: undefined, - }; - } else { - // continue callback - return { ...position, hasCallback: position.hasCallback + 1 }; - } - } - const utxos = await Helpers.fetchRawUtxos(nitroWallet.walletInfo.address); - const balance = Utxo.sumValue(utxos.map(Utxo.fromHex)); - const qMinToken = Asset.fromString("186cd98a29585651c89f05807a876cf26cdf47a7f86f70be3b9e4cc0"); - const supplyAmount = balance.get(qMinToken); - const txHash = await LendingMarket.OpeningLongPosition.withdrawAllSupply({ - nitroWallet: { - address: nitroWallet.walletInfo.address, - privateKey: nitroWallet.privateKey, - utxos, - }, - networkEnv: CONFIG.networkEnv, - marketId: "MIN", - supplyQTokenAmount: Number(supplyAmount), - }); - console.log("Step 6: Withdrawing collateral from Liqwid", txHash); - return { - ...position, - updatedAt: Date.now(), - amount: { - ...position.amount, - mSupplied: 0n, - }, - transactions: [...position.transactions, { txHash, step: LongPositionStatus.STEP_6_WITHDRAW_COLLATERAL }], - hasCallback: 1, - }; -}; - -// MARK: Position Tab -export const PositionTab = () => { - const { message } = App.useApp(); - const wallet = useAtomValue(walletAtom); - const nitroWallet = useAtomValue(nitroWalletAtom); - const positions = useAtomValue(longPositionAtom); - const setPositions = useSetAtom(setLongPositionAtom); - - // Track positions that are currently being processed to prevent duplicate calls - const processingPositions = useRef>(new Set()); - - const handlePosition = useCallback( - async (input: HandlePositionInput) => { - const { position, errorMessage, successMessage, innerFn } = input; - if (!nitroWallet || !wallet) return; - // Check if this position is already being processed - if (processingPositions.current.has(position.positionId)) { - return; - } - // Mark position as being processed - processingPositions.current.add(position.positionId); - try { - let retries = 0; - const maxRetries = 3; - let lastError: Error | null = null; - - while (retries <= maxRetries) { - try { - const newPosition = await innerFn({ position, wallet, nitroWallet }); - setPositions((prev) => prev.map((p) => (p.positionId === position.positionId ? newPosition : p))); - if (!newPosition.hasCallback) { - message.success(`${successMessage} ${position.positionId.slice(0, 8)}`); - } - break; // Exit loop on success - } catch (error) { - lastError = error instanceof Error ? error : new Error(String(error)); - if (retries < maxRetries) { - retries++; - console.error("backoff error", error); - console.log( - `Retry ${retries}/${maxRetries} for position ${position.positionId.slice(0, 8)} after 30s...`, - ); - await Helpers.sleep(30000); // Sleep 30 seconds before retry - } else { - const txHash = position.transactions[position.transactions.length - 1]?.txHash; - if (txHash) { - const txConfirmed = await Helpers.checkTxConfirmed(txHash); - if (!txConfirmed) { - const prevStep = position.transactions[position.transactions.length - 2]?.step; - const lastStep = position.transactions[position.transactions.length - 1]?.step; - if (prevStep && lastStep && prevStep === lastStep) { - const transactions = [...position.transactions]; - transactions.pop(); // remove last failed tx - const newPosition = { ...position, hasCallback: 1, transactions }; - setPositions((prev) => prev.map((p) => (p.positionId === position.positionId ? newPosition : p))); - } - } - } - console.error("max retries reached for position:", position.positionId, lastError); - setTimeout(() => { - window.location.reload(); - }, 5000); - // throw lastError; - } - } - } - } catch (error) { - const errorMsg = error instanceof Error ? error.message : String(error); - message.error(`${errorMessage}: ${errorMsg}`); - console.error("handle position error: ", error); - } finally { - // Remove from processing set when done (success or error) - processingPositions.current.delete(position.positionId); - } - }, - [nitroWallet, wallet, setPositions, message], - ); - - useEffect(() => { - console.log("go here", { - date: new Date().toString(), - status: positions[0]?.status, - hasCallback: positions[0]?.hasCallback, - }); - for (const position of positions) { - if (position.status === LongPositionStatus.STEP_1_BUY_LONG_ASSET) { - handlePosition({ - position, - errorMessage: "Failed to buy long asset", - successMessage: "Successfully bought long asset for position", - innerFn: handleStep1, - }); - continue; - } - if (position.status === LongPositionStatus.STEP_2_SUPPLY_TOKEN) { - handlePosition({ - position, - errorMessage: "Failed to supply MIN tokens", - successMessage: "Successfully supplied MIN tokens for position", - innerFn: handleStep2, - }); - continue; - } - if (position.status === LongPositionStatus.STEP_3_BORROW_TOKEN) { - handlePosition({ - position, - errorMessage: "Failed to borrow tokens", - successMessage: "Successfully borrowed tokens for position", - innerFn: handleStep3, - }); - continue; - } - if (position.status === LongPositionStatus.STEP_BUY_MORE_LONG_ASSET) { - const buyMoreAmount = position.amount.iTotalBuy - position.amount.mBought; - handlePosition({ - position, - errorMessage: "Failed to buy more long asset", - successMessage: "Successfully bought more long asset for position", - innerFn: async (input) => { - return handleStep1({ - ...input, - extra: { - extraStatus: ExtraStatus.EXTRA_STEP_BUY_MORE, - buyMoreAmount, - nextStatus: LongPositionStatus.OPENING_POSITION, - logStatus: LongPositionStatus.STEP_BUY_MORE_LONG_ASSET, - }, - }); - }, - }); - continue; - } - if (position.status === LongPositionStatus.STEP_4_SELL_LONG_ASSET) { - handlePosition({ - position, - errorMessage: "Failed to sell long asset", - successMessage: "Successfully sold long asset for position", - innerFn: handleStep4, - }); - continue; - } - if (position.status === LongPositionStatus.STEP_5_REPAY_ASSET) { - handlePosition({ - position, - errorMessage: "Failed to close loan", - successMessage: "Successfully closed loan and repaid debt", - innerFn: handleStep5, - }); - continue; - } - if (position.status === LongPositionStatus.STEP_6_WITHDRAW_COLLATERAL) { - handlePosition({ - position, - errorMessage: "Failed to withdraw collateral", - successMessage: "Successfully withdrew collateral for position", - innerFn: handleStep6, - }); - continue; - } - if (position.status === LongPositionStatus.STEP_SELL_ALL_LONG_ASSET) { - handlePosition({ - position, - errorMessage: "Failed to sell all long asset", - successMessage: "Successfully sold all long asset for position", - innerFn: async (input) => { - return handleStep4({ - ...input, - extra: { - extraStatus: ExtraStatus.EXTRA_STEP_SELL_ALL, - nextStatus: LongPositionStatus.CLOSED_POSITION, - logStatus: LongPositionStatus.STEP_SELL_ALL_LONG_ASSET, - }, - }); - }, - }); - } - } - }, [positions, handlePosition]); - - const handleClosePosition = async (positionId: string) => { - try { - setPositions((prev) => - prev.map((p) => - p.positionId === positionId - ? { - ...p, - status: LongPositionStatus.STEP_4_SELL_LONG_ASSET, - updatedAt: Date.now(), - } - : p, - ), - ); - message.success("Position closing initiated"); - } catch (err) { - const errorMsg = err instanceof Error ? err.message : String(err); - message.error(`Failed to close position: ${errorMsg}`); - } - }; - - if (positions.length === 0) { - return ( - - ); - } - - const isPositionInProgress = ![LongPositionStatus.OPENING_POSITION, LongPositionStatus.CLOSED_POSITION].includes( - positions[0]?.status ?? LongPositionStatus.STEP_1_BUY_LONG_ASSET, - ); - - return ( -
- - {isPositionInProgress && ( - - )} - - - - - {positions.map((position) => { - const { pnl, pnlPercent } = calculatePnL(position); - const isPositionComplete = position.status === LongPositionStatus.OPENING_POSITION; - - return ( - } - onClick={() => handleClosePosition(position.positionId)} - type="text" - > - Close Position - - ) - } - key={position.positionId} - style={{ - border: isPositionComplete ? "2px solid #52c41a" : "1px solid #d9d9d9", - boxShadow: isPositionComplete ? "0 4px 12px rgba(82, 196, 26, 0.15)" : undefined, - }} - title={ - - - Position #{position.positionId.slice(0, 8)} - {getStatusText(position.status)} - - } - > - {/* Progress Bar */} - - {/* Basic Position Info */} - - - } - title="Leverage" - value={`${position.leverage}x`} - valueStyle={{ color: "#1890ff", fontWeight: "bold" }} - /> - - - } - title="Long Asset" - value={position.longAsset.tokenName.toString()} - /> - - - - - - = 0 ? "+" : ""} - suffix="ADA" - title="P&L" - value={pnl} - valueStyle={{ - color: pnl >= 0 ? "#3f8600" : "#cf1322", - fontWeight: "bold", - }} - /> - - - - {/* Financial Details */} - - - - - - - - - - - - - - - {/* Fees */} - - - - - - - - - = 0 ? "+" : ""} - suffix="%" - title="P&L %" - value={pnlPercent} - valueStyle={{ - fontSize: "12px", - color: pnlPercent >= 0 ? "#3f8600" : "#cf1322", - fontWeight: "bold", - }} - /> - - {" "} - {/* Transaction History */} - {position.transactions.length > 0 && ( - <> - -
-

Transaction History:

- - {position.transactions.map((tx) => ( -
-
- {getStatusText(tx.step)} -
-
- Tx: {tx.txHash} -
-
- ))} -
-
- - )} - {/* Timestamps */} -
- Created: {new Date(position.createdAt).toLocaleString()} - {position.updatedAt !== position.createdAt && ( - Updated: {new Date(position.updatedAt).toLocaleString()} - )} -
-
- ); - })} -
-
- ); -}; diff --git a/apps/web/app/components/margin-trading-trade-tab.tsx b/apps/web/app/components/margin-trading-trade-tab.tsx deleted file mode 100644 index dcce637..0000000 --- a/apps/web/app/components/margin-trading-trade-tab.tsx +++ /dev/null @@ -1,440 +0,0 @@ -"use client"; - -import { ArrowRightOutlined, InfoCircleOutlined } from "@ant-design/icons"; -import { DexV2Calculation, OrderV2Direction } from "@minswap/felis-dex-v2"; -import { ADA, Asset, Bytes } from "@minswap/felis-ledger-core"; -import { sha3 } from "@minswap/felis-ledger-utils"; -import { LendingMarket } from "@minswap/felis-lending-market"; -import invariant from "@minswap/tiny-invariant"; -import { - Alert, - App, - Button, - Card, - Col, - Divider, - Form, - InputNumber, - Radio, - Row, - Slider, - Space, - Statistic, - Tooltip, -} from "antd"; -import { useAtomValue, useSetAtom } from "jotai"; -import { useCallback, useEffect, useState } from "react"; -import { - type LongPositionState, - LongPositionStatus, - longPositionAtom, - minAdaPriceAtom, - nitroBalanceAtom, - nitroWalletAtom, - setLongPositionAtom, - walletAtom, -} from "../atoms/walletAtom"; -import { CONFIG } from "../config"; - -// POC Configuration -const MIN_REQUIRED_ADA = 312_000_000n; -const HARDCODED_TOTAL_ADA = 450; -const HARDCODED_LEVERAGE = 1.5; - -// MARK: Trade Tab -export const TradeTab = () => { - const { message } = App.useApp(); - const nitroWallet = useAtomValue(nitroWalletAtom); - const wallet = useAtomValue(walletAtom); - const positions = useAtomValue(longPositionAtom); - const setPositions = useSetAtom(setLongPositionAtom); - const nitroBalance = useAtomValue(nitroBalanceAtom); - - // Trading parameters - const [leverage, setLeverage] = useState<1 | 1.5>(HARDCODED_LEVERAGE as 1.5); - const [marginMode, _setMarginMode] = useState<"isolated">("isolated"); - const [autoBorrowRepay, setAutoBorrowRepay] = useState(true); - - // Input values - const [adaAmount, setAdaAmount] = useState(HARDCODED_TOTAL_ADA); - const [minAmount, setMinAmount] = useState(0); - const [sliderValue, setSliderValue] = useState(100); - const minAdaPrice = useAtomValue(minAdaPriceAtom); - - // Loading states - const [loading, setLoading] = useState(false); - // const _hasEnoughBalance = nitroBalance >= MIN_REQUIRED_ADA; - - const handleAmountChange = (value: number | null) => { - if (value === null) return; - setMinAmount(value); - }; - - const handleAdaAmountChange = useCallback( - (value: number | null) => { - if (value === null) return; - // const percentage = (value / Number(MIN_REQUIRED_ADA)) * 100; - setSliderValue(100); - - if (minAdaPrice) { - try { - const amountInLovelace = BigInt(Math.floor(value * 1_000_000)); - const result = DexV2Calculation.calculateSwapExactIn({ - datumReserves: [BigInt(minAdaPrice.datumReserves[0]), BigInt(minAdaPrice.datumReserves[1])], - valueReserves: [BigInt(minAdaPrice.valueReserves[0]), BigInt(minAdaPrice.valueReserves[1])], - tradingFee: { - feeANumerator: BigInt(minAdaPrice.tradingFee.feeANumerator), - feeBNumerator: BigInt(minAdaPrice.tradingFee.feeBNumerator), - }, - amountIn: amountInLovelace, - direction: OrderV2Direction.A_TO_B, - feeSharingNumerator: minAdaPrice.feeSharingNumerator ? BigInt(minAdaPrice.feeSharingNumerator) : null, - }); - - const minAmountReceived = Number(result.amountOut) / 1_000_000; - setMinAmount(minAmountReceived); - } catch (err) { - console.error("Failed to calculate MIN amount:", err); - } - } - }, - [minAdaPrice], - ); - - useEffect(() => { - if (nitroBalance && nitroBalance >= MIN_REQUIRED_ADA) { - handleAdaAmountChange(300); - } - }, [nitroBalance, handleAdaAmountChange]); - - // const handleSliderChange = (value: number) => { - // setSliderValue(value); - // if (minAdaPrice) { - // try { - // const amountInLovelace = BigInt(Math.floor(totalAda * 1_000_000)); - - // const result = DexV2Calculation.calculateSwapExactIn({ - // datumReserves: [BigInt(minAdaPrice.datumReserves[0]), BigInt(minAdaPrice.datumReserves[1])], - // valueReserves: [BigInt(minAdaPrice.valueReserves[0]), BigInt(minAdaPrice.valueReserves[1])], - // tradingFee: { - // feeANumerator: BigInt(minAdaPrice.tradingFee.feeANumerator), - // feeBNumerator: BigInt(minAdaPrice.tradingFee.feeBNumerator), - // }, - // amountIn: amountInLovelace, - // direction: OrderV2Direction.A_TO_B, - // feeSharingNumerator: minAdaPrice.feeSharingNumerator ? BigInt(minAdaPrice.feeSharingNumerator) : null, - // }); - - // const minAmountReceived = Number(result.amountOut) / 1_000_000; - // setMinAmount(minAmountReceived); - // } catch (err) { - // console.error("Failed to calculate MIN amount:", err); - // } - // } - // }; - - const borrowAmount = 150; - const shouldBorrow = borrowAmount > 0; - const adaDebt = shouldBorrow ? borrowAmount : 0; - - const calculateLiquidationPrice = (): number => { - if (adaDebt <= 0) return 0; - return adaDebt / minAmount; - }; - - const _liquidationPrice = calculateLiquidationPrice(); - - const handleBuy = async () => { - invariant(wallet && nitroWallet); - if (adaAmount <= 0) { - message.error("Please enter ADA amount"); - return; - } - - if (!minAdaPrice) { - message.error("Price data not available"); - return; - } - - setLoading(true); - try { - const now = Date.now(); - const newLongPositionState: LongPositionState = { - positionId: sha3(Bytes.fromString(`${nitroWallet.walletInfo.address.bech32}.${now}`).hex), - status: LongPositionStatus.STEP_1_BUY_LONG_ASSET, - nitroWalletAddress: nitroWallet.walletInfo.address.bech32, - leverage, - longAsset: Asset.fromString(LendingMarket.mapMINToken[CONFIG.networkEnv]), // MIN - borrowAsset: ADA, - createdAt: Date.now(), - updatedAt: Date.now(), - amount: { - iTotalBuy: BigInt(Math.floor(adaAmount * 1e6)), - iTotalBorrow: 150_000_000n, - iTotalOperationFee: LendingMarket.OpeningLongPosition.OPERATION_FEE_ADA, - mTotalPaidFee: 0n, - mBought: 0n, - mTotalLong: 0n, - mLongBalance: 0n, - mBorrowed: 0n, - mSupplied: 0n, - mRepaid: 0n, - mWithdrawn: 0n, - }, - transactions: [], - }; - setPositions([newLongPositionState]); - message.success("Position opened successfully!"); - setAdaAmount(0); - setMinAmount(0); - setSliderValue(0); - } catch (err) { - const errorMsg = err instanceof Error ? err.message : String(err); - message.error(`Failed to open position: ${errorMsg}`); - console.error("Buy error:", err); - } finally { - setLoading(false); - } - }; - - return ( - - {/* POC Version Warning */} - {nitroWallet?.walletInfo && nitroBalance >= MIN_REQUIRED_ADA ? ( - - ) : null} - - {nitroWallet?.walletInfo && nitroBalance < MIN_REQUIRED_ADA ? ( - - ) : null} - - {/* Mode Selection */} - - -
Margin Mode
- - - -
Leverage
- setLeverage(e.target.value)} style={{ width: "100%" }} value={leverage}> - - 1.00x - - 1.50x - - - -
Borrow/Repay
- setAutoBorrowRepay(e.target.value === "auto")} - style={{ width: "100%" }} - value={autoBorrowRepay ? "auto" : "manual"} - > - Auto - - Manual - - - - -
Order Type
- - -
- - - - {/* Market Price Info */} - - - - - - - - - - - - - - {/* Amount Inputs */} -
- - - - - - - - - - - - - - - -
- - {/* Slider */} -
-
- Margin Slider - {sliderValue.toFixed(0)}% -
- {}} - value={sliderValue} - /> -
- - - - {/* Trading Info */} - - - - - Avbl - - } - value={((Number(nitroBalance) ?? 0) / 1_000_000).toFixed(2)} - valueStyle={{ fontSize: 14 }} - /> - - - - - - Max - - } - value={(((Number(nitroBalance) ?? 0) / 1_000_000) * leverage).toFixed(2)} - valueStyle={{ fontSize: 14 }} - /> - - - {/* - - - Liq.Price - - } - value={liquidationPrice.toFixed(4)} - valueStyle={{ fontSize: 14 }} - /> - - */} - - - {/* Borrow/Repay Info */} - {shouldBorrow && ( - <> - - - - - - Borrow - - } - value={borrowAmount.toFixed(2)} - valueStyle={{ fontSize: 14 }} - /> - - - - - - Repay - - } - value={borrowAmount.toFixed(2)} - valueStyle={{ fontSize: 14 }} - /> - - - - - )} - - {/* Buy Button */} - -
- ); -}; diff --git a/apps/web/app/components/margin-trading.tsx b/apps/web/app/components/margin-trading.tsx deleted file mode 100644 index fdf493f..0000000 --- a/apps/web/app/components/margin-trading.tsx +++ /dev/null @@ -1,43 +0,0 @@ -"use client"; - -import { Alert, Badge, Card, Tabs } from "antd"; -import { useAtomValue } from "jotai"; -import { longPositionAtom, nitroWalletAtom } from "../atoms/walletAtom"; -import { PositionTab } from "./margin-trading-position-tab"; -import { TradeTab } from "./margin-trading-trade-tab"; - -export const MarginTrading = () => { - const nitroWallet = useAtomValue(nitroWalletAtom); - const positions = useAtomValue(longPositionAtom); - const hasActivePositions = (positions ?? []).length > 0; - - return ( - } - style={{ marginTop: 16 }} - title="Isolated Margin Long $MIN" - > - {!nitroWallet && ( - - )} - , - }, - ]), - { - key: "position", - label: `Position${hasActivePositions ? ` (${(positions ?? []).length})` : ""}`, - children: , - }, - ]} - /> - - ); -}; diff --git a/apps/web/app/components/nitro-wallet-connector.tsx b/apps/web/app/components/nitro-wallet-connector.tsx deleted file mode 100644 index a52a730..0000000 --- a/apps/web/app/components/nitro-wallet-connector.tsx +++ /dev/null @@ -1,252 +0,0 @@ -"use client"; - -import { CopyOutlined, LogoutOutlined } from "@ant-design/icons"; -import { Address } from "@minswap/felis-ledger-core"; -import { NitroWallet } from "@minswap/felis-lending-market"; -import invariant from "@minswap/tiny-invariant"; -import { Alert, App, Button, Card, Col, Row, Space, Statistic, Tooltip } from "antd"; -import { useAtom, useAtomValue, useSetAtom } from "jotai"; -import { useCallback, useEffect, useState } from "react"; -import { - type NitroWalletData, - nitroBalanceAtom, - nitroWalletAtom, - setNitroBalanceAtom, - setNitroWalletAtom, - walletAtom, -} from "../atoms/walletAtom"; -import { CONFIG } from "../config"; -import { LocalStorageKey } from "../constants/storage"; -import { Utils } from "../lib/utils"; - -export const NitroWalletConnector = () => { - const { message } = App.useApp(); - const nitroWallet = useAtomValue(nitroWalletAtom); - const wallet = useAtomValue(walletAtom); - const [_, setNitroWallet] = useAtom(setNitroWalletAtom); - const [loading, setLoading] = useState(false); - const nitroBalance = useAtomValue(nitroBalanceAtom); - const setNitroBalance = useSetAtom(setNitroBalanceAtom); - - /** - * Fetch balance from the blockchain for a given address - */ - const fetchBalance = useCallback( - async (address: Address): Promise => { - try { - const value = await NitroWallet.fetchBalance(address.bech32, CONFIG.networkEnv); - setNitroBalance(value.coin()); - return value.coin(); - } catch (err) { - const errorMsg = err instanceof Error ? err.message : String(err); - message.error(`Failed to fetch balance: ${errorMsg}`); - return 0n; - } - }, - [message.error, setNitroBalance], - ); - - const loadFromStorage = useCallback(async () => { - const rootAddress = wallet?.walletInfo?.address.bech32; - if (!rootAddress) { - return false; - } - try { - const stored = localStorage.getItem(LocalStorageKey.NITRO_WALLET); - if (stored) { - const parsedData = JSON.parse(stored); - const nitroWallet = { - ...parsedData, - walletInfo: { - ...parsedData.walletInfo, - address: Address.fromBech32(parsedData.walletInfo.address), - }, - }; - if (nitroWallet.walletInfo.rootAddress !== rootAddress) { - return false; - } - - // Fetch real-time balance from blockchain - const balance = await fetchBalance(nitroWallet.walletInfo.address); - // Create updated wallet with fresh balance - const updatedNitroWallet = { - ...nitroWallet, - walletInfo: { - ...nitroWallet.walletInfo, - balance: balance, - }, - }; - - setNitroWallet(updatedNitroWallet); - return true; - } - return false; - } catch (err) { - const errorMsg = err instanceof Error ? err.message : String(err); - message.error(`Failed to load Nitro wallet from storage: ${errorMsg}`); - return false; - } - }, [wallet?.walletInfo, fetchBalance, setNitroWallet, message.error]); - - useEffect(() => { - if (wallet?.walletInfo) { - loadFromStorage(); - } - }, [wallet?.walletInfo, loadFromStorage]); - - const handleCopyAddress = async (address: string) => { - try { - await navigator.clipboard.writeText(address); - message.success("Address copied!"); - } catch (_err) { - message.error("Failed to copy address"); - } - }; - - /** - * Save Nitro wallet to localStorage - */ - const saveToStorage = useCallback( - (data: NitroWalletData) => { - try { - localStorage.setItem(LocalStorageKey.NITRO_WALLET, JSON.stringify(data)); - } catch (err) { - const errorMsg = err instanceof Error ? err.message : String(err); - message.error(`Failed to save Nitro wallet: ${errorMsg}`); - } - }, - [message.error], - ); - - /** - * Create new Nitro wallet using wallet.api.signData() - * Requires connected wallet (wallet.api must exist) - */ - const createNewWallet = useCallback(async () => { - invariant(wallet?.api && wallet.walletInfo); - try { - const nitroWalletData = await NitroWallet.createNitroWallet({ - rootAddress: wallet.walletInfo.address.bech32, - networkId: wallet.walletInfo.networkId, - signData: wallet.api.signData.bind(wallet.api), - }); - const balance = await fetchBalance(nitroWalletData.walletInfo.address); - setNitroWallet({ ...nitroWalletData, walletInfo: { ...nitroWalletData.walletInfo, balance } }); - return nitroWalletData; - } catch (err) { - const errorMsg = err instanceof Error ? err.message : String(err); - throw new Error(`Failed to create new Nitro wallet: ${errorMsg}`); - } - }, [wallet?.walletInfo, setNitroWallet, wallet?.api, fetchBalance]); - - const connect = useCallback(async () => { - setLoading(true); - try { - // Try to load from localStorage first - const loaded = await loadFromStorage(); - if (loaded) { - return; - } - - // If not in storage, create new wallet - const nitroWallet = await createNewWallet(); - saveToStorage(nitroWallet); - } catch (err) { - const errorMsg = err instanceof Error ? err.message : String(err); - message.error(`Failed to connect Nitro wallet: ${errorMsg}`); - } finally { - setLoading(false); - } - }, [createNewWallet, loadFromStorage, message.error, saveToStorage]); - - /** - * Disconnect and remove from localStorage - */ - const disconnect = useCallback(() => { - try { - localStorage.removeItem(LocalStorageKey.NITRO_WALLET); - setNitroWallet(null); - } catch (err) { - const errorMsg = err instanceof Error ? err.message : String(err); - message.error(`Failed to disconnect Nitro wallet: ${errorMsg}`); - } - }, [message.error, setNitroWallet]); - - if (nitroWallet) { - const { walletInfo } = nitroWallet; - return ( - } onClick={disconnect} type="text"> - Disconnect - - } - headStyle={{ color: "white", borderColor: "#764ba2" }} - style={{ - marginTop: 16, - background: "linear-gradient(135deg, #764ba2 0%, #667eea 100%)", - borderColor: "#764ba2", - }} - title="Nitro Wallet Connected" - > - - - - - -
-
Nitro Address
- - - -
- -
-
- ); - } - - return ( - - - {!wallet?.walletInfo && ( - - )} - {wallet?.walletInfo && !nitroWallet && ( - - - - - )} - {nitroWallet && ( - - )} - - - ); -}; diff --git a/apps/web/app/components/short-trading-position-tab.tsx b/apps/web/app/components/short-trading-position-tab.tsx deleted file mode 100644 index d3f8793..0000000 --- a/apps/web/app/components/short-trading-position-tab.tsx +++ /dev/null @@ -1,731 +0,0 @@ -"use client"; - -import { CloseOutlined, DollarOutlined, SwapOutlined, TrophyOutlined } from "@ant-design/icons"; -import { DexV2Calculation, OrderV2Direction } from "@minswap/felis-dex-v2"; -import { Address, Asset } from "@minswap/felis-ledger-core"; -import { LendingMarket, LiqwidProvider } from "@minswap/felis-lending-market"; -import invariant from "@minswap/tiny-invariant"; -import { Alert, App, Button, Card, Col, Divider, Progress, Row, Space, Statistic, Tag } from "antd"; -import { useAtomValue, useSetAtom } from "jotai"; -import { useCallback, useEffect, useState } from "react"; -import { - minAdaPriceAtom, - nitroWalletAtom, - type ShortPositionState, - ShortPositionStatus, - ShortPositionStatus as ShortPositionStatusEnum, - setShortPositionAtom, - shortPositionAtom, -} from "../atoms/walletAtom"; -import { CONFIG } from "../config"; -import { - ExtraStatus, - shortHandleStep1, - shortHandleStep2, - shortHandleStep3, - shortHandleStep4, - shortHandleStep5, - shortHandleStep6, - usePosition, -} from "../lib/use-position"; -import { Utils } from "../lib/utils"; - -const getStatusColor = (status: ShortPositionStatus) => { - switch (status) { - case ShortPositionStatusEnum.STEP_1_SUPPLY_TOKEN: - return "processing"; - case ShortPositionStatus.STEP_2_BORROW_TOKEN: - return "purple"; - case ShortPositionStatus.STEP_3_SHORT_TOKEN: - return "magenta"; - case ShortPositionStatusEnum.OPENING_POSITION: - return "success"; - case ShortPositionStatusEnum.STEP_4_BUY_BACK_TOKEN: - return "orange"; - case ShortPositionStatusEnum.STEP_5_REPAY_ASSET: - return "geekblue"; - case ShortPositionStatusEnum.STEP_6_WITHDRAW_COLLATERAL: - return "cyan"; - case ShortPositionStatusEnum.CLOSED_POSITION: - return "red"; - default: - return "default"; - } -}; - -const getStatusText = (status: ShortPositionStatus) => { - switch (status) { - case ShortPositionStatusEnum.STEP_1_SUPPLY_TOKEN: - return "Supplying Token"; - case ShortPositionStatusEnum.STEP_2_BORROW_TOKEN: - return "Borrowing Token"; - case ShortPositionStatusEnum.STEP_3_SHORT_TOKEN: - return "Short Token"; - case ShortPositionStatusEnum.STEP_4_BUY_BACK_TOKEN: - return "Buying Back Token"; - case ShortPositionStatusEnum.STEP_5_REPAY_ASSET: - return "Repaying Asset"; - case ShortPositionStatusEnum.STEP_6_WITHDRAW_COLLATERAL: - return "Withdrawing Collateral"; - default: - return status.replace(/_/g, " "); - } -}; - -const getProgressPercent = (_position: ShortPositionState) => { - if (_position.amount.mShortedL < 0n) { - return 50; - } - return 100; -}; - -const calculatePnlA = (options: { - mClosedAda: bigint; - minAdaPrice: LendingMarket.PriceInAdaResponse | null; - shortReceivedL: bigint; - totalRepay: bigint; - supplyEarned: number; -}): number => { - const { mClosedAda, minAdaPrice, shortReceivedL, totalRepay, supplyEarned } = options; - if (mClosedAda > 0n) { - const pnlA = (Number(shortReceivedL) - Number(mClosedAda)) / 1e6 + supplyEarned; - return pnlA; - } - if (!minAdaPrice) { - return 0; - } - const result = DexV2Calculation.calculateSwapExactOut({ - datumReserves: [BigInt(minAdaPrice.datumReserves[0]), BigInt(minAdaPrice.datumReserves[1])], - valueReserves: [BigInt(minAdaPrice.valueReserves[0]), BigInt(minAdaPrice.valueReserves[1])], - tradingFee: { - feeANumerator: BigInt(minAdaPrice.tradingFee.feeANumerator), - feeBNumerator: BigInt(minAdaPrice.tradingFee.feeBNumerator), - }, - expectedReceive: totalRepay, - direction: OrderV2Direction.A_TO_B, - feeSharingNumerator: minAdaPrice.feeSharingNumerator ? BigInt(minAdaPrice.feeSharingNumerator) : null, - }); - if (result.type === "err") { - return 0; - } - const buyBackL = result.value.necessaryAmountIn; - const pnlL = Number(shortReceivedL) - Number(buyBackL); - const pnlA = pnlL / 1e6 + supplyEarned; - return pnlA; -}; - -// MARK: Short Position Tab -export const ShortPositionTab = () => { - const { message } = App.useApp(); - const nitroWallet = useAtomValue(nitroWalletAtom); - const positions = useAtomValue(shortPositionAtom); - const setShortPositions = useSetAtom(setShortPositionAtom); - const positionHandler = usePosition(); - const [supplyApy, setSupplyApy] = useState(0); - const [borrowApy, setBorrowApy] = useState(0); - const [borrowInterest, setBorrowInterest] = useState(0); - const [supplyInterest, setSupplyInterest] = useState(0); - // const [liquidationCall, setLiquidationCall] = useState(false); - - const minAdaPrice = useAtomValue(minAdaPriceAtom); - - useEffect(() => { - const getSupplyApy = async (paymentKey: string) => { - const result = await LiqwidProvider.getNetApy({ - input: { - paymentKeys: [paymentKey], - supplies: [ - { - marketId: "Ada", - amount: 1000, - }, - ], - currency: "USD", - }, - networkEnv: CONFIG.networkEnv, - }); - if (result.type === "err") { - return 0; - } else { - return Math.floor(result.value.supplyApy * 100 * 100) / 100; - } - }; - if (nitroWallet) { - const pkh = nitroWallet.walletInfo.address.toPubKeyHash()?.keyHash.hex; - if (pkh) { - getSupplyApy(pkh).then((apy) => setSupplyApy(Number(apy))); - } - } - }, [nitroWallet]); - - // polling data - useEffect(() => { - if (!nitroWallet) { - return; - } - const paymentKey = nitroWallet?.walletInfo.address.toPubKeyHash()?.keyHash.hex; - if (!paymentKey) { - return; - } - const fetchData = async () => { - const loansData = await LiqwidProvider.getLoansBorrow({ - input: { - paymentKeys: [paymentKey], - }, - networkEnv: CONFIG.networkEnv, - }); - if (loansData.type === "ok") { - const loan = loansData.value[loansData.value.length - 1]; - if (loan) { - setBorrowApy(loan.APY * 100); - setBorrowInterest(loan.interest); - } - } - - const supplyEarned = await LiqwidProvider.getYieldEarned({ - input: { addresses: [nitroWallet.walletInfo.address.bech32] }, - networkEnv: CONFIG.networkEnv, - }); - if (supplyEarned.type === "ok") { - for (const market of supplyEarned.value.markets) { - if (market.id === "Ada") { - setSupplyInterest(market.amount); - } - } - } - }; - - fetchData(); - const interval = setInterval(fetchData, 10000); // 10 seconds - return () => clearInterval(interval); - }, [nitroWallet]); - - useEffect(() => { - console.log("go here", { - date: new Date().toString(), - status: positions[0]?.status, - hasCallback: positions[0]?.hasCallback, - }); - for (const position of positions) { - if (position.status === ShortPositionStatusEnum.STEP_1_SUPPLY_TOKEN) { - positionHandler.handlePosition({ - position, - errorMessage: "Failed to supply asset", - successMessage: "Successfully Supplied asset for position", - positionType: "short", - innerFn: async (input) => { - if (input.position.hasCallback && input.position.hasCallback >= 16) { - const transactions = [...input.position.transactions]; - transactions.pop(); - return { - ...input.position, - amount: { - ...position.amount, - mSuppliedL: 0n, - }, - transactions, - status: ShortPositionStatusEnum.STEP_1_SUPPLY_TOKEN, - hasCallback: undefined, - callbackExtra: undefined, - } as ShortPositionState; - } - return shortHandleStep1({ - ...input, - extra: { - extraStatus: ExtraStatus.EXTRA_SHORT_STEP_1, - marketId: "Ada", - amount: position.amount.iTotalSupplyL, - }, - }); - }, - }); - } else if (position.status === ShortPositionStatusEnum.STEP_2_BORROW_TOKEN) { - positionHandler.handlePosition({ - position, - errorMessage: "Failed to borrow asset", - successMessage: "Successfully borrowed asset for position", - positionType: "short", - innerFn: async (input) => { - if (input.position.hasCallback && input.position.hasCallback >= 16) { - const transactions = [...input.position.transactions]; - transactions.pop(); - return { - ...input.position, - amount: { - ...position.amount, - mBorrowedL: 0n, - }, - transactions, - status: ShortPositionStatusEnum.STEP_2_BORROW_TOKEN, - hasCallback: undefined, - callbackExtra: undefined, - } as ShortPositionState; - } - return shortHandleStep2({ - ...input, - extra: { - extraStatus: ExtraStatus.EXTRA_SHORT_STEP_2, - borrowAmountL: Number(position.amount.iBorrowAmountL), - collateralMode: { - mode: LendingMarket.CollateralMode.ISOLATED_MARGIN, - borrowMarketId: "MIN", - supplyMarketId: "Ada", - }, - borrowMarketId: "MIN", - }, - }); - }, - }); - } else if (position.status === ShortPositionStatusEnum.STEP_3_SHORT_TOKEN) { - positionHandler.handlePosition({ - position, - errorMessage: "Failed to short asset", - successMessage: "Successfully shorted asset for position", - positionType: "short", - innerFn: async (input) => { - const priceData = await LendingMarket.fetchAdaMinPrice(CONFIG.networkEnv); - return shortHandleStep3({ - ...input, - extra: { - extraStatus: ExtraStatus.EXTRA_SHORT_STEP_3, - priceInAdaResponse: priceData, - amountIn: position.amount.mBorrowedL, - }, - }); - }, - }); - } else if (position.status === ShortPositionStatusEnum.STEP_4_BUY_BACK_TOKEN) { - positionHandler.handlePosition({ - position, - errorMessage: "Failed to buy back asset", - successMessage: "Successfully bought back asset for position", - positionType: "short", - innerFn: async (input) => { - const priceData = await LendingMarket.fetchAdaMinPrice(CONFIG.networkEnv); - let loanTxHash: string | null = null; - for (const { txHash, step } of position.transactions) { - if (step === ShortPositionStatusEnum.STEP_2_BORROW_TOKEN) { - loanTxHash = txHash; - } - } - invariant(loanTxHash, "Loan transaction hash not found"); - const totalRepay = await LendingMarket.calculateRepayAllAmountL({ - networkEnv: CONFIG.networkEnv, - address: Address.fromBech32(position.nitroWalletAddress), - loanTxHash, - }); - return shortHandleStep4({ - ...input, - extra: { - extraStatus: ExtraStatus.EXTRA_SHORT_STEP_4, - priceInAdaResponse: priceData, - expectedReceive: totalRepay + totalRepay / 1000n, - }, - }); - }, - }); - } else if (position.status === ShortPositionStatusEnum.STEP_5_REPAY_ASSET) { - positionHandler.handlePosition({ - position, - errorMessage: "Failed to repay asset", - successMessage: "Successfully repaid asset for position", - positionType: "short", - innerFn: shortHandleStep5, - }); - } else if (position.status === ShortPositionStatusEnum.STEP_6_WITHDRAW_COLLATERAL) { - positionHandler.handlePosition({ - position, - errorMessage: "Failed to withdraw collateral", - successMessage: "Successfully withdrew collateral for position", - positionType: "short", - innerFn: async (input) => { - return shortHandleStep6({ - ...input, - extra: { - extraStatus: ExtraStatus.EXTRA_SHORT_STEP_6, - supplyMarketId: "Ada", - qToken: Asset.fromString(LiqwidProvider.mapQAdaToken[CONFIG.networkEnv]), - }, - }); - }, - }); - } - } - }, [positions, positionHandler]); // IMPORTANT: Don't add more - - const handleClosePosition = useCallback( - (positionId: string, liquidationCall?: boolean) => { - try { - setShortPositions((prev) => - prev.map((p) => - p.positionId === positionId - ? { - ...p, - status: ShortPositionStatusEnum.STEP_4_BUY_BACK_TOKEN, - updatedAt: Date.now(), - liquidationCall: liquidationCall, - } - : p, - ), - ); - message.success("Position closing initiated"); - } catch (err) { - const errorMsg = err instanceof Error ? err.message : String(err); - message.error(`Failed to close position: ${errorMsg}`); - } - }, - [setShortPositions, message], - ); - - // liquidation notice and POC notice - useEffect(() => { - const lastPrice = minAdaPrice?.price; - if (!lastPrice) { - return; - } - for (const position of positions) { - const liqPrice = position.amount.iLiquidationPrice; - if (Number(lastPrice) >= liqPrice && position.liquidationCall !== true) { - handleClosePosition(position.positionId, true); - message.warning( - `Position #${position.positionId.slice( - 0, - 8, - )} has been liquidated as the market price reached the liquidation price of ${liqPrice} ADA/MIN.`, - ); - } - } - }, [minAdaPrice, positions, handleClosePosition, message]); - - if (positions.length === 0) { - return ( - - ); - } - - const isPositionInProgress = positions.some( - (p) => - [ShortPositionStatusEnum.OPENING_POSITION, ShortPositionStatusEnum.CLOSED_POSITION].includes(p.status) === false, - ); - - // const adhoc = (positionId: string) => { - // setShortPositions((prev) => - // prev.map((p) => - // p.positionId === positionId - // ? { - // ...p, - // status: ShortPositionStatusEnum.STEP_6_WITHDRAW_COLLATERAL, - // hasCallback: 0, - // callbackExtra: undefined, - // } - // : p, - // ), - // ); - // }; - - return ( -
- - {isPositionInProgress && ( - - )} - - - - - - {positions.map((position) => { - let pnlA = 0; - if ( - [ - ShortPositionStatusEnum.STEP_1_SUPPLY_TOKEN, - ShortPositionStatus.STEP_2_BORROW_TOKEN, - ShortPositionStatusEnum.STEP_3_SHORT_TOKEN, - ].includes(position.status) - ) { - pnlA = 0; - } else { - pnlA = calculatePnlA({ - minAdaPrice: minAdaPrice, - shortReceivedL: position.amount.mShortedEstimateAda, - totalRepay: position.amount.mBorrowedL + BigInt(Math.floor(borrowInterest * 1e6)), - supplyEarned: supplyInterest, - mClosedAda: position.amount.mClosedAda ?? 0n, - }); - } - const isPositionComplete = position.status === ShortPositionStatusEnum.OPENING_POSITION; - - return ( - } - onClick={() => handleClosePosition(position.positionId)} - type="text" - > - Close Position - - ) - } - key={position.positionId} - style={{ - border: isPositionComplete ? "2px solid #52c41a" : "1px solid #d9d9d9", - boxShadow: isPositionComplete ? "0 4px 12px rgba(82, 196, 26, 0.15)" : undefined, - }} - title={ - - - Position #{position.positionId.slice(0, 8)} - {getStatusText(position.status)} - - } - > - {/*
- ); -}; diff --git a/apps/web/app/components/short-trading-trade-tab.tsx b/apps/web/app/components/short-trading-trade-tab.tsx deleted file mode 100644 index 8e9b428..0000000 --- a/apps/web/app/components/short-trading-trade-tab.tsx +++ /dev/null @@ -1,280 +0,0 @@ -"use client"; - -import { ArrowRightOutlined, InfoCircleOutlined } from "@ant-design/icons"; -import { DexV2Calculation, OrderV2Direction } from "@minswap/felis-dex-v2"; -import { Asset, Bytes } from "@minswap/felis-ledger-core"; -import { sha3 } from "@minswap/felis-ledger-utils"; -import { LendingMarket } from "@minswap/felis-lending-market"; -import invariant from "@minswap/tiny-invariant"; -import { Alert, App, Button, Card, Col, Divider, Form, InputNumber, Radio, Row, Space, Statistic, Tooltip } from "antd"; -import { useAtomValue, useSetAtom } from "jotai"; -import { useEffect, useState } from "react"; -import { - minAdaPriceAtom, - nitroBalanceAtom, - nitroWalletAtom, - type ShortPositionState, - ShortPositionStatus, - setShortPositionAtom, - shortPositionAtom, -} from "../atoms/walletAtom"; -import { CONFIG } from "../config"; -import { Utils } from "../lib/utils"; - -const MIN_REQUIRED_ADA = 1_200_000_000n; // 1200 ada -const AMOUNT_IN_L = 500_000_000n; // 500 ada -const SUPPLY_AMOUNT_L = 1_000_000_000n; // 1000 ada - -// MARK: Trade Tab -export const ShortTradeTab = () => { - const { message } = App.useApp(); - const nitroWallet = useAtomValue(nitroWalletAtom); - const positions = useAtomValue(shortPositionAtom); - const setPositions = useSetAtom(setShortPositionAtom); - const nitroBalance = useAtomValue(nitroBalanceAtom); - const minAdaPrice = useAtomValue(minAdaPriceAtom); - const [minAmountL, setMinAmountL] = useState(0n); - const [supplyAdaAmountL, _setSupplyAdaAmountL] = useState(SUPPLY_AMOUNT_L); - const [liquidationPrice, setLiquidationPrice] = useState(0); - - useEffect(() => { - if (minAdaPrice) { - try { - const result = DexV2Calculation.calculateSwapExactIn({ - datumReserves: [BigInt(minAdaPrice.datumReserves[0]), BigInt(minAdaPrice.datumReserves[1])], - valueReserves: [BigInt(minAdaPrice.valueReserves[0]), BigInt(minAdaPrice.valueReserves[1])], - tradingFee: { - feeANumerator: BigInt(minAdaPrice.tradingFee.feeANumerator), - feeBNumerator: BigInt(minAdaPrice.tradingFee.feeBNumerator), - }, - amountIn: AMOUNT_IN_L, - direction: OrderV2Direction.A_TO_B, - feeSharingNumerator: minAdaPrice.feeSharingNumerator ? BigInt(minAdaPrice.feeSharingNumerator) : null, - }); - setMinAmountL(result.amountOut); - setLiquidationPrice(Number(minAdaPrice.price) * 1.1); // Liquidation at 10% price increase - } catch (err) { - console.error("Failed to calculate MIN amount:", err); - } - } - }, [minAdaPrice]); - - const handleShort = async () => { - invariant(nitroWallet, "Nitro wallet is not connected"); - const now = Date.now(); - const newShortPositionState: ShortPositionState = { - positionId: sha3(Bytes.fromString(`${nitroWallet.walletInfo.address.bech32}.${now}`).hex), - status: ShortPositionStatus.STEP_1_SUPPLY_TOKEN, - nitroWalletAddress: nitroWallet.walletInfo.address.bech32, - shortAsset: Asset.fromString(LendingMarket.mapMINToken[CONFIG.networkEnv]), // MIN - createdAt: Date.now(), - updatedAt: Date.now(), - amount: { - iTotalSupplyL: supplyAdaAmountL, - iBorrowAmountL: minAmountL, - iLiquidationPrice: liquidationPrice, - mSuppliedL: 0n, - mShortedL: 0n, - mShortedEstimateAda: 0n, - mBorrowedL: 0n, - mTradingPrice: 0, - mClosedPrice: 0, - mClosedAda: 0n, - mRepaidL: 0n, - mWithdrawnL: 0n, - }, - transactions: [], - }; - setPositions([newShortPositionState]); - message.success("Short Position opened successfully!"); - }; - - return ( - - {/* POC Version Warning */} - {nitroWallet?.walletInfo && nitroBalance >= MIN_REQUIRED_ADA ? ( - - ) : null} - - {nitroWallet?.walletInfo && nitroBalance < MIN_REQUIRED_ADA ? ( - - ) : null} - - - - {/* Mode Selection */} - - -
Margin Mode
- - - -
Leverage
- {}} style={{ width: "100%" }} value={0.5}> - - 1.00x - - 0.50x - - - -
Borrow/Repay
- {}} style={{ width: "100%" }} value={"auto"}> - Auto - - Manual - - - - -
Order Type
- - -
- - - - {/* Market Price Info */} - - - - - - - - - - - - - - {/* Amount Inputs */} -
- - - - {}} - placeholder="0.00" - step={0.01} - style={{ width: "100%" }} - value={Number(Number(AMOUNT_IN_L) / 1e6)} - /> - - - - - - - - {}} - placeholder="0.00" - step={0.01} - style={{ width: "100%" }} - value={Number(Utils.formatAmount(minAmountL))} - /> - - - -
- - - - {/* Trading Info */} - - - - - Total Supply Ada - - } - value={Number(Utils.formatAmount(supplyAdaAmountL))} - valueStyle={{ fontSize: 14 }} - /> - - - - - - Total Borrow - - } - value={Number(Utils.formatAmount(minAmountL))} - valueStyle={{ fontSize: 14 }} - /> - - - - - - Liq.Price - - } - value={liquidationPrice} - valueStyle={{ fontSize: 14 }} - /> - - - - - -
- ); -}; diff --git a/apps/web/app/components/short-trading.tsx b/apps/web/app/components/short-trading.tsx deleted file mode 100644 index 22666c7..0000000 --- a/apps/web/app/components/short-trading.tsx +++ /dev/null @@ -1,43 +0,0 @@ -"use client"; - -import { Alert, Badge, Card, Tabs } from "antd"; -import { useAtomValue } from "jotai"; -import { nitroWalletAtom, shortPositionAtom } from "../atoms/walletAtom"; -import { ShortPositionTab } from "./short-trading-position-tab"; -import { ShortTradeTab } from "./short-trading-trade-tab"; - -export const ShortMarginTrading = () => { - const nitroWallet = useAtomValue(nitroWalletAtom); - const positions = useAtomValue(shortPositionAtom); - const hasActivePositions = (positions ?? []).length > 0; - - return ( - } - style={{ marginTop: 16 }} - title="Isolated Margin Short $MIN" - > - {!nitroWallet && ( - - )} - , - }, - ]), - { - key: "position", - label: `Position${hasActivePositions ? ` (${(positions ?? []).length})` : ""}`, - children: , - }, - ]} - /> - - ); -}; diff --git a/apps/web/app/config/index.ts b/apps/web/app/config/index.ts deleted file mode 100644 index e57ce77..0000000 --- a/apps/web/app/config/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { NetworkEnvironment } from "@minswap/felis-ledger-core"; - -type Config = { - networkEnv: NetworkEnvironment; -}; - -const networkEnv = - process.env["NETWORK_ENV"] === "mainnet" ? NetworkEnvironment.MAINNET : NetworkEnvironment.TESTNET_PREVIEW; -export const CONFIG: Config = { - networkEnv, -}; diff --git a/apps/web/app/constants/storage.ts b/apps/web/app/constants/storage.ts deleted file mode 100644 index 2702bab..0000000 --- a/apps/web/app/constants/storage.ts +++ /dev/null @@ -1,6 +0,0 @@ -export enum LocalStorageKey { - NITRO_WALLET = "minswap_nitro_wallet", - LONG_POSITIONS = "minswap_long_positions_state", - SHORT_POSITIONS = "minswap_short_positions_state", - CONNECTED_WALLET = "minswap_connected_wallet", -} diff --git a/apps/web/app/favicon.ico b/apps/web/app/favicon.ico deleted file mode 100644 index 718d6fea4835ec2d246af9800eddb7ffb276240c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25931 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 LrJugUA?W`A8`#=m diff --git a/apps/web/app/fonts/GeistMonoVF.woff b/apps/web/app/fonts/GeistMonoVF.woff deleted file mode 100644 index f2ae185cbfd16946a534d819e9eb03924abbcc49..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 67864 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ϣ%UY8`RWp~V-U_7 zDSM&-@cv_g11tXxtR8hhSsvhbm}^TIbEA^ zez~Ise9A5xP83c_%z83NHI&u7X>Mt9`pnf9TVC8vDso9r$$%-f#fu6f@a*df)uo-Q_5os=ED| zcEe;FMSWSJ&ct}ag!R8s`bGUZ`f~{uR>BX_16UIZu3|HQ{An_9v zHp7)lLClDc62YY@VO}JkS_2kF)MYGEO;oHS%W;YuDSf29meyQ*kC&Q@D5Y()UirbQ zeT^&uH7^72nS2!YD|zY#+SZO~YV!l{p=s^XHa8fe1Wr{Ir~lt? z&T9&mFQ)1Obn6G9RBhN4O5^az)h8(>R7Z`?G=z2B6om`t%6fF1Lre{m0c~K~0 zXZ`%Asz;D)&nPl8w^z!q(xW3qYNIS&^j=w1)?4pd)hsHQJu%L&>=IUNSr-?V@a<#y zTe$XUE|?}yQS@G4Hzyq}NAYok$^v;@M3G?#N~=Lk0A7LKEyo$`IGn`T`3c+&xhE&g zGUdOb(GqsDl}c<$s___$V9iP|P`$KE66Ka)!2y>Q0W!(Z1+^C&IwAD7-&RKDm zn@lTqPUJ4whnly4U#AuBOX0`y@9}=T_iKqGj)SrPBvyHgUX8{~cQ&n$YZMhEYGih$;=(NLFnCA; zJ<{P6EViq3GdR@A0F*j71H;Z7rbk7w@|D5)fHG%I7z!A3i&zoOG}HN^4@2Y@zZPW8k#z-2^|-~Kx5rTa2PJ#IoVGbx9( zms$_6iSdGT;U0f^Fi(^HUqEObfHCxveHQQmm5N68!ya{NsbpQ!J&T!=K7H*BqwI3( z<(8F_S1t|R9X3GYtkqCkY%MCbUS*P0tD$w9$x6L;NSmOB={inXdS_%wItd~9g6P?q zbe5ls)xwWyqa@6o*JRjjFm*JXA3Z_f7BV2Q zr|8x;r2WS3q$)JNtkgct{V{eZW>(nSUAP3`gSGb@Ta068{O(62Mo>By3C4Fb0xq|f zF($svLG@T|?ZAQUbnm64rqnxjz@vnk*h&!BzyCpfWGxn*q%`b!2z>QlqgEDaj{z0qttc?)(Dp;3e z(yy(@YjF6%)!PGZ32TFI_{e0?Tr)><@Nh}%lMmyo%EZs_SFe3u*|%^JhjHJ1XGXjI z``I;gHSp+U(PI(CA?ZoqXG6&?-|KFNIGgKWj|g#lmAvsh#qaePKkb)vfkVD7B!sBr ztwrDIu9PhVp@t9Ota(3qIW!E{Stq+;x1M+(GR!qB3mdmJ6EZTkf_M>gnYyV*G~{HY z916Bf_&5)i%wxFAr?Wy1r!~*FqLp^99NyPZ-4ZHUy`0AUEz%0+bKT6;SlXPy5^Tn9 zit~>w<74c@=Of=s&C`mfeNxu7BhA8zZ8aUPGKDEyrHnjrw?v_#{)nzNg>MHveY_6& zIahSkcjLb>)xyrl4^6X;NEoPI)mVS-Scfz&*j>UtsLUHUf3vOFe{VM$n}31R)1_Fa z4wRr_VWG*Hdy0v*FC?d$Ny$k{ruxs|=UgZ|Sy?quvZB$JfE;70t4l^6I!Tg}>eg_Y zhK81qii(yP9MQjwa+ZXOmOLc=wpjZZ^%-&YDc@d%&LQkEUp2PM-s@%<^j>Wd*zN{m z`uIvD`cpvhgNaqh?8!Rgu94tEplL>Qwr-K^bDvl+D{FmgJ(tCsl2)sp@ zO8+Z6RqvHilF0dRCY(_2%LY>mq<5f&S<@pZhp;K@gL)OlJ+wIoR9s4riQb7G*E(lM zT`eb%v_6o2fW3}!gLQdyB7{*2rErWtZ}2<$YTTn(CQ5@*lC)YA5dw-p!l1x?Fy_?9 z3leg;vQHW-#<5G;K_a7kIS|F5x2qAw4Sjry?}hr}BzXo5(-a}1Nc2lv-Ux=7dw_`8 zr#XGH9?Vo})J2ws+jH0iX=yh&74q$+tx?E~Dm3uC#iso#%yxrgdwQ4sCaS#1Ba6qP@BDTTlWER; z_Nr?)h}&+X`Ml*kd?vj9KHR?7)+4QIjnxNdB$-4<7JHBLV%V%f75QVvg=?DA@P6oP z6|+Cm*j}NeBB0y|MVZI3d#*aVv3lH!Q7ug;bw0VX0C1mpTVDuBU-JlZ&L*CrEx~@g zvWYf!%l@HoTQc76+$Rpybh9IpMMRVsTga6ck4{C19$W_b-Af|r-k^#2-F(MyP}23< zJMWV1g}YafX{Z_Rw!3?-w2Q@oq1XAOMa^scf-SjkdSwG>qy_`I@4l?3=ytXtN6RU2 zRZ?CjbKpA1i}Nb`pyH@hS5vF0`s&TH$8A47t|iq@+0wI3nn-*7ob=)T!M(+ruye(< zEom9SCd#4heQ9Q{%npGh?2m^nPetWYjy9zv4ia)CrBY?wNlG2o zo#y=B+)MHX17`SlMY?qZw;;hMoH1JbxC*NXfq=*3fcaLt)%B_ci+Z)ctA0~lZj7Ga z6vPCw82$QeeH~s2j~}m&FVF^B5Z#nSEA;WOmT~aU%`JChOSD#3x0<`7!@a5b^5klL zE{Z37&-828$DM=l8@bj!a;JCkT=(qSYNG~mYkT=r@32~Pp9^&Xo0jSK~pHT?6)f?A*>9E846baRamXh?Tkxg^BjK7qxaHX5Y=?%)&BTXb5Z*`A0_YR#@MG~i$G&mDiVqBUEQmb~ zT-b4iN)tcawMQpfkx7NKEy1{U4Vn; zOn`N`SltDeICuwP!4I|f=KE&G=pA?A`qlH(c;DggP=Hm>jkJD-jK*C)#5xi`pESX`hO z)^AT71c;{_!-jQ+x%G$xqtk23#8vBfe!c#pI5j)(Ml$E{L-uq#7#P3Dj=X_A4S*3H znBlL^`de1}*(c$r2C$6jPAg-6!zeYxwbp@XvS>GY%obNhzgT{!V7`!tha) z-OVAEZ3n1vj2wN3s5_q~K0zKsWlI+qA)%XFSW#i>btv)AF5|UYK=>9Y<6WAGKhDm9 z>~TM~Vs#Y8lnF4USHyMiR4{8lyM^>Z)dfszO%?SH*J5wT-p#cJ8(>q7#3GzJM3d!F z)-Za@re5UMqQu?&n9LL_mJ&?!G}p(vhkYsK$*YuiBRNhjbc7<@KedR3oRvOw-kVSZ zvNJxHu<3gx+=T^c628Kyo3L^%6*UVHBMCbNS2_Jlr-!(Ngw;HidJPwcpmr&Bl;U59 zAB?_`@FD&}7<>qFe0pDef`=aa3O_%Rh`BLksk z1{srtza=8k86*=_O@dPgt9HG}|0hh)8OxMT0bAv-7S4Fb0 zkDTdD6%FGH%Ue}4h>u*^j8xB_GrG5#lle?4ZT|>P~W#{+!GHsZ*!l_U6YuunTFV9Vtqf-CEsVDxn`5_ zegWYFLHw{L|BwU&fdGMe0K@i!pl&e$0rj!O=1jNPZnS(7m~FJ!;{0j+xwhQ_1~U3a z05a}_tpl|I+UO&6fZzNz(^vM}Pl59UBL=z@EIP=wKXq5@hQb5vVDO@jfd;{P@VE}| z0xY~=(gD8rGvaO%D4&jJXmxC?gP==rw>UIMnZNf={z4-^_zT*Ix}^-jB!2k zsR-f(%PW|#fZ&86H7muGRa1F6?9pIhm8d1o)(~P9%PpAKkYJU7&co?v^T_d|XN>#) z!3%Ovp#4Gk3#VVSKe7Ntf`SREr>Nwd-~$rz5UQg@HcIOd^R48sza~N%YRAc*PdML#BJHU% zJ4#DV4c^j`%%U_6meXa;{077Xkq-yUny?@_RH-3I0cN|8tC7J-Yl^_$Rx=_&M=_pvWW=AIentRL+haM^^M| z!TJ`luzS(QKo?tikn2H_8}V;H#ebuMG_;kI2~LHZbhVRt6=mpZSrx`hmuKFx z3p~}OY^Pl#R_&`Tvz(4^{RvRshVqw-X{)yH9 zEB6-L=j}?Bvia1BBkGmEU6oSnRJ0X5#9WAJ5!^$}`yjW`GO}i*_erGV6U72-gx>Mg zW9BMOQH5LzgXPRFBi|ThsvX!{k@({FMf7vMm_e4Kum+_J(dn)Lx?}A7A200KY_cH& zZ?wkfPkq{|_yzY9Mp{DUScVS29VmOGc7M+9)y?>8m5*ZX!DrXh%3k;_&I`f^Jz;aa zG6fxC5KR*@I8v{~$+WUL|Ow zdm)QEgfm<=jDTes8x>}^Dn@G@!Z^BWn9Ycf*$dbtGkju9OVo@ zN9JtXndsN)ukmMZ%1Mg5TXE=SLrr7d` zicE-1gCh69WSS7B=|11x~CP`}>r@j8`xaL>{FyB{^fQ6J{djI=f^&&_Ni6`plZ3X^D3zfCZpN`I&8SBNX_9q)=j-Lf8 zYj3Tk$k~Cdm-m&_^Hkc^D`A`*;amMNkFK47Q+u?<4Y#Q_%qirCD5S5q7wGWybg1UW z$zq7iLKXIoVfZFiSM=*s=+hIaizoRvD#CpOAc7%+GWDghfOQ{tkn;%--4Rdsk7xQ1 zgN;yU_w@wG?XGduS}l@sWdStsu_z{6;wpta-!bKJ1NAzhaD3S(Z8t)%dEs)kE+ZJX zn8YzdzDArt7?Kv}*9<8pI<*d*u?4C%O?XObZYL18(V7*eHk@GU(b-JnjL1;83=vDO zb;;T{Zg#laRQT$Wg#f8g5vXrExuj*tA6dXNu?im;@qC!!En^%oGk<^`Y5@}S?vGnV zm-(nUVZCeBf=!wptO)3Hfz9gv<&t@Q067A9>=;Xr601f*wx}hVjrJs18=Pv$yWBLbvBXw>nybvCzqLC zIvrQL3rJLYh8-HK9rX@x*;aZ$M_Xqe$PWEobiHM zan!Ew`Cb1ABg@_`z-Ti_x(?)N#Fhiceb94=| zCK|AfQTYM6Amb+3f%HP z^V4u0z!4aj5*Yk9nldObupdW=d4v&@(TVAIU?{B2Hx}l~SJ>@fP_{27JOjnY%M8y! zFSIc9J%$(=7`=%Z6NZr7BHnsLv&+2%b>kD-&{MgM;U5Wu%_=ludGG0P;EwJW zw(-;ih3{K>ko83AOA0DgEede`#!H=+2LCmb%YhpN|7{bPt;+fcyrUuMIsZgGWq{iXfqPthbyUu9!)+ zJU47kLMuMCbn6s|E6}bu>(tIG0N>CJ@Q1Pr-g*MPj?{*DqyMSS{34WyvLz~O|1T(2 zL!vZgEsOg4iI8i%i@K`0YFUfAzVi_26`4t4@Yc>Z|G;(e@^zj z$RazYfEor}cw|BSH0p1sR9{H z5rKppn$OY{68FPYH>jflNo`1d5gH7I{M`SGey=+||IUHXQR9o|yI5~A4_rC(H ziNr(c;DY1}bfi`lQWhNvTivA%hIb~>UV>O*vs~WqJra`4%34)gQ6uu5Nrd}@kHYv9 zYLbh=uF#=k5vVROQ>1en6Dca%))vuV#c!4zxpn!=w5MsUA#AfLGdLllZ>os0SP!nK zGUf>;|Jv{1!@HI8m)2JoqbVhd({sx;Gc2P>wrloU#1#(d{Nas#BgdxI^s9)uBt)ia zj2)`u`D3HwLNo5h=+lDJ($hi5Jsnrb*)+;tiWerf?GSdd)}TI|C^nUe1fMU zzfJl#(}0yS{m1j&l~1x4VgC#H{ygyC0zhBjy>E89|ET$zUp;$Yo_wD9rnt914vO=h z8n1c%Fg^%@8mg8@?$*t??Ha4AQyTA5H{7(vs4cN*@=O~5Pf3@p1hkz~1CXK?M93+i zBqXGkV^Z)=$^k*BWke}|h2YK>LY`dmskcsyQ)qfsTllME$jy-N(`S^_8bYftjv&7F z8Ads#u;?7ay*K~W7YjgFIz&}bM46)5{8eq*q3tkjjBQz9Tcgu9bLK6WQr5IK^k4On zw~f9~hp|WEiNtH`~g%s2WN=~vDAXev}Q)o5k(7`1|7#$y#ymJcr$Sy=QryTHvc8)XBDW+kk z7<8p_$g1GU=lWAVB5ZXR!o^d@Hd8*Vj7zic{OJUL zu*i!8;e3v#P+SpiNyT4P&D~X5{!z)^RZ;y>(YILzB1IicRfSYl*>y?Dc1clpNtwD? zO}kl#_f7G8LH@1RZ&~28Q1DGP z_%SQ&3;}K-54)z9MF>J-+OC5F84oRYI!c0vZBCl;q&j^Wkf}{e+uYhFxOy23Vecw%=fq6_;Z3X&;HZgK zY1LfSvQ(F;Hgl%UT50E6Rl`~r2CLAOW?%M7?g1<_MXExofEv2@z5Tuk=I$PiN@D0s zTfCdy!%fImrCanX!RW^jE3Df(1~OM1xT6oZVBbYRj>#wnO{ zo|+`GnVs#`F*RnXWG6Z8b!I=lCcmBJoZChJkMC7wns_p2^7XI{r#*n@IYX~B!#ogR zOlT6gAq5M*#~BrBdd$~P&FmZsKbSZ$9_t8WL_@A>Qcm7P$w6x)?9-(MdAPLd(0*S zkhr0RX15y8;h<;k5lrB8dc^NR2846F>eFVcY9@g1?Jm-l7o+-I%+nqdHoCs0&}=s> z?DXGMD8-uGUnTkbO@FbvT41f|(#}Dn%xFV@>_!_`*p-PNbJ^_Xbw3qD_K;Re=fS)R z_e4U~4iu!8cSHqGU%!EHfL|Ah)B%6n&xq7MGiakN!FG0??PMfDzD^s^sOFsEtIMRE zV4H;eA_%N{(s|;J;^}xkIn1gRm0tQ`$=y&bOnhe^l(^;DZ7OeOtq@yoX#4$;G^O)LQ=g=q(@lq)b>A*=H@mxy1J=1&$=^A?lTO_)l#39YQ>8=k^ zm~&c`E@4bOQGyNNKrF$Sh~dLLVPP!6y3BDP`#UzA>@I>0Kg*Lx_+7KT=$om;f_*0EcZg?l*n zX>l~XdwUjs2d6Y6=?ALU)`6ast-`jVSY9kFg9XYb+lEo4ZL)Gd#>Qpc0$t~2!Mxsk z`973z41*Q_AUwwj;u1XfJ_T!B`yZ`m@4jH3vN$gU&sE|W&*UA@enDVCMIfO5ttcQw z&|P3YpnxpMnl}zXU;{F-NNCjwaP91JN3!W8P{|Fqi^PV}lvZB|k>XffE+?6=4wOt# zY`Gjx_q{|KPW76tHd6V(PHws@UWJFTyx$&u6~BKZ*yj9=WAYzBXuaq1j1{F~C0{Yg zj8?1Ja-~2y&5qaW@s!yPPg6dU^&Md0iW0NX@4opoq*35$~QV9DpFcPN^){+Vw{?Sin6l2 z;`R3Y`llrVF`z%-BU{$GM$u10*rtbz-d6PzU(k^$lxu`asFti2E0k*mi^!(5nxy{k z_m&Ga!ew+@UJqvr_I>$;gJLn*%yt9ClnZ8nOlJH3LefdKDy>Gl!BX0vo>_0a?kgZ3 zmCNRGz8WZ@Ub#IYOH7DzF(JZf9}_2xQgk|>?uPi2%j11}7M|z#dikgK%k%zfu(N6Jwh{(y%8})eFDrzrt0CJ69iK=NHI;V{+r*cDa#0yxXyC{;s zFG9~p?Vdi!(Ed|s<}7A&NPp|sTKDv6ulf{>4cEK3Nea!4X#6K&^4C>tYAW5>>j|6vzAEsWdBL!Irzul32428BP6n;xBh z-j5>ZCV&jv%pUen`nCs)oih!Iea(RjX-G;F~W5+~{MJX+Mq8nHs{#5OWyQbLN!9dgwk7DS!-P&l$( zq@ZmKP;a=}sQjW?tVMRtAe_q)pRVBZN#jX%IA5@$KkkyBUc^C85(;0Rzm7!q*n_PNR$*tPzlZz;(il~CDJR%oms*gR}8Ky_i&nk8k@OHEOulB zF$!Zc2i>M%cUvJmYW2NHG4xn7^qe!u?FJisln=BiFwjvkz{6mQ`bo#pLW(8AtY+i6 z>Xf^LNaije4=*VZ!HY(oVW$XD7tJHSZc_oLiD!TtuK$+72{{d}JNpg54Y3Sn@I@>| z7?==DXM+s>{rzCWMV)xs@}nmZDsUx#C&Eq88WLS(Lbev4rj~YIW^lbEAK_?L|H4=K z{-HZNu@wPE4dqrnZAchZ;H&C_6wY)&+3v!7#}76D{dNyi^cqbnBIUD8y&jeR;F;bT zeSP*Q`@*{(dOtY#Hq7?^nEy7e1E=MBm^WZODTc!=VYDcbO|Lf?CY#FVhR<$ukT#z! z6sDgl1Q7$I*BPXkEr4*dSyHjZU>0Y&48(wSy1=xu$d#IB0pNqHpt5Y>(=NdA$ZVW2 zIiq#pVdzfbv|LV1hpZBwfQw?ls~@14(W{u`I_83}I2`r|XoCf#;k#p^;V~JF2ZB^b zWDzb_O{!KIjN%RFf8M-cqS<8P%HVO!;1$zkc3b1ITch;?tRAg8skQT{ZH8B7)wUAY z<<7Tyz1$^EXMUKhzK>_4n9*p|8;%B|tRxw-X2AaZp3z_^M3ZmPP;avOfB|#ckB!%H z>d7xlkv=VT66ONLL&d{pDuI+h>aTn+^}hNqE~j)|f62w=t4V#&)YE+M!8NOqLt$R;ed=V(&BdkE+%zUu*e2|WOh&KbEFp<3FTBOjQ zCpX;rFkblx;J@$8M-1M(cA}hQ+oFdr2vvvvjOq^JUy|!C_^jNZ z71pFMm#kwXB&{YK?nzgO96d9 znhQcPoU>(ZsU(eentx@bDCGuT&~ncF&15hH;w#sAbmyXRO-5db`(!MXOwUn++L-sL zxa_%NS~TC4T(y=t}1I*7Xv9 z7HY}b#P->8Q3sw@DLwUXot%8iEJC+bHB)e$ueT{=RBxgsh!Ob1p-)8jX68vxZHk!y zLf041kwvK$7B2k5Ns!v$)wQ!QDg3RnX4M;vnoaR{tG^(mxG9fQfk!E^VlCI8uPRy( zF%A9%*_@DrSPa}Ei0wqDv_9Fh3rUIPxnYRmi&JmWFXZJPg+7+Lz4Pw009IOU<6aLU zA3%EYo{PW?5@n&-P(|^|=TX-iO$jpn9zj-{qvKo*e@zpr7kCTY*8#X!lI8gKzAQuw zn73cW^i7z18lQjuDA0ra;*qr0Wn$73v?y;sMh?S~tTH&U11gX|SPE6!~{hmrgr)BMD-fX)gy|Gn%k>5a_ z*t3=Y^$SP=^}vFLKp=bc{6EoT%sv6HdZr~*B`b7BKmo`@CKr-2MUDwnSk{mSmw7*<{BVX1;{23V3J@E)J+B; zfrGG>;+&tTR(09`qC~bEPfx(Vf&9gQ>iRjzUqEo+zfcg0!7~Kp6kt_;u?jNJLOnnX z_JKzjDr!J22Td86a{$$Zdw;!PX`&L82zx4Gslc&{>dpeO;BO6Ms*f}~!fc`;3?1Cq zd}Is}b4n;G1+$RmNboad%8*Nsfj8vvkX%#bLs@8LCZ(1wSsJhB#uaUxh^Z89M*$YGX3rW5heNEJ#Q4xS9Jru^T zhao>?eJc!&rAn53YC@-}lbQr~2+65Rmw0|i=c(+cqM?ZZmHJsvN6I&ngqE zTDHjgsL{O=>f))Z%f5`~qR%TMza0G_)-6x4g7F~xDbc&E56jeZYV($5XjYYBiJpFB z*0^RbmnEH`l^~ixo`Asj5KFKif7W`_`66zsv@zh;I(T8yIabs9eqrf7+0#U?3%jxa z=ZdnW^HYx06(X2M@Y6u7j%5`y8_o_~KKKtIv?wO43~DKibExZJ>Yjb-F7Sli@1G*d zw&dR9R4*}#|M4)`2!4W*{|Q2Bd#9gHP93H?X0>T=I$tqAN3*~7e{lI>_{a1P?SK%@ zA~u2X_5(5C#{637LvtW4bpm{(y9*H(v@+;m(gV=HqAZ61L};#aC}oilL-Gtz03ak9 z80!J>I=Bnq@IFQdaGhW5eU~?|A3)#vixeox3U-U2t^&TZkSxGcg4(mdF1Wg8_66o` zh;-rBduDAYSCQfS^&Vt;0V})LBv|7jkaH4liGPxbmL!Ph<7CKS#;~90JSBVP50lHF zn=S0LvegRUES%Tl+)6-BA-Mvl6A~po*RC!gEeo4;)~S8t`Nkp-V;X4Xlh`NdQ$(b^ zNVNx$p}46&lff=jkBTzInwONU^j&k_h~k-NQ?>{IeMBv44sJJM5>QKU)lk-ZQG0ZI zb9=TI%{O@xxgn&)3q;Yx(M1_Wu7x>;pM^<8&)oWL8a!)x4%M7tvV&cZRj>7$DdG6P2@M$3P z(#9RnWAOd6ntyJt5FIF6X}MQR_wa9Bd7}jT{14xssGw* z>)y%#3i3ym=ixe&HP2QaRy2PdC4_y>UP|=wmL)Q^&cZU$GoSLVW^otPR;K5XI&$9@ z-#Xsj!x%^EZs+qd8?vY}&eGX3r!%56HZsLCb~H3xWu?U@K_|H;v8=VMEve0OfJuXy zghLCQ;_-v>85TjX3-LiNLzD+g3}K%Jn)i+!$lEZwe$q8mRI?H==MgdjY((RJtIr-< zm^J;@f|t!-n040xr(st^u8bp0$H57s?Q=T_y*>7z_krbu&=0;Ik>6{*6&Il*B36tF zfTZt7k&W;>Qyfw;0Tg|Ezw*AGCo|77xX z-nUzOM|o>`ZhL3FV&;i|j_oY+Qz(!z5Z+`yHrTF#U4XkGct>>)_CT8j5!vsX-_r{>3oi&E3=R+a4onVk4~!0^5rYw{5=~1~ORS8&j7^MvQJ`NU z<00puOky^U5Y?B~8`gu}syOQU)bFC7LD7aH4VV}fIp}$i9%Crhx3tOdQ1K;9NDG{i z#46DzJ&j`>?mL-gq<%W-wrBC^=@Am7o^u zYgKPb1%x1`o4|6^yYu{HnK`XzJ8%2$+;k9Bi#<;-9Cy8U(Pu4e`X5|N_P}EX$1)lq zYX15OC23VJo^2~5uLhH@xqn=z`Gl5u4>bIoY zLzfH=cnChWD9kcg5I)bL=|ZU@c`bn4eq}p!DCrZ5y|e|2YXmOiT#ck7Ii^Xmqu;JJI6baux0aV7kP#z8%m3JV z{6#mQfD{F_WYw;tCf~T$RcZ-K{U9SJ=XG<(bd;N!>6Dt9#z{)Y09&CdL78@N6|QY6 zl~^2(kVJ)%n~@<&ma-}a2NSgGh8YIK_c}lFG#HN1x@4drJCJ6=h)FZRz%!~v8!>Oq z%KAh6$^D>0#makW-V{7MEZX~xo75Z1&=HIXy@AV+Iw-a$P#E+V^IxwOu>WA z&N->3J?mU=3 zPv(kPphJ%>;;7R$(C0I!0vS|>>eGorms0mg0Zgq=zwRT@?E0j$OwohG7ph(FYnQ7j zX~X`qrhS=JdTnc6t!i=ESG(BozUw~leopvqltk)E#>Yk0Hl$q(oIgW72Mt@Jl-b3- zS6O(k(Q)CaRcKMAxJ;jQKJ`D$7sY0(IvS|Clq`6mYLJ|vrib92!^IGkUGCNKe!kQr z7s;R;e7`rMr6k$;$=0%AP7fHwa8j4m_`mx1e$JTyo$Lr|Zt2l)YinsqRmNBjVPy&~ zbpYf=r#^j|xmcID7Vtv~h)AF_)pYf0*ml4~TL1tLMK+vhUoxwpzOA-?)*V(0O&u0R zd3myXO>1}l5TqXQCwwDNitITG)RD06uojT24o!wO0U9#xsNn)b{{S+hfFlLnKhnR3 zhYbFJpsUCQVXlTSK0llO9{^-Po4+bH97qfqgpjKy<(9n9HqI!|I8g0)K&-r6SkQGr zQ1g{Wl>?!`unDP}+TDbiHuA_Z2xRXqq*9_NQ-`_Ao3f$aRW@{Q(Mb#6E;Y`1kpl|o z-s2rDe-L4)2n{nL2xyU^OR01;WTh+Vjg5_Th334G2u&Xx9Gui>T2*PlU8RI<)_8z6 zaWCL*st2VP0e4$;D73d%t~KN)yDP(lLa@<50%yIykfWplJOtaZ6tI$F$CM2BM(b1caS63xzb@lPh(a|h4J0!`W(8c}zVgkLAB~FBR3(=A^ zRQ3bPxX;yOg+Ay#=(Q}n@)LA}t10w@f2sbmyUy+`nR*57Koi)9Gic@^Vs|wmB53UN zB3hhAU9FGzw=lZ*cz@eNf)>&Zb+9l7;i(~jxM*GwR#yuR*TlpGFifMN$UH?E$3PM} zmyBI(!li2^?Sq*xeYCK!AV2{Iv~vETp>bf9UWbew)SF!5BQu}2W8{2IC$C#V2t!54 z2K4Z?(u#J+Xwm}uZ5dT$9Ay$VpoE3sH-x)VlL}B&MnxIlTWI4M7a6(H2@h7%qF->C zvqd$C6PB0Dng();%07IU;ItbzP6R=NpLlw@ZS(>e!{2H2ENPj9(cggU1a4lygBNzL z{}=z>Y<&4;=IE%Q(8oVl`&!crwIBU4hX2;L%)UMzh&*7f|LQs-=cnb|0PILVQ^k)6 z-wb8^3jW476ui4jJ`>IupeWmCQ2T^!l6*z^)cle8hm=pzXXrEd{)fyTosZ{*@q7p& zt8kZ``X^0sjsBB@{y@U2N#vBXO*#Du`k!EQf2R!_LW|-%+q>sf+M+q!db;aV1U?4v zs{r>&j^Nd+S5;L-4(V4`#)EaUmAQBCs5IAFqtCUy1>!9j4ElqvUs*5jcDqH+?Z(vH z<&}Q}VWTm1bF&P?63xQsb;L5VbAF?Q#35p7icL#X zi5R47)j*Vm3`C*)Dy(ibk6fdmUq)Rp0?k~Ez|gXDdeDx}Ho*egJVW+DFoWJ-dc2Q+ z(t>MWQFefp0TrQGAhT(E7p~^sg{xT7F{Hi=UvuxqSG)AO(0U`gC5&-tcWv?i{Fndo zU;fYHTJrGlFuAr2mgw@@iD`cEMWgY>7p8ea)Lt1``8dN{QMn@9=66s(EVUnP&(9M> zC6(&w0X7_Av1yu!6`WEa5RjZgVQp=#APhn@V^Gj3>iYFo)nUL!1JQJxp(tcDWZM*M z8nj;t2~$(DWqH}}&txVh&gpMFiqRx$I&_#Os*1RC6c!~z(~P7976+4LWPx*p&_OwJ z>(;@6FH0d7FvcPZn0ga%wpkk;ttoL!IeVPhUR_<4d7*Ja5G4rb=Q@EfRNy0gN{x(+ zP^TE5W=~I{VuA3HdvkLWbpPPs;K|7eeDQj{pZiM8J`8@qlu9-$%xATg4u^&g6*ru9 z&`7~a6Dzssmf zB@n`)W-vB?q}S`Rv5AiI&-OYJa)Fypa;(zwzY`thn6B@6x0*9Oyp0`$^}i2JAoiqG9`O3)RO`txe<|3SQ$9c z{R0Dk`A36r2o|FpiVE)6E+Omkw_udCG=n86@ z%b0;l7;NFBWZo6a)@Hdnnx98??AMLL5lhhx5R0%-;csZ`!-|a8*FU#tcPQhY;K?cSr|9pazyJAb&t|ac z*{tiRCxw{d?9*Ycwmu2Hl1Wk(eCG~$Hp3pjL1l955^q#^szOFdp;YT#!TJb*u4Q+qFM~S1mKL$xUgB}Wz$gTo5Jh}sxeBw8@O z^9}}H6bt!l*9trL?%mtL*REmcRXZz|t5uoah9dJ$DxUevBnT8$K1v^C3|vmGtgLV` z7%vP)UX-%BYz|Qa9$bk?f7I{X&z30BxueW_c$Ol8X1#2hK8So>>Gk^L zF#}UBsYhxZsYw&}i+i+ZpmAUIq@dD{zH1W&Xe&4z=coBG!suHFp=cJs5`?g}j?1MY z*p$Um*#!omvsOw&OIibh#IYF#-``V^IcHxuLO$5cfPmDEg#{%V9UU9bW`~DIqhW~$ z+l-gO$zS~97n^yiXLxwHhb}_*hM`z3PGXaBEQ4kHq{Nnp?5wgbh*`Jza~TY^Dm#$Z#C0)#C03ve+W95I@Sm861EQmgp2x}5R^LD?yd0CPLI^%WHm>mE#fvAi;-@$XR47hGA5)d)uq)>yotcVs(43ky>A0PZ_Sk4?p}c2E1>@49gK5I4ue& zAvlXc7h5Hoti*yd|E7l6y%Zt*9>9MD@S)RG>h#@fZAIhXvf!bGk3U{0VT;9rOWC8H zy}fXFYkTJ?%bo7+?VVae6W{*!x32~i2Td1?=p74ht?&;ZjQ#{dXv`z%%wWvN)EeL+ z4zhL#ui05sS97^sv1U4fG+pK?1V~OnWQ*qDP~94xM8GJh@?%D2vh!7cdJ*HJc!$Gb!I(8crmsB9Vej}gkPi4(7#}aK zTqo3TA=EEc>b%ca1;XD`tGdh)@xp<4iD-F{FZoJcXF&ywO?b=cWRU=mH4vL1sHcx}H`$C~~ zI$fxizje0SeZVi;GWyYsf8xUa+KWrhynYaBhDvUy9q! zMuQcgI7LC2_Q>{#k87w0Kpv+JTO^`%)VYuj?hfxDDIM)_jlezce!esOuOkc<;M1Ch zeog!aiI_sa7LI49Ef#bJdVKP#ueSXF%KFMi8se3ym#a%Z{pAB1O6~N;g9rDY=M3Mq zYu6-0an)*>40;b-kDlikh?3sl$dpKc3?e>$^OR_AMW*(5PvXE+tP`vO7fwhjkmvQW zZ~$Zp7%qoZ574Ws$QDPh7v{3_GKUGfAF7F0w2Pdl6;aOQ2#!yaBg`_@r8fO7+9VF~=~-d-u21)?NL z+&Fd(%hb@*rwQlgema{yp&|LPxtW!utU|8=PU1MbB2ycalWi;Tca33ZNz2&fGmZf4 zJmUuyA@A+mgM;7w=5KxS$?q8eQE5ek3>8kn0E&u!&%f6F!*WQq7Ku%UJfzZEU)=;^fi>*ghYy?*Hz=(h6^v5Q*YbpKf1ir$f@8dziqd3@80d-gt`AVLg)j=ZnyI^GW2R?btO%E#&0x? z8m(dC{A-2dEjZ4t|`}0*tgm} z{UPx5^tAUO#v)+jb6~3siJpAvU-@6+WR#w*5QpLl4uzn7X)RW|k zH4q#kOeWNd+hm(19oY53{hc^t;Zda;r+qg+`Z~C4$4wU~0^8e#qljtKH?Q9s84fx~ ziZM7mcH`E>^t49&?+kKYfz!C+ngi*f7EK2JB@=QCyn*Ggd#VxVM(%7Y1Q-gQ8fU0aF_okFHI>bWt zHd$zPi6=EWNLlW@_n(Vm^p}Xl3?odD7pxHq#o%UP;3okvVFzC;ot$jGI6OW+&Z{^u zFfb6LRo}ost+>19z`8Dn3{)@35 zgETb24}x==fAFP@?w(Um?BX66>+|^_O`SRfB}-@(;)7~ZX4co9o>Qpv@a4;w@KCTv zk}6GydX{$&H5${?lW$Puc(i4K*u^F$Xs85DV%`svTui}d{76lb;p1r1Tl9L1ZR6W@ zJ)1@Cb6k!SfJ8=Fr~=dv+IXT!PBPWS4?enp4`0|!0u+#J$GQUyuUu|uAT$uLDRZ25 z1ke*xp&ULjA*F!yL2UI>+2&=LmBp8P+iMW8s#KwSFDx|(7Mo0sOawYd7%lJeQ*amC z%Iw17^)7I&BfR_gB7xVt%u9D(wH>wclU!sMMRt=hMMn2N=dz<{RT|t>fL*^Q2#Hr- zN(`P9g#|ORi*INfF_atxZ{!}s+*8mWNr>7+pu!(53qlb&N(vT)PtZTd3`5=lq3GWv z{(o9Ymu{Nd`a|pHaB6FR5O4G;sMhphbr}sNY&*LX=5k+u-&6DIzCtANM<9@8G=Jd< zo%?<+HgDRc;FaJ8J)GGEDrXfEZc3^Ox+i1W_{_C_0*=t(W@gx2_Yd~5<#okQLROQJ zh#>qKK^U;Nd7suU=f`)krMWJWp6UX(T);c#w)q=;Wud}8oJ2EE5u5vOIoA(7?Bs^9 zG1+l^<}!WY&Qwix^544q10-_%hX6jz*}#Sm+J;AZD7ZoA7HI=P7A6ww6*((OX)ra= zk0+q=9TX;Mx-+7=duY=j{~5tUPT2;zA}t*BbCpBL&kff}-n*7rc#_dw!&lWaonpY; z%%qM_>*^{<$!1!v*8%#CbGUeiXgyEMS(+BDjMXY+M*x1G~m|Pm`0hD*5W=KMIjN!PyI-Khg^JH4j zU&0yu{EEHp1g>`()%C8`#m;4?)7n%_xk5RcElb6s1bX^#O=i}fz0%XfX^BD!OOiJm z4rk#B>6XllPE0~8*qd*^FWjDI>c3dSIKog7@`BG?wgJxp1D;iLxvF1P{R&57Ea>uD zypKP)dH-y8cef8p$mMb#hC+u5M}jPIDgf`2EvUaWBT^x)onz&;E+;^B zfwNtoZ;LLn&FCTp(Z!CGrnbw?OPu~znQG}EQ_aqN%yn4tC0d2M5l|7jMkJw?@9VQS z@|zpH1vkohC}-tLrEFUKey@Y2ptVoW0J9%MCZxY!Etk}?6Yc?fC=&tKW0cziHf>(1 zp=nwcHjAd;WjD*2%}wQ69iGsu#bOnKY}IuG(JU0sLem&Gs+Drh)N9}wPy&P_1Wth+ z$rgrTbnwvXvWJ2JDdcuRA?`Z#gz=rM0qy}}g;zI?Zj$(X6rlhM(FGPa&d$yn*a=3s z6BohIEs}JUVd6N2O+&V=Fc59@*VS({F?R3%@*yqkw#6h|Sa z1*8|{bhhTY9>wT3;Z6rUe|{euW2g?@_OgCi2d#503@PkQ%t(j&NSy);^5bclpeUeq-iN!hSrL{M1=Fm+Kq`Jt>;u%== zWN{WRp^hAGyykEbVW@~@Fa?FFPLcl2`=JbTpNv5-AsD68vuAF2mO1Dp&yHbumI)rg zvv1rN=ZaMbf7hX0zrMK0UBAAvv~>3ig(3gDNXwY~JLcicOnURnhlean}r~I>4-@gcb{~8(DA$nXZ zt681z1tHjPtH{xcH~`cWwwdbAh7@qKW}^flw4KBB{t6YPApVgiv7xF4nE(@`jN=Uj6dRFJBZ)_teee zSy314HptJ{YPALppMoeTazya?qJXq3UQ0a(J}3B64*g_*74E5R9UrTZ{WJ}|UX@u3 zM_X8&xctAJiHW%xLW=rJq&zvkWou#F_^6R&EPTFjD}o!CJq znGEbCJ39*>GyIR4nQ_lj+cUez%*@R9@y^cd4u-*T5;I%2n57o<|5pM#@?_xnDk-bg z>MpKVuipE;SJ+y?@( zuX8<3o<5yicKy23+F$4z^&RSJZgzgRrJy-cfvk>6?jJvR@OabQ9G7cljlXh*)ZegI zV<}J{tM&fn>qB9B|HRIq zwpUU;fm6X1aWuNMv9?xgWr#8PUYIJv8;-5rSTeQ0wliit4W2#iZft4NIfM%^#V5Za zOnab2yZm%3odvYr1W?O_k1hjm6ejO#yxL>sBV08T3(J#JpkmV#6K#aEvxSGo z62rBEymz+TTb!P}N^V5>8{`I&?YB)2#gA53$hioAj+`S$droW1PP0Y-Ec!PUNb{=(elBS%tYKF zesuFAmOwMtW*d9Z#_qvmd(PdSmC>Y&OQEbs8qn>5p>>o3rEQgT>c~!qKD#bh)|j1+ zXH9UQJ?jzpt~J3sIeBEM6Njy$-m=xvX65HC2Hiboe)#axG+<)Wm&{-JwZHb)e&rIr zpDh-F7#AUgj1}t<<;HeVgv|8DjW_-Ai3x#%nWRGe$-nz||L%!^@613JPlL-G@d^>; z+%V)vg~GXWZ+_NFmvEE=4oBc@x&O@9zIL|%V=G-|d^~gN6i+2pRVB(N5~og8*D!Y0 zs-Lyeb!;qVhuORZgv@5!d~knplh~d-&X%yol(IG-#+gZI0DCRn$@I zoubgJwKh`UjV9vj)6?m+cVx^+)YH>bLjg&W0z>Hb_5%7^AyYYci7 zw8o%UZnj3dWS84G>K-@rcKg^+?kC*LFbX2SsQSVSFQ`RqRkW~xQXCZDwB&N9PTklm za;<{&80XIqIT;Fd$S6)u7O!TrS92&p4idm%s|$L)mNzVZe>9425L+2{VV{R&6Jyn6 zl27N(OxPe$gFtF6k40rVm&y}e$4;wbfasFk?xB{QRDKzqvKEV#!_6g78|s)#K?Z;O zexhR~MH2UJnoT_6`CP7LAz#rWE-+!cSW;jpWf=yI3d*t)=A$U2M!L&paatFavUm#J zIcy=>rw^?T3#pWt2apPxk)#>uQp&Lyv$J2$w~V-k+-|93+Qp-2C|kW$ynNn$WWnV= zH&e{ljtsl3^|}?wD6$+xVUSI36@}YHAtQob!CVdVto=R%ef~nHAAz%o#xlint=dxT z_HtzgxAZVWat7(3RO4i)J1o0TW0QK?En#zeMKfVV>*?!p*~~)33aYoBS4JT{D3bH% z=fZqpH(QTzqTL&opFBqYEIfXy(fjw0d-C!iAtOa_*u`81*=BOhA@t5WQDG2GHz?#b z-}`U>?Z3UZnZqjzsYJL6QRdyOb#ASdh%$n98#a+L+EH^k8DXa!VoT_XKVYFnx%xu< zN3%}q!<_@)aLWCq0?)s9dviW9E`-Ojj;K~jqQpTl|R+h z4ZXp>fH~q)y#4)|x8Htyy{wEp+ZQ?TL4qs^To`7RKEf=}@87@M?2uy$cjdVh?k2ql zwP9MiR}=>arJ}gz>85bv#Dq9DX4E-wWL(`iI2ao%ErDxWDrpw0Ro9LY7-*diHNu8G~6{QU@DbNRaBpkL=X4lU^n-+*4IDFc(XqqJJ{db z+1glN-%pQvy}n>i@4z5JlzfI&=L_EcfX#8Z6J1@|*-h;xOIwOMbaujH6F$q-v!8dk zJ+8sA@$rclUsv+^bZTRLb#>|8pDB~iWdl0c;Tokoaq05;fW2BRHi+~jq=osVr7MFG z0r|Z4%jV_UOK!{K)r=`D2sXEW0Hf{eUth{b1dR4an=Nj;2Wj=Qb@~NLU-+q^yZl%# zH&%Mb`#s;|d8Z`Y9r`Kl@AwzMZ2kLE*}2#nD$rfA7K|Y_|wYWox#DK`^rxbvbX-y5q5GMZ@Ddtix$}H zI;nHj^Gek36Qk(lv#gshZf#xstRZhw z)s+?U-|00#If4B84fy4^G_jk73Sd!YtIOu``PSDr*S0^p{b2LSmM(C0(2fQtcqTw$ zCq0V33-)EZ0!v%7&Fhj$2D_TP5H{I7-q8Nd$B$OC^B|~U`<>-1v5n!KF&oK3C8=Gg z9!3+`D3_|agY9jf&(4PiFP;xLO}wEv-3TgQ+JddjX0C36to_WO1&!RVx_maNCi~m~ zyxR&pTbb>&1a1fc>lR1D_UR#;phsb&eoz%`gGVy@R|Z=girYnaDssHQ2z@JX)a6Ma zkckPhM%>ubyXhL8tp=V}l-z?vC)@kC-s+%JI1P#~bf$KDO`$vf}7^LX#oSNGO% zv6_DM)wE`5!s1Ofg{yIVE#ka560*R``{G46$wkppZujx-)-gzk)Y7BHN4sV=*BH`qx>%Ufcx)51bISBIsUI91 zEH8)Q1CGV{9yJC8{I04#c;GoT<#(&qS1(noK40~gDBjW}4DeT=RSSbOed(&t=X>d; zdi~O+Fn{S%z5ZEf^Uubx``c0}_m2c_3T!ov{)gJ-3+4Y1Rqh6U1TvrZ5@*XheSJIb zmz4*1gqPj5i;4F%DvDu>BC$_QGf`ym*jL0)GHV7~U*GP2wrXOyzaoNy3v(m8v(?wH zHqszFyW87)_((x24Zt5^2&Mg+6^Oq?JXYkHdfrbOhDLcKf}Vc!RC#xIWXLJxAu&Hp zQ<^@+MV6|;UZ7bdCy+NjyWI!Lt3%di$MJm>Eb36eT&>k@c86GJ7{s*R^rEL)BwmyN zr;(54JU)yulY4b_gu&<*FwDq5)5ve0XM0yR1H|~)zGpcont#2S{PR!Noa)-Kt!^)q z$?W{Yr-Olwjlkg2Kiq*##`S~F#Z`}IbLs*qO}4 zL?V$YNdqlm$-c%~v>$XJ^B1UtDwsf({eaB$yLTo@SXWF7i@aQW9*JZdU!7 z>h)6T%$dgnx0)_#en}&LDop;^yyehW-LP05KCJ0uXYx!>{Th-We?3h8@_c8ve~fL$ z4DqaO_YKFx^w1YRk^l^@7xP0KqDuN>X3~7iKFH>BM=s=v55rD-x^0Bd4y0-ROn`<86t&kmCdD_T>aOE4cMYWQU%_nKk z-d@kKV-cPw^?F#nu}^|nD1u}kLV$rRBfJSL3T`O%+*ZP@gff)bXgTOkPtT6lqnE0p z-3?j1+b&j1x<2d>bxdzvbPNx_c_jB`9{+rh7%4SfYGFx|y5W9SU_^^-$z8`JSWfG2 z`W91(I2bzclF$nFxa!*=@aR^};}~+w45^<3m|_?x{mH?Qxr0=8ASc(e5+iYKIPUpw zB}^6~`~q1ZGXKbSL%RL``|>3-F<&Axt$y*NUwQ|hl^A)~*z4U3 z9QJO@W=J^A_}6-W6z@+Co|GVU(%1?N46t-q3GfW%jsw7}rPan_>3#CS+i$C#L@(86 zj-~51@~ljW)rTvhI%40B|6q7cq=ePvNCP*;C>eH2iB|An%P}S<@Esxp#un5d<9QUT zS<&*39%=6MsZ$d{^lWeEb9%Nk%VL8`xepU^mmNsb-)SpI5nOBuQ+yE%x+JO-(X72-lRvE<&Zcp9bHT z*&nsQ8;NBf-@E9}+;Q6;)afCT|V%$&^BlYOf zxasuiiPL5RA|-}RC?b!RRif}+U9;YW5>5}TDYGv`_MxU#k~y;QBKEMsdcGc%b^vJ9Io@#0|1w$bGj1ln$P z7VtLbbXAfQqa?kw#Jm?yBrDZ;*e+Z80GW(2jBPD~S>zdu3R7ri&I;%+LuW!Q5#|quhYz$C;`^v1#)45q#q5sDCM!SNuIOv7r?bCEHA32?g}H|3lEID~d(Icgdj z84CG4zTR`i>ts&(<&Bk<#*4q~m%ZrbB*m-<95IuD__PP8;(~X&S*i)N+yI+CgwmFj zqBV=G7Tgfq-v!Phn@n4Q8#hc+pm4iD%lf>aPff)ZY`UU&$p@ixx#S1Rm%gNg1>H=N z$*`zDeym#ukNs#eyNA(!NIrJcgf>-r7Y58_0I2)>?V}eEa8DNdF-7MfpLui`A+?Ak zHLWzIu!(Jd_ld(n3XzuO>6rB^U%CFmg)5`zAdvi|Y4j^!`HFRKdFcth;U2B-F$*Tm zWwqAt?lCKP>C0c!Z#4rG-ey`Ix`T{*+;BfI;zu)Grr!xmn-+z>7C=HMO)a5UH`3J9knkm4T z6OiWqQ|D)1xOR<`jA9!6+sc!>_g&=EOazYo6k_5Ln|Ha~AL5Jg_(AkAx(MM5_dzdg zKBp1J=56|mmIqHVswhf|%|4*Bt=DgPl0nLl&E0#@p2a;KY&H}>m!7v5fb@m!N8Z_< zEHB$^%i=`(?QbO}#Ol=cI~t`l{3&|^cLzsnfBMwE`;V4}f}5Mcq2+(H3z^JrfB&xg zhg^@>yxz6Pt{-wY)9U7o2}>hz%%e2PKPOk;YjK?#<2s*VQY;UBkK%{^MVXQo@7XMa zx8o7g{gg~3AWUdVV#s$jy0*Y-V$(BOu2)V%ARJa+qS*N~7c6lTLQ|OVBSAB9yX8tO z0Zz1BWMek|fNkz{h`Sh%5g~k7Xv86nh+wGoU@yM4w6(ppy`9NGO93w|PM5>$CEJ4| z+pxWtRi#(l*hBz`D&>V%SAcT3ZcVnYNy*nQH6dT_25A^m7 z;uFR&g@b)X^1*&P1!ApF-EY9~;vVD_GvtS{#f<=hg zQw#O<5@_+G4I4jyzEl7TO6NpT$RQLfRB$I#hU8_+tZ|1_DoJj33581IAPLk|1)z2+ z$|jjqD%onSVMO}s>F?ga6kFIhsHou3u_z^p#XpG^;?fr!^869kfQa?7HGD2e{d8lGUbUjl)Fh5PKFnG~CO6^R*nrw<*zTsSd@C9 z<#99;3-=VW+$d*3d!jqhh4@$`;zl;zv z?XsHhJ;*jK5{9itK5zJ-BlViN-Hkx6*F@Q&4ba@A*nW-&P9{_>IvL2^7qH>Z+HU!S7)j4i{+9(xgE`+2MgCcMRWc+MJ1}=3 z;AMuDRtZVVUO%(+8nV$8%*pU;{cxS>st?eTW^`=@gNq|v+wZfhv&$!~tq_$b&1d0$ zbMlt#-6ZQ?@$+s zc<^w)Tw`XtRUR@lM?){>wwqo!-I(+J4o6tIa%E>FY9NGZ4Q|0IIMrf$%Ee_sOb&>t zZ#Wto8}s#g0#5jIh2X`la!7}P8hTN`kizyCyQy5*^5B6<;#uJ(nWx7+gGk7f%Y$Gl zMb|chK2pl>FM~WK3xy0UV{(S*f$HB`E$p=%nL&SAZd8qkn-fg|=6}DixX842RYqaM z)?2#`H&(Av7##HALo`V9oQ?SA<^dau4Z@tz zIZ2A?oQV_HK5~fb?WS(flxLY)-1Hb4%LzqA6V`AIVFm;G++aGnUi_i)r^AwZ(DG2QZ`gp>Q6nLIM z{=-Nu+TDJR(b#o{GGsLN2pc04ibx1Qm|3%GZ}OXTprN%jX8&K?AJ94LR$-9E6oimf z>>NmH_u>6iJ7iO-t@l5~h27;V=k=L;*fRf#0~+F?M<2UKo0|fdsyu4 zW6Jk8&qYoC;-2iy8>K=a1sYr>s>f#-)Ziox8LQRl^GcGDN+x5;T+U)iX>ZyjWFcUs z!qbqh)Zvr2S_efEZJ-KbEXHImEotZPMd^PBA>^e_>CsT}WZfKu9Mf;cs_)0_@|j60 zVMZ_^a#U!_~JZ6Q_fV38i#8It= zI<=yd`h6CWVVY|^rF<2lm>LI*b_`5T!~lTY1%D-;K2yVQ1S!ueShLL%1?9)@VERzm zLZwoVNR$|qP=2nfrhkJ_^4FPnwoXk2Ns1m;Brg*&gXT$Y2p?TiEp{Lwh=`3kVGXQE z2BwM%?;{SQu)S&6jaC3}m|c8=3+=z7{-4y_^Vd4VyX%bx z;ZY!-vcd_}D5VmKeTXh{W!_>d*-Mp@4h*>=iYA-2(I|b+M*6g|(wdL25=vfV^Rd%% zQYKS{mz&J~J_>U8FQ^7pXW1GU`S!f&W&kkE~*WNHM z1CEXj;*R`m@BPWPef_oPmjP>ZDnqQjY=N}8T-Feik6HO_+KOO76a^W7ZFZ~n@j?nH zb5PKgPr=zsyTL$<5dV{tb8SQD9d5<;nr%d$q0m{kNt5T2ciNZ2By77A|w)>mu*&6G~N zR2hNixg&DZs>h!ol>9M5h|;MCnnp33&`5-faHV275}?G!EE`CMSvEAUZ6wRCKVBz= zBXvsZk}O6PQI_h2Hc*jR>nY^wRxfU$;|qC^4|6`gUzdak=B!!!)RqZ;QpuYYR$kA8Cdn|!@soLMk^ zdi(Z#V*7?*WI!F>H~xp)u$)a+5E`7#R(^gn^?Xt@m9c<^xwtOOAKR5o3=-1AjsoCF zqsENGRLm}wFb`7&A_pr6+Mls+{2B|SgVs(E}piRag*EUQ*Bl&oX2P#YHq66YLyzLp-^4xro!ji2pI6(VTE}?agyTB z)|-S6bGgS)-}odRWmW|{oo4(QwRrtuD@S-_q}XgQpq1s%!Abl8^8F!#&RyH6py zv!6jcXFnG`{85zU#|R-*6oDc(V=@^%K9T5&t(~1BWMC01C06u-MPN>53LJB!TW8kE z<|^SVtoJh;@d)3jBR6%sNX)pU5{8kcke-eRA`whNDpwa&Ur$fKrYOzAH46zKb~+$9MZ2L2>%@%#oX-kDUAP@$^6 zL_+?Iys_bMu&DhRIS|<0Wl=lE=vkk^hBP<>|HKUk`$yC;DTGD;4*S=ABG@db3%T}6 zozz~@Oj}zHM+G#k!2Gq`yh+~rjzH*lG*ck3v(o^2lhPBGkxJ`LVzbSeS}(FBG^O<- zxp{NW)OwGl@W0^Q(~RabYTSPJ$A28c)HxF2zVwyXu9JvnKT4=m4^un2xjAy(_!GkH zciwt?RR=+_9vMaO$g+oh4!aYH!8oLdNYvCjWtFpA z@I-AbXCLj9BF@{lZ@%|osnQTYK$NR5UY?oxX1CovS0u2z=Rmu(ZktWQVKvsM&o{?m zW2Vu=!@1V)0-=b6%#*;}Ji*;AITnQyg4pJ$$)pj}+_9983h=Vi#aHk{$-Us8p_uq` zG#Uu7sPT!x(B7W`Um1o}VtpNOsnRp@)EV|xe{9?L7uZ{Btu{T4WA}QOmn|0UOSL)f zTl}A_e@Xii|C{Q+ruMhFfB5DX8-KL%N9okmSIK|FzrToo6;d%ghKHY=6a?+#NMUNz zJ3a!MZDU-x-D#Dv_WW~y!R!6P`02B!U-kK3WuL)EkAj-UGq(CQIV&%n|9CO@+hwOHcN;wotCKV-@YuD^*=L}|E(EV^R z6k60ctb}0>M0Ni8`LmV{F}1cB7DUfZy!TD=9BcGY5X9ByiUa&mdujV z8$w}Eq|Qp7O2iIYE>Qg*7Zy2Xa*_y~A%r|((GwI5PSBjJ%DzCb7ilAhoxSJ*o_q3y zY{KhKr3lugoQmyjwp0Id$NN4jdymf^7+^dIJW{L&ePUftLydHJxV?`on^m#VLXn3> z0JDbk^9Fb)-sU8Cdict%&f9uKrQzF=?fUbCLI{-Iu< zMIt#c2yw!3nu!vy4T8zx@n~J`K1TqVKxV&WZH{zsW5L0e6^tx3F>C^r+%q$7ayu>! zb5DQq7x`gxmLa)`4VxDGocdrZU4@lGEsev7PqZbq2f|XoULfXlG%Q5ZW>V0c4X-zs zGnd!P=3LI}Z8%OlG-okcuP2KZk~6t@-et;RcsMKZnAubn-D1^bj>RkKt+YnExDDBS zbJKA)EnNn)A&!qoPxaEW_Ggauq0AD;=Efwfp^~iK@j2Hf0X&bu)RGiZaseQy~jy&0bO4pDlB`{Ikjf;^aHEh?=jVCC+7^+n@)EYwG))QUTjiw z1C#9W+=*4gXc%nOXdJB?m)cfE0k_xJnm>oJMB2ePeG4nrc79GcNXB;)VIi>_PaZ^+ zB+7|`ZYAdfj~?BD@`Ro52Ds^yXA3Tbq+p;o?CK2!C8)}}s?o8yXyuzu#130C%jb1F z^3BapGxxb5MWK2JJEf8Z%HV{nQhHhyd(&nwZCKG5bX2&LZAdHiEr-oh8&_;Wjx3xn2`PbpcTW} zN{i5{6{u!68G4m7nR}VujWa|c;^AepYVQkr>~1$XZj@7NPoCa}y69ev`p=$ArSmmW zbue^!@2SDQzO^ip%hnZGfhcv&KGhe1{HU~t=MN1k@S3+)sx@S{Yv_4xCbefL0Sjkn zWD-;K#HDlz8J+egKK5JDOxJAGT*Pl(na%!ANs(;#aP(65{j$9g1A84GF9W7QOremGFpS{x`@C5o(JIgyM zZJw(Van4j&y|r36>lgjZNvnyJAQ2(fxz4T(k&v+#7ini)q`l2WZf+iKAnY9;?y%3p z%}uH~IAU-nhd#ER2hR@m7LBJ}!v zJ?zsrFksXRX@pF^Sj=bGRiSQZD)(R^&vAlGDa?^M>zVTrC&yz~8;kDug!~Q@XAo9a z!$_nM42#8Jp9$!|q@i;N!&XJH46~~tDT}hYUBO_bl!+BmhtUt;zkNI6EbTnnK4{o% z3lF!;4NDzOq&?4e8NFlqwYH^uy#d(yq8eUo(mj!}fsh~E=W62q3^&hN@#>-Q!a&YTE~*(|kKsP@f| z|LVpXUnm$ho56lP>BA`h)I3Yizr@LXU}m-q(njJ@GRNj}w;z~RSzCW$bM)xjc~kz| z&g%IupRa0v;Thh1V7tSccTQde50Ok~5*7`-qcG&zTd8SsK3_1oTuMQU@UgtbJ9qSk zgT3LlJ6w=_|0+70pEzHZfPOOa%gh%?1#JUm?Vwm-B8V3Ko)^Va?S{+XHn{oA+UtwXqtAEJRd#BM7`B25PZFv3iL zeefN=DXo3<(Hhdiw?OpG6HmI`3(@F;yP3s2eAEF*H5|jYqcq(ex>ow&gN4G?tBUEg z7AEE}Q6UV*(%0DDrgTRO^Ln9B4O8qJj&pFd<_)0n4vk1*BF%T5%6RnbOvhi6qUglQ z#6@}{L5tg)n_Dr?o=Dg=nZh_H%adwE!LHm*coU^fpt#RuDnkSqi`A*BjzjN`6Y>K@ zRp(}zi=a!Fv)PDrAK`(`8s?+X|NNh|E(G4Vy0M{}D-7zD2a+ib*`OerL(tc_V3)}` zk%qmnupnt~m<568Wfn>xk~h{%9GGJmz~rSqun}u(+Bh4GD^2S{r>)U&;8Q8AY=FVo z$Oi)XHC(J^1A#1(QY6tN6RxJ~`G^xpnHnH-=g<3u;x0faKHtZzHn9&N6~qC=#!2}D zyaKxh5Q1)ZkbSzm%gb$goMrSl+os34+&k|8&~)$KgG^ZEMZ>668^m_@{P~ET;~^9| z+}jNXJQf)o{Wp8v?!?*(LcCImv(MFp+r3e+_aQiqu*Gn)D|=yMX^C{m>BIMKf;QVho3mvrwlZ5;**ev0`sT6CB(u{yG4l>>mpli|#uH;8#bmbc-W>?XKG$ripyQ$+}P?_MM zBSZjs92%-2JbrAqg9GTcyYEQsMn=MPWMt0T60tEPEQ?2yJBDq&e}B#jA)7%dnrfr3 z@8IBnLt5wBGo_Q(ulY4$?$`Vp2;aiO*RQ?y>en?l3=m7X{QA1x&SJIEsFun{Y5)Dd zALjo4-zQ%*{+RJ~?(JV{O5fZNJl754a;>fP^hBeiRwEp*wXC2BMLd=c9_9Ae=}*1J zWPM@!+E3w|=B?Ih)k2}2Dzg;xrmS%XQpa{~qa7QCR@>GpzwoV}uVk)V$#i6_ z&xma8tp?TW*IxcYeROegRI@XYH@KbV-~Rrik<`?NV z0%x%f{8{yTt~BDIb7E-3zMen!mXCPU+p&N9cG&#Rzm08-jBK!|c{@X>P^{IQ&XYsQ z`D53^=GT7I;kb}ov|?p`$*RrG4xx%@EW@4>&73Kf1%li zx;&pGJc!pEi?y{y*-!;7)*8yrcT%Ws$UhREPnYXzX<%*9Q}zef04XF{)XnIgbk%N z45cWB5{49wVkl|dqe2!4|L!~QX0z>4QEZM1*&wx7UwifP-c9x#lPW2GUYDb=o5fSQPrQS+8lL0H2L`q@=ha|g(K@w7wx+C$h2T|U zwH|wvXY`O7Mi@+87@za%!1A)K)<_KW#twTmjdI*KRq_L6UhA?*XwSse z)i7OMowv67xkLOqGxA)^HL8_1m(dL@qX$?9ENb3XYoT&Q=QB%&=56Ki_P8D^*!RQgnlMYZ&CPlH7AK6RH^+Qqo9R)3+wx(F zljX3WCSuv#RvT6_{tw)-j&0C{6Z(B3?8Sd%)aq8_Ai2u%8??kQ}e~LsjcaE`7 z`Oex?V(e47lgY39bzzFgz4rR`*GPoC!Jao5^F%s}4#$|MHt!T66p@fulV?s(Cu4UX zZyg-&uid|S_tE-JG@UDE4_6i*FYg|fnT_g$<-=U11ZC##@}v8YcjD>9;nv#I+c(~S z|EBh8i-yNy$xMtL*Pcm1znMrLUqja!Hw3t1_p_TJH^k(mwG4tCA7q}8$kxy?RPldkM!n%AqiUfPM3J96hcgd!4h?acX1 zN?+SfWb*N~#Rrd`Z0sE5D)kb8EE~J=bioi5T1Xtk;qHi-9WJNpc(8Ea;a)Oo#cV29 zRcs?>K`&$u_Rx+s&d^hbduz*2kZUQI*j`&%xPR-`?aT%38f&#KwQ%=!@|o*=&7fR! zp2Pjnh0`PbOm{reRv!EC#nZm_9x0Wv`wRAfE?iq%>ivQ5pMXEm@u2{Oi5>_qO;(## zfTSGFRw|V%rF85NB1gEo+1h-1XJ=w~bmzgs%Erd##^zo!GXhJrH1@)|g3dALgv_qM zWU~1Kez!N!+uz^YHvl!lHLTIh?(X!kAF2`W;3-_68umT+`s}G8zrV>ZFfYq+I?VHY zVdQWNt{!&cWqc{MuS>Wt9&WSiM3K2iIN4K9o8!Tg2lp11cMcMTaP=P0S=o*CK6=Jn?r@gqk=9$!4T_O-9s{r-{Du)YJWxVF2$ zJ$C)&7hZnll@~8xnz?l8+{D=UTug-Jzs7pR`8@ltQU@3K8Regd3Z~!5a%dNS%T$lp{FMnJKTC2IHMV=`CL|#WMVWSUX&8aEY=S;clWlo_Y*~GVnAW1T5kwau~62_DNquqk~a_h zv3M+=f{9B8Xu}dTSJ|q>+$lh^!cY!WSL07Iffm41p>irMX!|0qoY=knushZ zSg$3K$-(`24SO8qjYmU*P=dUu1gtfRktihW&9&qvL>Kfde zZ$krha0ovcP*fTE;mV55CiA3GuN4!~DD+a>8|yH}e!770@b1s-pBkIk-_l+!$99(5 z7^Ds!X{C8xuC}JfXs@FUTk1fVtRY-aH4#;vHTZY5ZL?-Wm&EvQV84wLF4k?HxBq zv|K*9eqAW{1)Vn4?jJopKIn5=MGos#pufkbN*wsSGO@auUbX~uMn*TeY__GPI2y$2 zQ1omvldsJVi*|1i=H8VWRV>b)!O=daNmNv~A5{GO*~zo%Z0amH4J_?$y# z^;+YlcNJZZwFO*q=m9&+ghlUesiYKzjugv<vlkLcG0hB#eZ63kYBa^}o zJI0Z$Zs({CB)i9})xNP;baCKSJGG%bRLV%3R_>nmd+Ih=jas3IKXAcK*yjkHunXBx74o){@oimc!LM znvBLXd!tTMqb!eIF*9Z&Qz?5;phkM<>60f30CoGgMzLf_oJ(@}or1wDp|dlmLiUBl z@BI8P-N}~1G-wO^9_-|&LbMoPe(=DM?L#lVaQSr5-q_P#&Zc40luE3uF$Ka#qNEeE zD=<8|aO?dK>a|8gy7A=kZvOE*Z&mE4&zu{qZ^dA{yp`op0*8RSMVNtFETjf{P^;;c zie9f*i`k#}zF~`O@p{5EQw{qro*r9?72%iR(u}!q2><^dt-v3orz5dzOJuCq;F#^& z>mPlT%LRk4zm6uV5#i5S7t$pv^sTov>ahH2()LpG7xCs_W^|)2!*S=Mcu@iq z;Va6_PJeJ_5P!J}Kv+B5eh;Z-)^Hrxdb*fmPRW-(TEX8^rD(+)eY|*x`N1H?0S239 z#~^N343ooZ)QP0jbNe3lQmOG)g8e3KIw3r$N@ieEOy%U(fp$#? ziJUp_rb*UTIp~6u(MPwI(RcA;L$Rrr4{k&aB{V)UIXTjAQ7|xjr-B$X7@kq&oundj zX5`ehYhEvq6I0i(Uq93D7HVK9O4$ll=xWvAnbmT&n!vcO5GU z@e!wyK_(f)IXZ3_yrKOC&(pm!kwYkANFtTJr%#DN7=@r=vl};UBnyuoi7+wdU#{1Y zQqx^y(>V+>fQlO#2zIF7?E(>+ldT5F64{m2Y|Rdwti6_9TghhYHRk9MPclc3C}}dF*;Zx0eufgBlKp?x-hs6@@e{ z%3EG}`g%{6zLR>h2EE;7=LHJASe-jSL+}UuiIQt(RMnyGqS>3hX^DupkQt zmEcKB_v)JSsIWD?UCxddZbU--<>jQ|%Qs1P(;GglU zAxA!1;z*3rSfNxZ6fKq_i+F_6Z{o2(LrBMu;^bhBj91 z9%lW`B53@fT|ESD?*zsm0j*@tt<9hC1Hgo}0825UEZ*tHCHfBz{44^O2>>^cwT=oA+JLB^J`!67V9rp2|M$+e-!Vg9&92L>*QZBUOwE@ zC`F&%_(dGb@QXK|MoW#xJ#fCj<*hwkymwDKWsr>xT?b7zAb$YKEEJel$)KP>)Tosq zvMARKSW+1^ElhqyBY!hY`}@N^9+H34Z1qd_w%6vCu1OWbHjTNoc))kZ7^f-JZH zYFM3FoC{OPHF-e*So7%Wjcz|WnmRG@^rO#rOSkkGZF`ui`87B!(TB zR0W0*Uw!y4%b0$WR6C*T0S+K+9hjKl7P+2jbGf%{n%3qlNRAw*$IgVa8i$7#pK8QP zDpgByJcC4u&son(*_u;6A;S&ZH_7Jd#?z;b;=-;{Qg#-!`DT%O%KPU1Qje;I?Uc~N zyw6uKd1=8^Fg$pI6+2sZO3qqVZui1#XxZz7#Oon#;?fQ+lHhT`;W7fJ6ns~Z9;4W@EQ+?({gmaR!9ye)uyX*??MkdpTWhN%X>ak3$z9%FE!5!1@ z#FUl8N_IuxUWt(ySs`29RzG|q>2gPiS>u?ip*Jb4^bzN0c||FgBc!Hr=r!C&{~@06 zB0Sii%k^_AgnlYVtC@Ime9%ra%ub5hhDPIu6{^h%l0mp9hRqnfVa5mE(^V9B!ek%>_G0COi6aBr;`6Dlz zzhMygg#kzMPDbr#K5A4_*v2jZkXL*9cH*2pZNKQqxU|18khz<3u-j@M9_wp8W>32= zrthWg&Wz)NHaI}Ic4%(2g|=hS<1kQ#)uZTeh&q*^X)%RHMnWcbts9cT;y~-?YMR|M z7gzU6cn0^6o@uq=ZzdFxkW0Z-D#-DY<>9SG2yT6o;8y%jhYeN6vw9_aI6OJ1=uz-E zk2iLcd2nf|Tuqzva->|yt-}q`(`1cz_yazt!)4|oo>~JtF?K#&pM@(VlZhli2aWkl zHASgqa(eaR#bHzV-~oKv-P+;A26Jje1x`}c`w!Q10`o3@woho19j;zx*~qFbbP7#= zs?TL6>7CWhWWLgfc#LYX5L-s6qQwTR68n4H4pp2#mW8kr493iL-fXV%W|dXPhC!0a zPEYx{>JHx9sdBE#scfdoX;wC0SR|Aq4I|ga&rK&{xyGDre?KK! zeUq$}DMn00F$55n{e6h(TrfROrFwe6pe?bo*BF+4ruOLed+&YtBwjG!Q#lsRfS4ml z7R)Ztc{oaAR>xD9E?yWmSF@`NlHDbiH3*Hw+};NB61NH2s~#BuW0n;y7F{R2#cL7- zpHC31-u}}N8%+-M1)uSe{6fb^GDb0fuy+aH2otBLd!G*)Yht-3wfS5 zBzA~r*)~fZjyL#hHcgJtLH)Iakh2bU3fk!Kkg86NjUx=WKxb0%vooV|Et5omA5~R7 z%;pa_DOFX?e!oH_N%625fFVl^Ed-fR)7jgEgBf2}+05|f?tbt=o!r*WuCFsQnC)HY zM<7FHm6F-%QcpI^yeV{Q`pm_dS1tqs;{&~umzn8|X6d(*S~-*4-^Wm>g;Ae~zr3@s za1X7voG4Y$&Xn%&7o7kJhDrN;$g->7~;)l`enm*`XzzP%*-8e@7CipL^KQpF&bF2 z6^mkhp}ugJ<3oFa-4@FHcjMXLgY^6DCX3P_<>;O#U?$9_zrhnZ5Q;~O#Hrd%VR!o{ zy)F>i`DyO5-)nb(f+LF9aYG_|m|(LeQT6+SUMrJ5!n#am$55^99)iQh^sK=dn^Lb6 z(H0m5S|T7hBuV6re024}14?UIqru7c=1+FXfpv}6vz?!`%VIgfjAG)3L7_K*8mJd+ z28LNf6s2-}3zR2e7+kel2@2IStnyxrHE%-UQ#S`(vh9ATG#8J_=Dt&tHy z3^O~CFfrx^K&2~0!~pFH^mqu9+$4#EdG4zpY(=*Z>hJ|pNaiDizQI{t*0BFUjKE3! zITw5MeuB6!oIB$o@rMtzH<=jFXndou-e`7tDwC2Oy{KWYV+&Q=PL%9+M-dWp=CxX2 zUaX-9!(WTg@@1Vk#38#wR+3*|Tg?#WoS(U_U1N;G@Nl~pQ*G>@+h!w@KZxMYW{G~V zzaQNPjGTW6w}>F9LYN1Nz!j#A+MN68S{#NqK>imdh9DyC86LKRT1ZzAE@#sb3G3<2 zn>NP@T&7a&+XkO8!NBnUAdLUqy>s_8r55vJhCilL8aab*33Jom?wm(t?LGq{%q%7{)t6%-^%E=c$=_)q=PU*WQeRjGb{psas3xz9jI~Jq(6+a$Os&Xs+l{PjKy-< zd)Z>iXxt@oD~w~v2=GGPxKq`#v}Ca^FIz3;vPJtQTdh^=7r*8yo*qdJo6Wl|6 zlt0||uQ0B%V6~~%(HAaVIptUNs)^n4ow|JGm6?!Q+j+F`aI?y`Xf(`RW0;N1!gn(h zXGyiv(CiN$t!!p}=Pz8uidf!Wc&LrnYs`C$D3?}m-T3z798@Hp{(z}gS-*Yz?s{4F zOuhKh%jW{JHqPYF4TBQuoce~MMNTMJ?ogfJ!^K4>>7LXE)SksxTtOh|d zQh>lY-}G`s(OI;ry`gmWoy>NRqeN$rBFw~?({z_X!L$fzc&%of%r zR`FUDjiBV>JD|7g@p9PvbU&U!=IJ;b9g}i=9rt(Qx$wx-z2p0*dOb{3Vew%5$JsqW z#`k;d90wJKYHBc*gwqa{9H?gV5EEB`F_mEwtkU#Z4EVyHCNo@|@SU4CPuS^@v^Gb)h+R8>(0nT>vqHR_PY`%yj#6b>%x9CnYi}Xy0U1(1ePgo(DSWZ*;CYp?7vvZ~zVWmVF z_dwE`s4;T+^2v9hXWZP}ZREZET38kyKU{D~dnwJ7DV4^?22JP8JGiZ%I(shRzUtCW z)J5i{58nNNc?;B@#UYz&4gHntuUxz+idq*Ex%+L0!?VA=Gw3TC8mWb$-8kh4RnnR% z7Tfg%Lr)qbb!Mj{VFRB0FyTHv;Smx2VmX`s*FWjN(f9VB{MVUtnw6eCdw6*69DVR0 z5P+q&)kvxr?iJj`UATKegU~su?EBGwv5j(Ai^W8u2`O~B%w|Kgn#RxFeq1mLkMEuxR~jcU!2=$L&1x|VGA(2V zCIWh97bc95>6%O%dz@<9da4bKpPo8>dVGBB)Oq-0S4(xlWRZA*RC4f4Je6LxYj#@K zL4Rt3ZD71XL`4Z(IgzX852Fq%SB+At4RDo0D!O|6!|y)W+)TjiC@;AO&R)23=9J6I zOMO%JXWBc6N}3bzzwg=E@!X8ZZ)zO3GO6**EKidq(h})QaQ*c!5 zH#R-yvu)cRJrGUO17|{Z1$N`a&E``x!}<|7j!1}t1s-nPRZLo*S%yUD(zvE9T)(a; z3*@DjG=2}{B0?|R)joczAF>o7ZR{=df+;6UWLzx2J^em;UkvS$3*>HhKI1l9p)fuZ zwK0cUi3GL)OLNKx1_;;(?--k!eET+~7cY*E%{@P#gt>1=-4O#(GESC6<@&-)O?c8;z?pz>YOuDe?0oiT;a~br5wV@XosWlc* z?eg?=`8v@A$9Jz>{E&fK4>V`qn(@wjwWTgo0jZb6x(;h%{0gsrUESHEE4M6^~;jmTm|)s_(p0 z)uid#O|N%r>m-d$Aq_KPw+|3HzTBKHvjP^nwY9lf@$LmS6ma9Em&ljCbTVI;V}%}q zE0c^HhQ0harAfuwYsys^bWwm?cHe(h8UMb)I*l`Ge-i6Snh zZ*HNeC*LqFn1bA91u1e@oRdmglk~69eg7*K+|mDQ@~v&RcGBC_Qzn{cl61|)t;Aw0 z+(a-q0gBC}2tv~>zsWlRL9ZA4CGMohsByo4oIumNJZF0HWMH5?F!1Dwp(#u~$L585 z&gAt*qm5|P>owZ)cVFjZJ|~X}Es7)Ot*iHlxN1E&V!bbk4opzo&MjDmriaAo+`_tb zsF~*n$n!(SyGVStM1aVnrEJ}1tyZ#}V3i7mvc+61=aqUnZ!nQo!i$Re765$qy8Cs|sznVo@yRe9>H1l}1jNZS_)4wVd8il}bL#n^+-;Y~%Ae3CWlWEz9LRD2=KV zkg3$jRzxc(R-V{2e@*8J;1m!8m_=g9R#lLy1}{tDYi5%Q>MJsrSiHpq08qmazzjmV z%S&}$0=HKyl_*!w*CmOsS4#zhl42bYB@x#1HA1CIg~^g@+BFqP*90P{%+H%>YH+m% zry@mcc7=M?tWtxR>mtRwirFI64H+5bi&c)6i-j5|OPpLa!aYUgP~#cr*UFX{f>ES__dceMs1Kv;k2PdRm%u`3xCj_%;{G=3UPbUR>a3TeEBtJ`lDMX477rK-i`b)>UZBHA43SZU5`S9o5BKuPC$#ctOuKv!5)p41C@n@yRs7V6mA z$<0_V6xvj1vUOsgMP<$kJBPTbkZ2IJ4_^naK-KqjTd`DcH0q_I%}QufJKuiNT7xCF z+1#|=k!5PFa~7wCQ)N_MmesBk`DX=Dv6-Z>In?XGwBs1kB#foM$Y}v6jJ-e>`FsrC zisnJUUPOY?asU7$YGCt`FO&%<2&7TdL4d4sLkrZZwGy7J*Cm$=sBj-r@H!kavm1M! z_mh1$^M0bnPFVa~v7jYSt{F%QNPWVgCM_-H^MH7^-?-E{ zjf+$5H9*igMsqovRnMf@zOmNO{8q_GW`IURM_Ft}gA}U<0j;!ZLOr@C@L@+8KbHAQ z$rWVhd^;sx^Y3T!4ktV7LJ_JJi6_vNRr0a@{gd`XRv&`jx|K-6sYNQA&w&lDaGKX8 zp?$duF)6iT3O^kjs8+0CUZ%Fk#@>$h_Ie?GVjE0>YF@no9-5A)JQi~ zXlg z#=^oz-i&COni{m=E5jaP%twT#>)tR(UBtw&VJ&3T++VO$bRgG08;XGfwf`R&XuC!L z004La49P=a9#9Yj;F3JM z6;K#LUsp*GWl-NXLKEA}k7$7&wiia&F_>m&V7Xn1wRSyr*j>11AK-<3g?IJ?3hgia z107{;c~-VnS}Za&6FA9E=Qnow|#k}$Dp3+ zndet}1?i36gZiqkHd2u`N>ToeQLIf;lFd*Cf&m5y2FeEh*Gv{idjmlbZLyh|nXf(@ zLU43nI1b}yHZzH(_8Y^hdTNK>Qt1{im>}sGx`rMoRhk{oPD|O@?6L}_R9?xhOUyEQ z{%6YUCjE!$SG+j(5|%BzRE(#5S_BOz@q`$Xzeg=9ysD$#)y;@93Pc7kc6HCobmsVj zTW{0dlRw~D6|6G2{uME1bb2OwAP8|D52~;`Itn58PdBKBdc>{7OvEetN9q#1eKxa` z{zwf~u#Qs6X<`L;Ds618BYNo0CYtIXnMS3~6F=uZXcB&?@DCMyu}TB!HqpaWd`Gnh z)QWr5ekHJHTZuRQUT6FTzm9YIC$YgFbt?WSo3*px#@V6|Rh&3MnR2)-^dYi*r5=0F zqxR_-XW8!&?n$h@qub1nlM%|?(>GC*DM8#gO8o*2P>%Xn><@aU!<_mEUJW<6G@*ZE} zeszlc9oIUAF5@3%orF913jaB=g5HGe>)#f!N9A|{Op^t0Tt^ayzki;!Cq1op*H0@5 znNeImGt11(%uXT*Gcz+YGc$8yI%ej}F*ECCTJo#xRQGhhrmt#x5fIbKt%}U5S*&C`i`mKh zY~n-q`uhERk$3qr-)0}*<>!2fUrKyWk(Tf`eNR8r4E@`mMQ)@!PK(_M?gU-s9(GUY zYWI|TS~t4q+)KLIz2&~4JKVS2clEOSzWb$KcYlqX_C&p-{`zV(F#5DU#(jcO#wcTy zG0GTaj507J%F3+9gM6DFziG#0zg0_NWfjqN!SXNLpobm3=>|ZQWZjnJQ>HPlJf7qE*YaN~^U-Yqee*v{75MRok>(yR=(J zt4;0d(CIouXX-4St#fp~F4kqbTvzByU90PLgKpGKx>dL7cHN=7bhqx&{dzzT>LER> z$Muw+(X)C>@9I6huMhN*_Up6yvc96P>TCMCzCmm5cu)b9vD+m6M|rMnP`m0&NPl<&)K^Q|+7Yd$33D%G{lL z8T2IBy$5o8a^EfgRqngtb~7M|z7F~!=vPp6qo4C+?&bU}2vX5ru`S!_?JQ)^_A(Om zFBgYAcc}MgVC=5Wjr6^&KGYFuR&;gz&5B*Ya(m*>+qWU%e}h@k)x;HZfI;@gqb*`q z`r36CIXvBl`tDs#{RZ>v-JZ%nVHRXBHLD@b8E~%oY0rV?x41nO-CMrceVbzOQnM1` z;xM4aa=QImV1)UN?%QP}iet@6C|3Rt`{r}z0b?y^NvNs(DbQ;E*mUl+ZVroo2uwGB zpi6ScR=()1A-J+{Tkhm;A& zWxj)!K;OVOjMK<6$d29{Dj}>bNo)~=o|bl^O;N!gnpqvSQddt5Mc*XU&ng5HMppf6=t590n(@~=A1c_;D+sC z2boWHkkm0RlGlk;_ac8}IE&{=1?Q8(G&_e&*g4^r1I$ITb{LT+qP|co^6}gw(a|_ZQHiGYwGkWzgpDS^{;j(-EnuY@E5_L zvRkd!G2BlSv;?NcIQHM2(}lZ(@(ke_K0Z@;o{!HG9u)pENJ+_T;ep`+OL<_9Wtdx~ zGEa%BMV#C_i$N-Ps`V;ef6VWIg%Y_p`~`K(3eNK_w@YpYKuerg&qo#|k*|wHxp}~1 z$NbXPack-^8yRXNcjbl<@;9HeOmZfH@^ax0Hs`|B$R>1hvOb+Yo7PmfwkFZS!2t&0Js#T;{QuP)pl zlv^ch8r-5;%_S?HlzLT#upc|~687==+IynEaO_T86AOFgTD=)Q7Iup6P_Je5H|w1i zh zGHi-f6}%*>URC$G)W0CPWt=r>EeoohM!6tGpeGN>IK$X@8zxB?g)^<&1w@+v3G1D^J(s^GOP2=?S)|(zY zMj`9!t**VYWm3<{z=0SSalK0a4rr_U&*o&FaGuZUBstrFzKKS1mH_>P7XbxyuEUm@ zF|JHB1As%KX=VHOtIQ(xevsKGd*U(3Z1LU@H!d69lUbnNrc8(A1z-+ItsUIFX9A$( zai?-;!Vp}jd#g5e(^oqWRI@)u>m8E*Oub&|+pSk&y$R`;)Ekz*I9VUfEW}`>Ejd}i z25=q(%Sg^hZ9CR!KqqOTfp4+1o(k8OZqDs&bHpMciM=@;dXoadFd67X%|dOrRgU8$dH$@ddx7})xbe)rVIFo8K3Ojsl!%V35B%UMks-?tWV9v6_~ zNuH&KF{X?<_I>g#8k+uQFpb6){fuuJ1Y4Df20F{w$_P% za2lQE71*CUc#u)1+~k>JTA6;#w__N>Rx`{DXPX&m#<0VTH{;o3CYvej#mG19em*H> zCR4&1o?yjNrrAk+PD$%#)|9Ye=1>XyMM?WdNjtlw&5_!DeNIOh^zb`;Y>eglp2rDi zoQL(yPkiKuvE!#b|H!iZ5}+$S*)sfC@>_e=c*(k$hN_w%s)?fN;#HGG^@-=7NId2F zr^3}d|IG67yJ-lsWH;3(Ag!nG`_{_j+?C6@%gVW{A?L1+oV&Vu;zFKrp8~-c;Eyph zVuV@``*()575qhQ2j4@@(&=iK>!(#D{r-iFsG(!?0r2x=UWH!(et8r>0Q^ey{}a9u z_>J(qV2#e(Z!N>`r1V#!`Umi9;lBv~0{Fe~pM?(rf3RFm9z%qYnW~SWDKiK#VZoj} zFwP?d)YiWZfwmaa0lA<1S#K(}FZ0~YvLTh+0e_5fW|S(FiyWmB8C7)BF%-n08L_iyaI@PX0k^0EkiBYn-Ps|&Jg|H$1)7iem$o8 z2BPmRrGb>XS{n+dysD9?y2gA1y=Y^8004LajM4*a1qmF);hFzF)#jmWjHd#D@07ChilML(X8CnsMvy+?6BNi) zCucXqQPb0Ni#TEZrO9cWHoMUVlQ?H~VR{yq{AaKFLvL_<+rrY!Jnq?aqxtpm$flc? zmE$S30cdr=0gZk)A5g#(Hh#*~6Rao$~JHy&!Nw;JUzLf%if@AtfO_p`Os>(6Z10 zIKNy=+Yi&Y4-ernJcZ}*5?;ewcn=@p3w(ngX!J3ZcQBH%Ok^sTX9javz!Fxlh7D|C z4~ICxRk=3T=PZ}F6?fon+>871ARfkJcmhx189a{{@iJb;8+eQEb`KxmBYc9-@CClY zH~0=e;1~SP%mNl^@s?_7mSaU$W>r>aP1a^z)@MUDW-HpNwx+FXGq$14+M;b{TiJHD zlkH}EfgA^MupA?ixn0Wchh!?g~QBjiYFklkeuIZF1Fy<~6MMLd|2Pn$IdYEMPU;U@T;fTEtqln00Ci>(x>=fNYlz>69)Q z9%i>zkMv3(3{SCNt5KSy8OBVuXthd~OvnI;A3=I$P=;h!Mr2gR;F#ZH_$~B3TdW#l zacZc=t6`R)hFhWCsD@cV@f|!QEk9aJH<&ljX&AuVGtu&6{}%&tbui~K4!5c zw#TkG5GUY7oP?8c3QomoI2~u;Oq_*_a5b*M9qvE;r?$!g# znBzWTHiZ&*E^X+}YPNeuC;GcHy&24CCfi?RTIt>WJFr>=)<}W1$^siO3ic0SgJ?@v zS+XqbvQV4cyKU*+Ce5$b>fMv5ZZsLj=n3ZD9j418gejp>6$V}$5R6{95T}2He3moBCbQf{vdG&1MQbb4S>ry%X6Gmy*9#3M(H{tRb4(<8$#o#W9z)m`>}OC;VWH38!gb5psOjQ_w_{8PB&ACoQt|AswnD;^nY_@ z%IT`Wa$QFj9yg@E+?1-lCFOi;V7YFOYPaZ)z%t$C_^Ipf#?k5WsO4JZQErTm+!ph? zGbR;%VK5^Z&s05>eD4jP`;Z>h{o(UK_&ive?!!ox7+qsuF3=*a&`S5&GiF)zOg;_$ zu5anGRy)o!alDtup_TmLkXKOiANjP9@5=!>x#;PdtGJqLxR&dukMku#L9KHrp24YTInP zR%?ycYMs_=gEnfDHfN)<(b>$naFa^+ZDL%tt+@;K(EnVkAM>|q_d66f$1hH+s)k~i zRbX_-=m;S-Cwb&AO15&HSjbnQS&-Ajb+H|`)BJ}~h&^~OE&l>0;q(`H0Zodv6#_v3 zME~sKZaErW0hBHOz6o*a=wfh8txO1xk3- zY0zT8h7&#lkeI+XTdpn#jM^nasUV(f%*)S z000000RR91000313BUlr0M%91RqCtis{jB101V9x%^8{*nkHr@W-~K0Ge7`90002Q CLkb=M diff --git a/apps/web/app/fonts/GeistVF.woff b/apps/web/app/fonts/GeistVF.woff deleted file mode 100644 index 1b62daacff96dad6584e71cd962051b82957c313..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 66268 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 >-Aun5A4Gp>4U0IQJ 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 G)cz05(PHWV diff --git a/apps/web/app/globals.css b/apps/web/app/globals.css deleted file mode 100644 index 6af7ecb..0000000 --- a/apps/web/app/globals.css +++ /dev/null @@ -1,50 +0,0 @@ -:root { - --background: #ffffff; - --foreground: #171717; -} - -@media (prefers-color-scheme: dark) { - :root { - --background: #0a0a0a; - --foreground: #ededed; - } -} - -html, -body { - max-width: 100vw; - overflow-x: hidden; -} - -body { - color: var(--foreground); - background: var(--background); -} - -* { - box-sizing: border-box; - padding: 0; - margin: 0; -} - -a { - color: inherit; - text-decoration: none; -} - -.imgDark { - display: none; -} - -@media (prefers-color-scheme: dark) { - html { - color-scheme: dark; - } - - .imgLight { - display: none; - } - .imgDark { - display: unset; - } -} diff --git a/apps/web/app/layout.tsx b/apps/web/app/layout.tsx deleted file mode 100644 index 28be03e..0000000 --- a/apps/web/app/layout.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import type { Metadata } from "next"; -import localFont from "next/font/local"; -import "./globals.css"; -import { AntdRegistry } from "@ant-design/nextjs-registry"; -import { App as AntApp, ConfigProvider } from "antd"; - -const geistSans = localFont({ - src: "./fonts/GeistVF.woff", - variable: "--font-geist-sans", -}); -const geistMono = localFont({ - src: "./fonts/GeistMonoVF.woff", - variable: "--font-geist-mono", -}); - -export const metadata: Metadata = { - title: "Create Next App", - description: "Generated by create next app", -}; - -export default function RootLayout({ - children, -}: Readonly<{ - children: React.ReactNode; -}>) { - return ( - - - - - {children} - - - - - ); -} diff --git a/apps/web/app/lib/helpers.ts b/apps/web/app/lib/helpers.ts deleted file mode 100644 index 052066c..0000000 --- a/apps/web/app/lib/helpers.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { type Address, NetworkEnvironment, Utxo } from "@minswap/felis-ledger-core"; -import { NitroWallet } from "@minswap/felis-lending-market"; -import { CONFIG } from "../config"; - -export namespace Helpers { - export const fetchBalance = async (address: string): Promise => { - try { - const value = await NitroWallet.fetchBalance(address, CONFIG.networkEnv); - return value.coin(); - } catch { - return 0n; - } - }; - - export const fetchRawUtxos = async (address: Address): Promise => { - try { - const utxos = await NitroWallet.fetchUtxos(address.bech32, CONFIG.networkEnv); - return utxos.map(Utxo.toHex); - } catch { - return []; - } - }; - export const sleep = (ms: number): Promise => { - return new Promise((resolve) => setTimeout(resolve, ms)); - }; - - export const checkTxConfirmed = async (txHash: string): Promise => { - try { - const network = CONFIG.networkEnv === NetworkEnvironment.MAINNET ? "mainnet" : "preview"; - const blockfrostApiKey = process.env.NEXT_PUBLIC_BLOCKFROST_API_KEY || "previewb6Lj4mtJ7suOIgYjOAVzweDaKONGNSUJ"; - const url = `https://cardano-${network}.blockfrost.io/api/v0/txs/${txHash}`; - const response = await fetch(url, { - method: "GET", - headers: { - project_id: blockfrostApiKey, - }, - }); - // Status code 200 = transaction confirmed - // Status code 404 = transaction not found (not confirmed yet) - if (response.status === 200) { - return true; - } - - if (response.status === 404) { - return false; - } - // Handle other status codes - console.warn(`Unexpected response status ${response.status} when checking tx confirmation`); - return false; - } catch (error) { - console.error("Error checking transaction confirmation:", error); - return false; - } - }; -} diff --git a/apps/web/app/lib/use-nitro-wallet.ts b/apps/web/app/lib/use-nitro-wallet.ts deleted file mode 100644 index 96f51b8..0000000 --- a/apps/web/app/lib/use-nitro-wallet.ts +++ /dev/null @@ -1,252 +0,0 @@ -import { baseWalletFromEntropy } from "@minswap/felis-cip"; -import { Address } from "@minswap/felis-ledger-core"; -import { sha3 } from "@minswap/felis-ledger-utils"; -import { NitroWallet } from "@minswap/felis-lending-market"; -import { useAtom, useSetAtom } from "jotai"; -import { useCallback, useEffect, useState } from "react"; -import { setNitroBalanceAtom, setNitroWalletAtom } from "../atoms/walletAtom"; -import { CONFIG } from "../config"; -import { useWallet } from "./use-wallet"; -import type { WalletInfo } from "./wallet-utils"; - -export interface NitroWalletData { - walletInfo: WalletInfo & { rootAddress: string }; - privateKey: string; -} - -const NITRO_WALLET_STORAGE_KEY = "minswap_nitro_wallet"; - -export const useNitroWallet = () => { - const wallet = useWallet(); - const [walletData, setWalletData] = useState(null); - const [loading, setLoading] = useState(false); - const [error, setError] = useState(null); - const setNitroBalance = useSetAtom(setNitroBalanceAtom); - const [_, setNitroWallet] = useAtom(setNitroWalletAtom); - - /** - * Fetch balance from the blockchain for a given address - */ - const fetchBalance = useCallback(async (address: Address): Promise => { - try { - const value = await NitroWallet.fetchBalance(address.bech32, CONFIG.networkEnv); - return value.coin(); - } catch (err) { - const errorMsg = err instanceof Error ? err.message : String(err); - console.error("Failed to fetch balance:", errorMsg); - return 0n; - } - }, []); - - /** - * Fetch UTXOs from the blockchain for a given address - */ - const fetchUtxos = useCallback(async (address: Address) => { - try { - const utxos = await NitroWallet.fetchUtxos(address.bech32, CONFIG.networkEnv); - return utxos; - } catch (err) { - const errorMsg = err instanceof Error ? err.message : String(err); - console.error("Failed to fetch UTXOs:", errorMsg); - return []; - } - }, []); - - /** - * Load Nitro wallet from localStorage - */ - const loadFromStorage = useCallback(async () => { - console.log("nitro loadFromStorage called", wallet.isConnected, wallet?.walletInfo?.address.bech32); - const rootAddress = wallet?.walletInfo?.address.bech32; - if (!rootAddress) { - return false; - } - try { - const stored = localStorage.getItem(NITRO_WALLET_STORAGE_KEY); - if (stored) { - const parsedData = JSON.parse(stored); - const nitroWallet = { - ...parsedData, - walletInfo: { - ...parsedData.walletInfo, - address: Address.fromBech32(parsedData.walletInfo.address), - }, - }; - if (nitroWallet.walletInfo.rootAddress !== rootAddress) { - return false; - } - - // Fetch real-time balance from blockchain - const balance = await fetchBalance(nitroWallet.walletInfo.address); - nitroWallet.walletInfo.balance = balance; - - setWalletData(nitroWallet); - setError(null); - return true; - } - return false; - } catch (err) { - const errorMsg = err instanceof Error ? err.message : String(err); - setError(`Failed to load Nitro wallet from storage: ${errorMsg}`); - console.error("Storage load error:", err); - return false; - } - }, [wallet?.walletInfo?.address.bech32, fetchBalance, wallet.isConnected]); - - /** - * Create new Nitro wallet using wallet.api.signData() - * Requires connected wallet (wallet.api must exist) - */ - const createNewWallet = useCallback(async () => { - if (!wallet) { - throw new Error("Wallet instance not provided"); - } - - if (!wallet.api) { - throw new Error("Eternl wallet not connected. Please connect your wallet first."); - } - - if (!wallet.walletInfo) { - throw new Error("Wallet info not available"); - } - - try { - // Placeholder signature for testing - const message = `Create Nitro Wallet with address ${wallet.walletInfo.address.bech32}`; - const signedData = await wallet.api.signData( - wallet.walletInfo.address.bech32, - Buffer.from(message).toString("hex"), - ); - const entropy = sha3(signedData.signature); - const nitroWallet = baseWalletFromEntropy(entropy, wallet.walletInfo.networkId); - const nitroWalletData = { - walletInfo: { - address: nitroWallet.address, - rootAddress: wallet.walletInfo.address.bech32, - networkId: wallet.walletInfo.networkId, - }, - privateKey: nitroWallet.paymentKey.toHex(), - }; - setWalletData(nitroWalletData); - return nitroWalletData; - } catch (err) { - const errorMsg = err instanceof Error ? err.message : String(err); - throw new Error(`Failed to create new Nitro wallet: ${errorMsg}`); - } - }, [wallet]); - - /** - * Save Nitro wallet to localStorage - */ - const saveToStorage = useCallback((data: NitroWalletData) => { - try { - localStorage.setItem(NITRO_WALLET_STORAGE_KEY, JSON.stringify(data)); - setWalletData(data); - setError(null); - } catch (err) { - const errorMsg = err instanceof Error ? err.message : String(err); - setError(`Failed to save Nitro wallet: ${errorMsg}`); - console.error("Storage save error:", err); - } - }, []); - - const connect = useCallback(async () => { - setLoading(true); - setError(null); - try { - // Try to load from localStorage first - const loaded = await loadFromStorage(); - if (loaded) { - return; - } - - // If not in storage, create new wallet - const nitroWallet = await createNewWallet(); - saveToStorage(nitroWallet); - } catch (err) { - const errorMsg = err instanceof Error ? err.message : String(err); - setError(errorMsg); - console.error("Wallet connection error:", err); - } finally { - setLoading(false); - } - }, [loadFromStorage, createNewWallet, saveToStorage]); - - /** - * Disconnect and remove from localStorage - */ - const disconnect = useCallback(() => { - try { - localStorage.removeItem(NITRO_WALLET_STORAGE_KEY); - setWalletData(null); - setError(null); - setNitroBalance(0n); - setNitroWallet(null); - } catch (err) { - const errorMsg = err instanceof Error ? err.message : String(err); - setError(`Failed to disconnect: ${errorMsg}`); - console.error("Disconnect error:", err); - } - }, [setNitroBalance, setNitroWallet]); - - const clearError = useCallback(() => { - setError(null); - }, []); - - /** - * Auto-load Nitro wallet from storage when Eternl wallet connects - */ - useEffect(() => { - const autoLoad = async () => { - const rootAddress = wallet?.walletInfo?.address.bech32; - if (!rootAddress) { - return; - } - try { - const stored = localStorage.getItem(NITRO_WALLET_STORAGE_KEY); - if (stored) { - const parsedData = JSON.parse(stored); - const nitroWallet = { - ...parsedData, - walletInfo: { - ...parsedData.walletInfo, - address: Address.fromBech32(parsedData.walletInfo.address), - }, - }; - if (nitroWallet.walletInfo.rootAddress !== rootAddress) { - return; - } - - // Fetch real-time balance from blockchain - const balance = await fetchBalance(nitroWallet.walletInfo.address); - nitroWallet.walletInfo.balance = balance; - - setWalletData(nitroWallet); - setError(null); - } - } catch (err) { - const _errorMsg = err instanceof Error ? err.message : String(err); - console.error("Storage load error:", err); - } - }; - - if (wallet.walletInfo) { - autoLoad(); - } - }, [wallet.walletInfo?.address.bech32, fetchBalance, wallet.walletInfo]); - - return { - walletData, - loading, - error, - connect, - disconnect, - clearError, - saveToStorage, - loadFromStorage, - createNewWallet, - fetchBalance, - fetchUtxos, - isConnected: walletData !== null, - }; -}; diff --git a/apps/web/app/lib/use-position.ts b/apps/web/app/lib/use-position.ts deleted file mode 100644 index 8a31d29..0000000 --- a/apps/web/app/lib/use-position.ts +++ /dev/null @@ -1,576 +0,0 @@ -import { DEXOrderTransaction } from "@minswap/felis-build-tx"; -import { DexV2Calculation, DexVersion, OrderV2Direction, OrderV2StepType } from "@minswap/felis-dex-v2"; -import { PrivateKey, Utxo, Value } from "@minswap/felis-ledger-core"; -import { Result } from "@minswap/felis-ledger-utils"; -import { type LiqwidProvider, NitroWallet } from "@minswap/felis-lending-market"; -import { CoinSelectionAlgorithm, EmulatorProvider } from "@minswap/felis-tx-builder"; -import invariant from "@minswap/tiny-invariant"; -import { App } from "antd"; -import { useAtomValue, useSetAtom } from "jotai"; -import { useCallback, useRef } from "react"; -import { ADA, Asset } from "../../../../packages/ledger-core/dist/asset"; -import { LendingMarket } from "../../../../packages/minswap-lending-market/dist/lending-market"; -import { - type LongPositionState, - type LongPositionStatus, - type NitroWalletData, - nitroWalletAtom, - type ShortPositionState, - ShortPositionStatus, - setLongPositionAtom, - setShortPositionAtom, - type WalletData, - walletAtom, -} from "../atoms/walletAtom"; -import { CONFIG } from "../config"; -import { Helpers } from "./helpers"; - -const CALLBACK_SLEEP_DURATION_MS = 10000; // 10 seconds - -export enum ExtraStatus { - EXTRA_STEP_BUY_MORE = "extra_step_buy_more", - EXTRA_STEP_SELL_ALL = "extra_step_sell_all", - EXTRA_SHORT_STEP_1 = "extra_short_step_1", - EXTRA_SHORT_STEP_2 = "extra_short_step_2", - EXTRA_SHORT_STEP_3 = "extra_short_step_3", - EXTRA_SHORT_STEP_4 = "extra_short_step_4", - EXTRA_SHORT_STEP_5 = "extra_short_step_5", - EXTRA_SHORT_STEP_6 = "extra_short_step_6", -} - -type StepBuyMoreExtra = { - extraStatus: ExtraStatus.EXTRA_STEP_BUY_MORE; - buyMoreAmount: bigint; - nextStatus: LongPositionStatus.OPENING_POSITION; - logStatus: LongPositionStatus.STEP_BUY_MORE_LONG_ASSET; -}; -type StepSellAllExtra = { - extraStatus: ExtraStatus.EXTRA_STEP_SELL_ALL; - nextStatus: LongPositionStatus.CLOSED_POSITION; - logStatus: LongPositionStatus.STEP_SELL_ALL_LONG_ASSET; -}; -type ShortStep1Extra = { - extraStatus: ExtraStatus.EXTRA_SHORT_STEP_1; - marketId: LendingMarket.MarketId; - amount: bigint; -}; -type ShortStep2Extra = { - extraStatus: ExtraStatus.EXTRA_SHORT_STEP_2; - borrowAmountL: number; - collateralMode: LendingMarket.CollateralMarginType; - borrowMarketId: LiqwidProvider.BorrowMarket; -}; -type ShortStep3Extra = { - extraStatus: ExtraStatus.EXTRA_SHORT_STEP_3; - priceInAdaResponse: LendingMarket.PriceInAdaResponse; - amountIn: bigint; -}; -type ShortStep4Extra = { - extraStatus: ExtraStatus.EXTRA_SHORT_STEP_4; - priceInAdaResponse: LendingMarket.PriceInAdaResponse; - expectedReceive: bigint; -}; -type ShortStep5Extra = { - extraStatus: ExtraStatus.EXTRA_SHORT_STEP_5; -}; -type ShortStep6Extra = { - extraStatus: ExtraStatus.EXTRA_SHORT_STEP_6; - supplyMarketId: LendingMarket.MarketId; - qToken: Asset; -}; -type InnerHandleFn = (input: { - position: LongPositionState | ShortPositionState; - wallet: WalletData; - nitroWallet: NitroWalletData; - extra?: - | StepBuyMoreExtra - | StepSellAllExtra - | ShortStep1Extra - | ShortStep2Extra - | ShortStep3Extra - | ShortStep4Extra - | ShortStep5Extra - | ShortStep6Extra; -}) => Promise; - -type HandlePositionInput = { - position: LongPositionState | ShortPositionState; - positionType: "long" | "short"; - errorMessage: string; - successMessage: string; - innerFn: InnerHandleFn; -}; - -export const innerHandleCallback = async ({ - position, - nextStatus, - extraBalance, -}: { - position: LongPositionState | ShortPositionState; - nextStatus: string; - extraBalance?: { address: string; requiredValue: Value }; -}): Promise => { - invariant(position.hasCallback, "type-safe"); - await Helpers.sleep(CALLBACK_SLEEP_DURATION_MS); - const txHash = position.transactions[position.transactions.length - 1]?.txHash; - invariant(txHash, "type-safe"); - let ok = await Helpers.checkTxConfirmed(txHash); - if (extraBalance && ok) { - const balance = await NitroWallet.fetchBalance(extraBalance.address, CONFIG.networkEnv); - for (const [asset, amount] of extraBalance.requiredValue.flatten()) { - const assetAmount = balance.get(asset); - ok = ok && assetAmount >= amount; - } - } - console.log({ txConfirmed: ok, txHash }); - if (ok) { - return { - ...position, - // biome-ignore lint/suspicious/noExplicitAny: lazy - status: nextStatus as any, - hasCallback: undefined, - callbackExtra: undefined, - }; - } else { - // continue callback - return { ...position, hasCallback: position.hasCallback + 1 }; - } -}; - -// supply ada, receive qAda -export const shortHandleStep1: InnerHandleFn = async (options) => { - const position = options.position as ShortPositionState; - const { nitroWallet, extra } = options; - invariant(extra && extra.extraStatus === ExtraStatus.EXTRA_SHORT_STEP_1, "Invalid extra data for short step 1"); - if (position.hasCallback) { - return innerHandleCallback({ - position, - nextStatus: ShortPositionStatus.STEP_2_BORROW_TOKEN, - }); - } else { - const utxos = await Helpers.fetchRawUtxos(nitroWallet.walletInfo.address); - const txHash = await LendingMarket.supplyTokens({ - nitroWallet: { - address: nitroWallet.walletInfo.address, - privateKey: nitroWallet.privateKey, - utxos, - }, - networkEnv: CONFIG.networkEnv, - amount: extra.amount, - marketId: extra.marketId, - }); - console.log("shortHandleStep1 tx hash:", txHash); - const newPosition: ShortPositionState = { - ...position, - updatedAt: Date.now(), - amount: { - ...position.amount, - mSuppliedL: extra.amount, - }, - transactions: [...position.transactions, { txHash, step: ShortPositionStatus.STEP_1_SUPPLY_TOKEN }], - hasCallback: 1, - }; - return newPosition; - } -}; - -// borrow MIN, supply qADA -export const shortHandleStep2: InnerHandleFn = async (options) => { - const position = options.position as ShortPositionState; - const { nitroWallet, extra } = options; - invariant(extra && extra.extraStatus === ExtraStatus.EXTRA_SHORT_STEP_2, "Invalid extra data for short step 2"); - if (position.hasCallback) { - return innerHandleCallback({ - position, - nextStatus: ShortPositionStatus.STEP_3_SHORT_TOKEN, - }); - } else { - const utxos = await NitroWallet.fetchUtxos(nitroWallet.walletInfo.address.bech32, CONFIG.networkEnv); - const balance = Utxo.sumValue(utxos); - const { collaterals, buildTxCollaterals } = await LendingMarket.getCollaterals({ - networkEnv: CONFIG.networkEnv, - address: nitroWallet.walletInfo.address, - balance, - collateralMode: extra.collateralMode, - }); - const { txHash } = await LendingMarket.borrowTokens({ - nitroWallet: { - address: nitroWallet.walletInfo.address, - privateKey: nitroWallet.privateKey, - utxos: utxos.map(Utxo.toHex), - }, - networkEnv: CONFIG.networkEnv, - borrowAmountL: extra.borrowAmountL, - borrowMarketId: extra.borrowMarketId, - currentDebt: 0, - collaterals, - buildTxCollaterals, - }); - console.log("shortHandleStep2 tx hash:", txHash); - const newPosition: ShortPositionState = { - ...position, - updatedAt: Date.now(), - amount: { - ...position.amount, - mBorrowedL: BigInt(extra.borrowAmountL), - }, - transactions: [...position.transactions, { txHash, step: ShortPositionStatus.STEP_2_BORROW_TOKEN }], - hasCallback: 1, - }; - return newPosition; - } -}; - -// short $MIN, receive Ada -export const shortHandleStep3: InnerHandleFn = async (options) => { - const position = options.position as ShortPositionState; - const { nitroWallet, extra } = options; - invariant(extra && extra.extraStatus === ExtraStatus.EXTRA_SHORT_STEP_3, "Invalid extra data for short step 3"); - if (position.hasCallback) { - invariant(position.callbackExtra, "type-safe"); - const callbackData = JSON.parse(position.callbackExtra) as { - address: string; - asset: string; - requiredAmount: string; - }; - const extraBalance: { address: string; requiredValue: Value } = { - address: callbackData.address, - requiredValue: new Value().add(Asset.fromString(callbackData.asset), BigInt(callbackData.requiredAmount)), - }; - return innerHandleCallback({ - position, - nextStatus: ShortPositionStatus.OPENING_POSITION, - extraBalance, - }); - } else { - const utxos = await NitroWallet.fetchUtxos(nitroWallet.walletInfo.address.bech32, CONFIG.networkEnv); - const { priceInAdaResponse, amountIn } = extra; - const result = DexV2Calculation.calculateSwapExactIn({ - datumReserves: [BigInt(priceInAdaResponse.datumReserves[0]), BigInt(priceInAdaResponse.datumReserves[1])], - valueReserves: [BigInt(priceInAdaResponse.valueReserves[0]), BigInt(priceInAdaResponse.valueReserves[1])], - tradingFee: { - feeANumerator: BigInt(priceInAdaResponse.tradingFee.feeANumerator), - feeBNumerator: BigInt(priceInAdaResponse.tradingFee.feeBNumerator), - }, - amountIn, - direction: OrderV2Direction.B_TO_A, - feeSharingNumerator: priceInAdaResponse.feeSharingNumerator - ? BigInt(priceInAdaResponse.feeSharingNumerator) - : null, - }); - const minReceive = (result.amountOut * 99n) / 100n; // slippage 1% - const txb = DEXOrderTransaction.createBulkOrdersTx({ - networkEnv: CONFIG.networkEnv, - sender: nitroWallet.walletInfo.address, - orderOptions: [ - { - lpAsset: Asset.fromString(priceInAdaResponse.lpAsset), - version: DexVersion.DEX_V2, - type: OrderV2StepType.SWAP_EXACT_IN, - assetIn: Asset.fromString(priceInAdaResponse.assetB), - amountIn: amountIn, - minimumAmountOut: 1n, - direction: OrderV2Direction.B_TO_A, - killOnFailed: false, - isLimitOrder: false, - }, - ], - }); - const txComplete = await txb.completeUnsafe({ - changeAddress: nitroWallet.walletInfo.address, - walletUtxos: utxos, - coinSelectionAlgorithm: CoinSelectionAlgorithm.MINSWAP, - provider: new EmulatorProvider(CONFIG.networkEnv), - }); - const signedTx = txComplete.signWithPrivateKey(PrivateKey.fromHex(nitroWallet.privateKey)).complete(); - NitroWallet.quickBatching(signedTx, CONFIG.networkEnv); - const txHash = await NitroWallet.submitTx(signedTx, CONFIG.networkEnv); - console.log("shortHandleStep3 tx hash:", txHash); - const newPosition: ShortPositionState = { - ...position, - updatedAt: Date.now(), - amount: { - ...position.amount, - mShortedL: amountIn, - mShortedEstimateAda: result.amountOut, - mTradingPrice: Number(priceInAdaResponse.price), - }, - transactions: [...position.transactions, { txHash, step: ShortPositionStatus.STEP_3_SHORT_TOKEN }], - hasCallback: 1, - callbackExtra: JSON.stringify({ - address: nitroWallet.walletInfo.address.bech32, - asset: ADA.toString(), - requiredAmount: minReceive.toString(), - }), - }; - return newPosition; - } -}; - -// buy back $MIN, used Ada -export const shortHandleStep4: InnerHandleFn = async (options) => { - const position = options.position as ShortPositionState; - const { nitroWallet, extra } = options; - invariant(extra && extra.extraStatus === ExtraStatus.EXTRA_SHORT_STEP_4, "Invalid extra data for short step 4"); - if (position.hasCallback) { - invariant(position.callbackExtra, "type-safe"); - const callbackData = JSON.parse(position.callbackExtra) as { - address: string; - asset: string; - requiredAmount: string; - }; - const extraBalance: { address: string; requiredValue: Value } = { - address: callbackData.address, - requiredValue: new Value().add(Asset.fromString(callbackData.asset), BigInt(callbackData.requiredAmount)), - }; - return innerHandleCallback({ - position, - nextStatus: ShortPositionStatus.STEP_5_REPAY_ASSET, - extraBalance, - }); - } else { - const utxos = await NitroWallet.fetchUtxos(nitroWallet.walletInfo.address.bech32, CONFIG.networkEnv); - const { priceInAdaResponse, expectedReceive } = extra; - const result = DexV2Calculation.calculateSwapExactOut({ - datumReserves: [BigInt(priceInAdaResponse.datumReserves[0]), BigInt(priceInAdaResponse.datumReserves[1])], - valueReserves: [BigInt(priceInAdaResponse.valueReserves[0]), BigInt(priceInAdaResponse.valueReserves[1])], - tradingFee: { - feeANumerator: BigInt(priceInAdaResponse.tradingFee.feeANumerator), - feeBNumerator: BigInt(priceInAdaResponse.tradingFee.feeBNumerator), - }, - expectedReceive, - direction: OrderV2Direction.A_TO_B, - feeSharingNumerator: priceInAdaResponse.feeSharingNumerator - ? BigInt(priceInAdaResponse.feeSharingNumerator) - : null, - }); - const necessaryAmountIn = Result.unwrap(result).necessaryAmountIn; - const txb = DEXOrderTransaction.createBulkOrdersTx({ - networkEnv: CONFIG.networkEnv, - sender: nitroWallet.walletInfo.address, - orderOptions: [ - { - lpAsset: Asset.fromString(priceInAdaResponse.lpAsset), - version: DexVersion.DEX_V2, - type: OrderV2StepType.SWAP_EXACT_OUT, - assetIn: Asset.fromString(priceInAdaResponse.assetA), - maximumAmountIn: necessaryAmountIn, - expectedReceived: expectedReceive, - direction: OrderV2Direction.A_TO_B, - killOnFailed: false, - }, - ], - }); - const txComplete = await txb.completeUnsafe({ - changeAddress: nitroWallet.walletInfo.address, - walletUtxos: utxos, - coinSelectionAlgorithm: CoinSelectionAlgorithm.MINSWAP, - provider: new EmulatorProvider(CONFIG.networkEnv), - }); - const signedTx = txComplete.signWithPrivateKey(PrivateKey.fromHex(nitroWallet.privateKey)).complete(); - NitroWallet.quickBatching(signedTx, CONFIG.networkEnv); - const txHash = await NitroWallet.submitTx(signedTx, CONFIG.networkEnv); - console.log("shortHandleStep4 tx hash:", txHash); - const newPosition: ShortPositionState = { - ...position, - updatedAt: Date.now(), - amount: { - ...position.amount, - mClosedAda: necessaryAmountIn, - mClosedPrice: Number(priceInAdaResponse.price), - }, - transactions: [...position.transactions, { txHash, step: ShortPositionStatus.STEP_4_BUY_BACK_TOKEN }], - hasCallback: 1, - callbackExtra: JSON.stringify({ - address: nitroWallet.walletInfo.address.bech32, - asset: priceInAdaResponse.assetB, - requiredAmount: expectedReceive.toString(), - }), - }; - return newPosition; - } -}; - -// repay MIN, receive qAda back -export const shortHandleStep5: InnerHandleFn = async (options) => { - const position = options.position as ShortPositionState; - const { nitroWallet } = options; - if (position.hasCallback) { - return innerHandleCallback({ - position, - nextStatus: ShortPositionStatus.STEP_6_WITHDRAW_COLLATERAL, - }); - } else { - const utxos = await NitroWallet.fetchRawUtxos(nitroWallet.walletInfo.address.bech32, CONFIG.networkEnv); - let loanTxHash = ""; - for (const { txHash, step } of position.transactions) { - if (step === ShortPositionStatus.STEP_2_BORROW_TOKEN) { - loanTxHash = txHash; - } - } - console.warn("loanTxHash", loanTxHash); - const { repayAmountL, txHash } = await LendingMarket.repayAllDebt({ - nitroWallet: { - address: nitroWallet.walletInfo.address, - privateKey: nitroWallet.privateKey, - utxos, - }, - networkEnv: CONFIG.networkEnv, - loanTxHash, - }); - console.log("shortHandleStep5 tx hash:", txHash); - const newPosition: ShortPositionState = { - ...position, - updatedAt: Date.now(), - amount: { - ...position.amount, - mRepaidL: repayAmountL, - }, - transactions: [...position.transactions, { txHash, step: ShortPositionStatus.STEP_5_REPAY_ASSET }], - hasCallback: 1, - }; - return newPosition; - } -}; - -// withdraw qADA collateral, receive Ada back -export const shortHandleStep6: InnerHandleFn = async (options) => { - const position = options.position as ShortPositionState; - const { nitroWallet, extra } = options; - invariant(extra && extra.extraStatus === ExtraStatus.EXTRA_SHORT_STEP_6, "Invalid extra data for short step 6"); - if (position.hasCallback) { - return innerHandleCallback({ - position, - nextStatus: ShortPositionStatus.CLOSED_POSITION, - }); - } else { - const utxos = await NitroWallet.fetchUtxos(nitroWallet.walletInfo.address.bech32, CONFIG.networkEnv); - const balance = Utxo.sumValue(utxos); - const { supplyMarketId, qToken } = extra; - const qTokenAmountA = Number(balance.get(qToken)) / 1e6; - const { txHash, withdrawAllAmountL } = await LendingMarket.withdrawAllSupply({ - nitroWallet: { - address: nitroWallet.walletInfo.address, - privateKey: nitroWallet.privateKey, - utxos: utxos.map(Utxo.toHex), - }, - networkEnv: CONFIG.networkEnv, - marketId: supplyMarketId, - supplyQTokenAmountA: qTokenAmountA, - }); - console.log("shortHandleStep6 tx hash:", txHash); - const newPosition: ShortPositionState = { - ...position, - updatedAt: Date.now(), - amount: { - ...position.amount, - mWithdrawnL: withdrawAllAmountL, - }, - transactions: [...position.transactions, { txHash, step: ShortPositionStatus.STEP_6_WITHDRAW_COLLATERAL }], - hasCallback: 1, - }; - return newPosition; - } -}; - -export const usePosition = () => { - const { message } = App.useApp(); - const wallet = useAtomValue(walletAtom); - const nitroWallet = useAtomValue(nitroWalletAtom); - const setLongPositions = useSetAtom(setLongPositionAtom); - const setShortPositions = useSetAtom(setShortPositionAtom); - - // Track positions that are currently being processed to prevent duplicate calls - const processingPositions = useRef>(new Set()); - - const handlePosition = useCallback( - async (input: HandlePositionInput) => { - const { positionType, position, errorMessage, successMessage, innerFn } = input; - if (!nitroWallet || !wallet) return; - // Check if this position is already being processed - if (processingPositions.current.has(position.positionId)) { - return; - } - // Mark position as being processed - processingPositions.current.add(position.positionId); - try { - let retries = 0; - const maxRetries = 3; - let lastError: Error | null = null; - - while (retries <= maxRetries) { - try { - let hasCallback: number | undefined; - if (positionType === "long") { - const newPosition = (await innerFn({ position, wallet, nitroWallet })) as LongPositionState; - hasCallback = newPosition.hasCallback; - setLongPositions((prev) => prev.map((p) => (p.positionId === position.positionId ? newPosition : p))); - } else { - const newPosition = (await innerFn({ position, wallet, nitroWallet })) as ShortPositionState; - hasCallback = newPosition.hasCallback; - setShortPositions((prev) => prev.map((p) => (p.positionId === position.positionId ? newPosition : p))); - } - if (!hasCallback) { - message.success(`${successMessage} ${position.positionId.slice(0, 8)}`); - } - break; // Exit loop on success - } catch (error) { - lastError = error instanceof Error ? error : new Error(String(error)); - if (retries < maxRetries) { - retries++; - console.error("backoff error", error); - console.log( - `Retry ${retries}/${maxRetries} for position ${position.positionId.slice(0, 8)} after 30s...`, - ); - await Helpers.sleep(30000); // Sleep 30 seconds before retry - } else { - const txHash = position.transactions[position.transactions.length - 1]?.txHash; - if (txHash) { - const txConfirmed = await Helpers.checkTxConfirmed(txHash); - if (!txConfirmed) { - const prevStep = position.transactions[position.transactions.length - 2]?.step; - const lastStep = position.transactions[position.transactions.length - 1]?.step; - if (prevStep && lastStep && prevStep === lastStep) { - const transactions = [...position.transactions]; - transactions.pop(); // remove last failed tx - const newPosition = { ...position, hasCallback: 1, transactions }; - if (positionType === "long") { - setLongPositions((prev) => - prev.map((p) => - p.positionId === position.positionId ? (newPosition as LongPositionState) : p, - ), - ); - } else { - setShortPositions((prev) => - prev.map((p) => - p.positionId === position.positionId ? (newPosition as ShortPositionState) : p, - ), - ); - } - } - } - } - console.error("max retries reached for position:", position.positionId, lastError); - setTimeout(() => { - window.location.reload(); - }, 5000); - // throw lastError; - } - } - } - } catch (error) { - const errorMsg = error instanceof Error ? error.message : String(error); - message.error(`${errorMessage}: ${errorMsg}`); - console.error("handle position error: ", error); - } finally { - // Remove from processing set when done (success or error) - processingPositions.current.delete(position.positionId); - } - }, - [nitroWallet, wallet, setLongPositions, setShortPositions, message], - ); - - return { - handlePosition, - }; -}; diff --git a/apps/web/app/lib/use-wallet.ts b/apps/web/app/lib/use-wallet.ts deleted file mode 100644 index a74c4ab..0000000 --- a/apps/web/app/lib/use-wallet.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { useAtom, useSetAtom } from "jotai"; -import { useCallback, useState } from "react"; -import { setConnectedWalletAtom, setRootBalanceAtom, setWalletAtom } from "../atoms/walletAtom"; -import { type Cip30Api, getAddressInfo, getWalletApi, type WalletInfo } from "./wallet-utils"; - -export const useWallet = () => { - const [_, setWallet] = useAtom(setWalletAtom); - const [__, setConnectedWallet] = useAtom(setConnectedWalletAtom); - const [walletInfo, setWalletInfo] = useState(null); - const [api, setApi] = useState(null); - const [loading, setLoading] = useState(false); - const [error, setError] = useState(null); - const setRootBalance = useSetAtom(setRootBalanceAtom); - - const connect = useCallback(async () => { - setLoading(true); - setError(null); - try { - const eternlApi = await getWalletApi("eternl"); - if (!eternlApi) { - setError("Eternl wallet not found or not enabled."); - return; - } - - const info = await getAddressInfo(eternlApi); - if (info) { - setWalletInfo(info); - setApi(eternlApi); - setWallet({ walletInfo: info, api: eternlApi }); - setConnectedWallet(true); - } else { - setError("Failed to get wallet information."); - } - } catch (err) { - const errorMsg = err instanceof Error ? err.message : String(err); - setError(`Error connecting wallet: ${errorMsg}`); - console.error("Wallet connection error:", err); - } finally { - setLoading(false); - } - }, [setWallet, setConnectedWallet]); - - const disconnect = useCallback(() => { - setWalletInfo(null); - setApi(null); - setError(null); - setWallet(null); - setConnectedWallet(false); - setRootBalance(0n); - }, [setConnectedWallet, setWallet, setRootBalance]); - - const clearError = useCallback(() => { - setError(null); - }, []); - - return { - walletInfo, - api, - setWalletInfo, - loading, - setLoading, - error, - connect, - disconnect, - clearError, - isConnected: walletInfo !== null && api !== null, - }; -}; diff --git a/apps/web/app/lib/utils.ts b/apps/web/app/lib/utils.ts deleted file mode 100644 index 43afff1..0000000 --- a/apps/web/app/lib/utils.ts +++ /dev/null @@ -1,13 +0,0 @@ -export namespace Utils { - export const shortenAddress = (address: string): string => { - return `${address.slice(0, 8)}...${address.slice(-8)}`; - }; - export const formatAmount = (amount: bigint, decimals?: number): string => { - const _decimals = decimals ?? 6; - return (Number(amount) / 10 ** _decimals).toFixed(2); - }; - export const formatBalance = (balance: bigint | undefined): string => { - if (!balance) return "0.00"; - return (Number(balance) / 1_000_000).toFixed(2); - }; -} diff --git a/apps/web/app/lib/wallet-utils.ts b/apps/web/app/lib/wallet-utils.ts deleted file mode 100644 index 507b990..0000000 --- a/apps/web/app/lib/wallet-utils.ts +++ /dev/null @@ -1,86 +0,0 @@ -import { Address, Value } from "@minswap/felis-ledger-core"; -import { RustModule } from "@minswap/felis-ledger-utils"; - -/** - * CIP-30 Wallet utilities for Cardano - * These utilities handle wallet connection and data retrieval - * Address parsing is delegated to the backend/wallet - */ - -export interface WalletInfo { - address: Address; - balance?: bigint; - networkId: number; -} - -export interface Cip30Window extends Window { - cardano?: { - [walletName: string]: { - enable(): Promise; - isEnabled(): Promise; - apiVersion: string; - name: string; - icon: string; - }; - }; -} - -export interface Cip30Api { - getNetworkId(): Promise; - getUtxos(): Promise; - getBalance(): Promise; - getUsedAddresses(): Promise; - getUnusedAddresses(): Promise; - getChangeAddress(): Promise; - getRewardAddresses(): Promise; - signTx(tx: string, partialSign: boolean): Promise; - signData(addr: string, payload: string): Promise<{ signature: string; key: string }>; - submitTx(tx: string): Promise; - getCollaterals?(params: { amount: string }): Promise; - onAccountChange?(callback: () => void): void; - onNetworkChange?(callback: (networkId: number) => void): void; -} - -export const getWalletApi = async (walletName: string): Promise => { - const window_ = window as Cip30Window; - if (!window_.cardano?.[walletName]) { - return null; - } - try { - const walletExtension = window_.cardano[walletName]; - const api = await walletExtension.enable(); - return api; - } catch (error) { - const errorMsg = error instanceof Error ? error.message : String(error); - console.error(`Failed to enable ${walletName}: ${errorMsg}`, error); - return null; - } -}; - -export const getAddressInfo = async (api: Cip30Api): Promise => { - try { - await RustModule.load(); - const addressRaw = await api.getChangeAddress(); - const balanceRaw = await api.getBalance(); - const networkId = await api.getNetworkId(); - const balanceValue = Value.fromHex(balanceRaw); - return { - address: Address.fromHex(addressRaw), - balance: balanceValue.coin(), - networkId, - }; - } catch (error) { - const errorMsg = error instanceof Error ? error.message : String(error); - console.error("Failed to get address info:", errorMsg, error); - return null; - } -}; - -export const connectToEternlWallet = async (): Promise => { - const eternlApi = await getWalletApi("eternl"); - if (!eternlApi) { - console.log("Eternl wallet not found or not enabled."); - return null; - } - return getAddressInfo(eternlApi); -}; diff --git a/apps/web/app/page.tsx b/apps/web/app/page.tsx deleted file mode 100644 index eaa7463..0000000 --- a/apps/web/app/page.tsx +++ /dev/null @@ -1,92 +0,0 @@ -"use client"; - -import { LendingMarket } from "@minswap/felis-lending-market"; -import { App, Button, Layout, Space } from "antd"; -import { useAtomValue, useSetAtom } from "jotai"; -import { useEffect, useState } from "react"; -import { connectedWalletAtom, setMinAdaPriceAtom, walletAtom } from "./atoms/walletAtom"; -import { DepositWithdraw } from "./components/deposit-withdraw"; -import { EternlConnector } from "./components/eternl-connector"; -import { MarginTrading } from "./components/margin-trading"; -import { NitroWalletConnector } from "./components/nitro-wallet-connector"; -import { ShortMarginTrading } from "./components/short-trading"; -import { CONFIG } from "./config"; -import { useWallet } from "./lib/use-wallet"; - -export default function Home() { - const { message } = App.useApp(); - const walletConnected = useAtomValue(connectedWalletAtom); - const wallet = useAtomValue(walletAtom); - const walletHook = useWallet(); - const setMinAdaPrice = useSetAtom(setMinAdaPriceAtom); - const [isClearingHistory, setIsClearingHistory] = useState(false); - const [positionType, setPositionType] = useState<"short" | "long">("short"); - - const clearHistory = async () => { - setIsClearingHistory(true); - try { - // Clear all localStorage values - localStorage.clear(); - message.success("Local storage cleared successfully"); - // Force page reload after a short delay - setTimeout(() => { - window.location.reload(); - }, 500); - } catch (_err) { - message.error("Failed to clear local storage"); - setIsClearingHistory(false); - } - }; - - useEffect(() => { - // Auto-connect if wallet was previously connected and we don't have wallet data yet - if (walletConnected && !wallet) { - walletHook.connect(); - } - }, [walletConnected, wallet, walletHook]); - - // polling fetch $MIN price 10 seconds - useEffect(() => { - const fetchMinPrice = async () => { - try { - const priceData = await LendingMarket.fetchAdaMinPrice(CONFIG.networkEnv); - setMinAdaPrice(priceData); - // message.info(`MIN price fetched: ${priceData.price} ADA per MIN`); - } catch (_err) { - message.error("Failed to fetch MIN price"); - } finally { - } - }; - fetchMinPrice(); - const interval = setInterval(fetchMinPrice, 10000); // 10 seconds - return () => clearInterval(interval); - }, [setMinAdaPrice, message]); - - return ( - - - -
- -
-
- - - - -
-
- - - {positionType === "long" ? : } -
-
-
- ); -} diff --git a/apps/web/eslint.config.js b/apps/web/eslint.config.js deleted file mode 100644 index e8759ff..0000000 --- a/apps/web/eslint.config.js +++ /dev/null @@ -1,4 +0,0 @@ -import { nextJsConfig } from "@repo/eslint-config/next-js"; - -/** @type {import("eslint").Linter.Config} */ -export default nextJsConfig; diff --git a/apps/web/next.config.js b/apps/web/next.config.js deleted file mode 100644 index 5d7066d..0000000 --- a/apps/web/next.config.js +++ /dev/null @@ -1,37 +0,0 @@ -/** @type {import('next').NextConfig} */ -const nextConfig = { - async rewrites() { - return [ - { - source: "/api/liqwid/:path*", - destination: "https://v2.api.preview.liqwid.dev/:path*", - }, - ]; - }, - turbopack: { - resolveAlias: { - // Alias node WASM modules to web versions for browser compatibility - "@minswap/cardano-serialization-lib-nodejs": "@minswap/cardano-serialization-lib-browser", - "@emurgo/cardano-serialization-lib-nodejs": "@emurgo/cardano-serialization-lib-browser", - "@repo/uplc-node": "@repo/uplc-web", - }, - }, - webpack(config) { - // Alias node WASM modules to web versions for browser compatibility - config.resolve.alias["@minswap/cardano-serialization-lib-nodejs"] = "@minswap/cardano-serialization-lib-browser"; - config.resolve.alias["@emurgo/cardano-serialization-lib-nodejs"] = "@emurgo/cardano-serialization-lib-browser"; - config.resolve.alias["@repo/uplc-node"] = "@repo/uplc-web"; - - // Enable WebAssembly support - config.experiments = { - ...(config.experiments ?? {}), - asyncWebAssembly: true, - layers: true, - syncWebAssembly: true, - }; - - return config; - }, -}; - -export default nextConfig; diff --git a/apps/web/package.json b/apps/web/package.json deleted file mode 100644 index 4043ced..0000000 --- a/apps/web/package.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "name": "web", - "version": "0.1.0", - "type": "module", - "private": true, - "scripts": { - "dev": "next dev --turbopack --port 3002", - "build": "next build", - "start": "next start", - "lint": "next lint --max-warnings 0", - "check-types": "tsc --noEmit" - }, - "dependencies": { - "@ant-design/icons": "5.x", - "@ant-design/nextjs-registry": "^1.1.0", - "@minswap/tiny-invariant": "^1.2.0", - "@minswap/felis-ledger-core": "workspace:*", - "@minswap/felis-ledger-utils": "workspace:*", - "@minswap/felis-tx-builder": "workspace:*", - "@minswap/felis-cip": "workspace:*", - "@repo/ui": "workspace:*", - "@minswap/felis-lending-market": "workspace:*", - "@minswap/felis-dex-v2": "workspace:*", - "@minswap/felis-build-tx": "workspace:*", - "antd": "^5.27.6", - "next": "^16.0.10", - "react": "^19.2.3", - "react-dom": "^19.2.3", - "jotai": "^2.10.2" - }, - "devDependencies": { - "@repo/eslint-config": "workspace:*", - "@repo/typescript-config": "workspace:*", - "@types/node": "^22.15.3", - "@types/react": "^19.2.7", - "@types/react-dom": "^19.2.3", - "eslint": "^9.31.0", - "typescript": "5.8.2" - } -} diff --git a/apps/web/public/file-text.svg b/apps/web/public/file-text.svg deleted file mode 100644 index 9cfb3c9..0000000 --- a/apps/web/public/file-text.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/apps/web/public/globe.svg b/apps/web/public/globe.svg deleted file mode 100644 index 4230a3d..0000000 --- a/apps/web/public/globe.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/apps/web/public/next.svg b/apps/web/public/next.svg deleted file mode 100644 index 5174b28..0000000 --- a/apps/web/public/next.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/apps/web/public/turborepo-dark.svg b/apps/web/public/turborepo-dark.svg deleted file mode 100644 index dae38fe..0000000 --- a/apps/web/public/turborepo-dark.svg +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/apps/web/public/turborepo-light.svg b/apps/web/public/turborepo-light.svg deleted file mode 100644 index ddea915..0000000 --- a/apps/web/public/turborepo-light.svg +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/apps/web/public/vercel.svg b/apps/web/public/vercel.svg deleted file mode 100644 index 0164ddc..0000000 --- a/apps/web/public/vercel.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/apps/web/public/window.svg b/apps/web/public/window.svg deleted file mode 100644 index bbc7800..0000000 --- a/apps/web/public/window.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/apps/web/tsconfig.json b/apps/web/tsconfig.json deleted file mode 100644 index c2fa4ee..0000000 --- a/apps/web/tsconfig.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "extends": "@repo/typescript-config/nextjs.json", - "compilerOptions": { - "plugins": [ - { - "name": "next" - } - ] - }, - "include": ["**/*.ts", "**/*.tsx", "next-env.d.ts", "next.config.js", ".next/types/**/*.ts"], - "exclude": ["node_modules"] -} diff --git a/docker-compose.yml b/docker-compose.yml index c609bc9..c5ec3ab 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -32,19 +32,6 @@ services: - "9999:9999" command: ["pnpm", "--filter=long-short-backend", "start"] - # web: - # build: - # context: . - # dockerfile: Dockerfile - # network_mode: host - # environment: - # NODE_ENV: production - # NEXT_PUBLIC_NETWORK_ENV: TESTNET_PREVIEW - # PORT: 3002 - # expose: - # - "3002" - # restart: unless-stopped - redis: image: redis:8 container_name: margin-redis diff --git a/package.json b/package.json index 601e344..2222367 100644 --- a/package.json +++ b/package.json @@ -8,8 +8,6 @@ "scripts": { "build": "turbo run build", "dev": "turbo run dev", - "lint": "turbo run lint", - "format": "prettier --write \"**/*.{ts,tsx,md}\"", "check-types": "turbo run check-types", "format-and-lint": "biome check .", "format-and-lint:fix": "biome check . --write --unsafe", @@ -26,5 +24,16 @@ "packageManager": "pnpm@9.0.0", "engines": { "node": ">=22" + }, + "pnpm": { + "overrides": { + "flatted@<=3.4.1": "3.4.2", + "glob@>=10.2.0 <10.5.0": "10.5.0", + "minimatch@<3.1.4": "3.1.5", + "minimatch@>=9.0.0 <9.0.7": "9.0.9", + "rollup@>=4.0.0 <4.59.0": "4.60.0", + "socket.io-parser@>=4.0.0 <4.2.6": "4.2.6", + "lodash@<4.17.23": "4.17.23" + } } } diff --git a/packages/cip/package.json b/packages/cip/package.json index f7c16ef..d44ad3c 100644 --- a/packages/cip/package.json +++ b/packages/cip/package.json @@ -27,13 +27,11 @@ "check-circular-imports": "dpdm --no-warning --no-tree src" }, "devDependencies": { - "@repo/eslint-config": "workspace:*", "@repo/typescript-config": "workspace:*", + "@types/json-bigint": "^1.0.4", "@types/node": "^22.15.3", - "eslint": "^9.31.0", + "dpdm": "^3.15.1", "typescript": "5.8.2", - "@types/json-bigint": "^1.0.4", - "dpdm": "^3.14.0", "vitest": "^3.2.4" }, "dependencies": { diff --git a/packages/eslint-config/README.md b/packages/eslint-config/README.md deleted file mode 100644 index 8b42d90..0000000 --- a/packages/eslint-config/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# `@turbo/eslint-config` - -Collection of internal eslint configurations. diff --git a/packages/eslint-config/base.js b/packages/eslint-config/base.js deleted file mode 100644 index f9aea48..0000000 --- a/packages/eslint-config/base.js +++ /dev/null @@ -1,32 +0,0 @@ -import js from "@eslint/js"; -import eslintConfigPrettier from "eslint-config-prettier"; -import turboPlugin from "eslint-plugin-turbo"; -import tseslint from "typescript-eslint"; -import onlyWarn from "eslint-plugin-only-warn"; - -/** - * A shared ESLint configuration for the repository. - * - * @type {import("eslint").Linter.Config[]} - * */ -export default [ - js.configs.recommended, - eslintConfigPrettier, - ...tseslint.configs.recommended, - { - plugins: { - turbo: turboPlugin, - }, - rules: { - "turbo/no-undeclared-env-vars": "warn", - }, - }, - { - plugins: { - onlyWarn, - }, - }, - { - ignores: ["dist/**"], - }, -]; diff --git a/packages/eslint-config/next.js b/packages/eslint-config/next.js deleted file mode 100644 index 6bf01a7..0000000 --- a/packages/eslint-config/next.js +++ /dev/null @@ -1,49 +0,0 @@ -import js from "@eslint/js"; -import eslintConfigPrettier from "eslint-config-prettier"; -import tseslint from "typescript-eslint"; -import pluginReactHooks from "eslint-plugin-react-hooks"; -import pluginReact from "eslint-plugin-react"; -import globals from "globals"; -import pluginNext from "@next/eslint-plugin-next"; -import { config as baseConfig } from "./base.js"; - -/** - * A custom ESLint configuration for libraries that use Next.js. - * - * @type {import("eslint").Linter.Config[]} - * */ -export const nextJsConfig = [ - ...baseConfig, - js.configs.recommended, - eslintConfigPrettier, - ...tseslint.configs.recommended, - { - ...pluginReact.configs.flat.recommended, - languageOptions: { - ...pluginReact.configs.flat.recommended.languageOptions, - globals: { - ...globals.serviceworker, - }, - }, - }, - { - plugins: { - "@next/next": pluginNext, - }, - rules: { - ...pluginNext.configs.recommended.rules, - ...pluginNext.configs["core-web-vitals"].rules, - }, - }, - { - plugins: { - "react-hooks": pluginReactHooks, - }, - settings: { react: { version: "detect" } }, - rules: { - ...pluginReactHooks.configs.recommended.rules, - // React scope no longer necessary with new JSX transform. - "react/react-in-jsx-scope": "off", - }, - }, -]; diff --git a/packages/eslint-config/package.json b/packages/eslint-config/package.json deleted file mode 100644 index e64f5cb..0000000 --- a/packages/eslint-config/package.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "name": "@repo/eslint-config", - "version": "0.0.0", - "type": "module", - "private": true, - "exports": { - "./base": "./base.js", - "./next-js": "./next.js", - "./react-internal": "./react-internal.js" - }, - "devDependencies": { - "@eslint/js": "^9.31.0", - "@next/eslint-plugin-next": "^15.4.2", - "eslint": "^9.31.0", - "eslint-config-prettier": "^10.1.1", - "eslint-plugin-only-warn": "^1.1.0", - "eslint-plugin-react": "^7.37.5", - "eslint-plugin-react-hooks": "^5.2.0", - "eslint-plugin-turbo": "^2.5.0", - "globals": "^16.3.0", - "typescript": "^5.8.2", - "typescript-eslint": "^8.37.0" - } -} diff --git a/packages/eslint-config/react-internal.js b/packages/eslint-config/react-internal.js deleted file mode 100644 index daeccba..0000000 --- a/packages/eslint-config/react-internal.js +++ /dev/null @@ -1,39 +0,0 @@ -import js from "@eslint/js"; -import eslintConfigPrettier from "eslint-config-prettier"; -import tseslint from "typescript-eslint"; -import pluginReactHooks from "eslint-plugin-react-hooks"; -import pluginReact from "eslint-plugin-react"; -import globals from "globals"; -import { config as baseConfig } from "./base.js"; - -/** - * A custom ESLint configuration for libraries that use React. - * - * @type {import("eslint").Linter.Config[]} */ -export const config = [ - ...baseConfig, - js.configs.recommended, - eslintConfigPrettier, - ...tseslint.configs.recommended, - pluginReact.configs.flat.recommended, - { - languageOptions: { - ...pluginReact.configs.flat.recommended.languageOptions, - globals: { - ...globals.serviceworker, - ...globals.browser, - }, - }, - }, - { - plugins: { - "react-hooks": pluginReactHooks, - }, - settings: { react: { version: "detect" } }, - rules: { - ...pluginReactHooks.configs.recommended.rules, - // React scope no longer necessary with new JSX transform. - "react/react-in-jsx-scope": "off", - }, - }, -]; diff --git a/packages/felis/package.json b/packages/felis/package.json index 532149f..2baacbf 100644 --- a/packages/felis/package.json +++ b/packages/felis/package.json @@ -11,7 +11,9 @@ "bugs": { "url": "https://github.com/minswap/felis/issues" }, - "files": ["dist"], + "files": [ + "dist" + ], "exports": { ".": "./dist/index.js" }, @@ -24,29 +26,27 @@ "check-circular-imports": "dpdm --no-warning --no-tree src" }, "devDependencies": { - "@repo/eslint-config": "workspace:*", "@repo/typescript-config": "workspace:*", "@types/node": "^22.15.3", - "eslint": "^9.31.0", - "typescript": "5.8.2", - "dpdm": "^3.14.0" + "dpdm": "^3.15.1", + "typescript": "5.8.2" }, "dependencies": { - "@minswap/felis-ledger-utils": "workspace:*", - "@minswap/felis-ledger-core": "workspace:*", + "@minswap/felis-build-tx": "workspace:*", "@minswap/felis-cip": "workspace:*", - "@minswap/felis-tx-builder": "workspace:*", - "@minswap/felis-provider": "workspace:*", "@minswap/felis-dex-v1": "workspace:*", "@minswap/felis-dex-v2": "workspace:*", - "@minswap/felis-build-tx": "workspace:*", - "@minswap/felis-stableswap": "workspace:*", + "@minswap/felis-ledger-core": "workspace:*", + "@minswap/felis-ledger-utils": "workspace:*", "@minswap/felis-lending-market": "workspace:*", + "@minswap/felis-provider": "workspace:*", "@minswap/felis-splash": "workspace:*", + "@minswap/felis-stableswap": "workspace:*", "@minswap/felis-sundaeswap-v1": "workspace:*", "@minswap/felis-sundaeswap-v3": "workspace:*", + "@minswap/felis-syncer": "workspace:*", + "@minswap/felis-tx-builder": "workspace:*", "@minswap/felis-wingriders-v1": "workspace:*", - "@minswap/felis-wingriders-v2": "workspace:*", - "@minswap/felis-syncer": "workspace:*" + "@minswap/felis-wingriders-v2": "workspace:*" } } diff --git a/packages/ledger-core/package.json b/packages/ledger-core/package.json index bfec973..2f524a1 100644 --- a/packages/ledger-core/package.json +++ b/packages/ledger-core/package.json @@ -28,22 +28,22 @@ }, "devDependencies": { "@repo/typescript-config": "workspace:*", + "@types/json-bigint": "^1.0.4", "@types/node": "^22.15.3", + "dpdm": "^3.15.1", + "fast-check": "^3.23.2", "typescript": "5.8.2", - "@types/json-bigint": "^1.0.4", - "dpdm": "^3.14.0", - "vitest": "^3.2.4", - "fast-check": "^3.23.2" + "vitest": "^3.2.4" }, "dependencies": { + "@cardano-ogmios/schema": "^6.11.0", "@minswap/felis-ledger-utils": "workspace:*", - "bignumber.js": "^9.1.2", - "@stricahq/cbors": "^1.0.3", "@minswap/tiny-invariant": "^1.2.0", - "remeda": "^2.19.2", - "json-bigint": "^1.0.0", - "@cardano-ogmios/schema": "^6.11.0", + "@stricahq/cbors": "^1.0.3", + "@stricahq/typhonjs": "2.0.0", + "bignumber.js": "^9.1.2", "cbor": "^10.0.3", - "@stricahq/typhonjs": "2.0.0" + "json-bigint": "^1.0.0", + "remeda": "^2.19.2" } } diff --git a/packages/ledger-utils/package.json b/packages/ledger-utils/package.json index c06da3a..9740ff9 100644 --- a/packages/ledger-utils/package.json +++ b/packages/ledger-utils/package.json @@ -27,25 +27,24 @@ "check-circular-imports": "dpdm --no-warning --no-tree src" }, "devDependencies": { - "@repo/eslint-config": "workspace:*", "@repo/typescript-config": "workspace:*", "@types/json-bigint": "^1.0.4", "@types/node": "^22.15.3", + "dpdm": "^3.15.1", + "fast-check": "^3.23.2", "typescript": "5.8.2", - "dpdm": "^3.14.0", - "vitest": "^3.2.4", - "fast-check": "^3.23.2" + "vitest": "^3.2.4" }, "dependencies": { "@emurgo/cardano-serialization-lib-browser": "13.2.1", "@emurgo/cardano-serialization-lib-nodejs": "13.2.1", "@minswap/cardano-serialization-lib-browser": "11.1.0-minswap.33", "@minswap/cardano-serialization-lib-nodejs": "11.1.0-minswap.33", - "@minswap/tiny-invariant": "^1.2.0", "@minswap/felis-uplc-node": "workspace:*", "@minswap/felis-uplc-web": "workspace:*", + "@minswap/tiny-invariant": "^1.2.0", "blakejs": "^1.2.1", - "sha3": "^2.1.4", - "json-bigint": "^1.0.0" + "json-bigint": "^1.0.0", + "sha3": "^2.1.4" } } diff --git a/packages/minswap-build-tx/package.json b/packages/minswap-build-tx/package.json index 80e28c7..e50e2b5 100644 --- a/packages/minswap-build-tx/package.json +++ b/packages/minswap-build-tx/package.json @@ -27,20 +27,18 @@ "check-circular-imports": "dpdm --no-warning --no-tree src" }, "devDependencies": { - "@repo/eslint-config": "workspace:*", "@repo/typescript-config": "workspace:*", + "@types/json-bigint": "^1.0.4", "@types/node": "^22.15.3", - "eslint": "^9.31.0", + "dpdm": "^3.15.1", "typescript": "5.8.2", - "@types/json-bigint": "^1.0.4", - "dpdm": "^3.14.0", "vitest": "^3.2.4" }, "dependencies": { + "@minswap/felis-dex-v2": "workspace:*", "@minswap/felis-ledger-core": "workspace:*", "@minswap/felis-ledger-utils": "workspace:*", "@minswap/felis-tx-builder": "workspace:*", - "@minswap/felis-dex-v2": "workspace:*", "@minswap/tiny-invariant": "^1.2.0", "bignumber.js": "^9.1.2" } diff --git a/packages/minswap-dex-v1/package.json b/packages/minswap-dex-v1/package.json index 5ac1d50..ba06505 100644 --- a/packages/minswap-dex-v1/package.json +++ b/packages/minswap-dex-v1/package.json @@ -10,7 +10,9 @@ "bugs": { "url": "https://github.com/minswap/felis/issues" }, - "files": ["dist"], + "files": [ + "dist" + ], "exports": { ".": "./dist/index.js" }, @@ -23,13 +25,11 @@ "check-circular-imports": "dpdm --no-warning --no-tree src" }, "devDependencies": { - "@repo/eslint-config": "workspace:*", "@repo/typescript-config": "workspace:*", - "@types/node": "^22.15.3", - "eslint": "^9.31.0", - "typescript": "5.8.2", "@types/json-bigint": "^1.0.4", - "dpdm": "^3.14.0" + "@types/node": "^22.15.3", + "dpdm": "^3.15.1", + "typescript": "5.8.2" }, "dependencies": { "@minswap/felis-ledger-core": "workspace:*", diff --git a/packages/minswap-dex-v2/package.json b/packages/minswap-dex-v2/package.json index 2ddd22f..0aeff2c 100644 --- a/packages/minswap-dex-v2/package.json +++ b/packages/minswap-dex-v2/package.json @@ -27,13 +27,11 @@ "check-circular-imports": "dpdm --no-warning --no-tree src" }, "devDependencies": { - "@repo/eslint-config": "workspace:*", "@repo/typescript-config": "workspace:*", + "@types/json-bigint": "^1.0.4", "@types/node": "^22.15.3", - "eslint": "^9.31.0", + "dpdm": "^3.15.1", "typescript": "5.8.2", - "@types/json-bigint": "^1.0.4", - "dpdm": "^3.14.0", "vitest": "^3.2.4" }, "dependencies": { diff --git a/packages/minswap-lending-market/package.json b/packages/minswap-lending-market/package.json index 89fb814..4463a50 100644 --- a/packages/minswap-lending-market/package.json +++ b/packages/minswap-lending-market/package.json @@ -25,25 +25,23 @@ "check-circular-imports": "dpdm --no-warning --no-tree src" }, "devDependencies": { - "@repo/eslint-config": "workspace:*", "@repo/typescript-config": "workspace:*", - "@types/node": "^22.15.3", - "eslint": "^9.31.0", - "typescript": "5.8.2", "@types/json-bigint": "^1.0.4", - "dpdm": "^3.14.0" + "@types/node": "^22.15.3", + "dpdm": "^3.15.1", + "typescript": "5.8.2" }, "dependencies": { + "@minswap/felis-build-tx": "workspace:*", + "@minswap/felis-cip": "workspace:*", + "@minswap/felis-dex-v2": "workspace:*", "@minswap/felis-ledger-core": "workspace:*", "@minswap/felis-ledger-utils": "workspace:*", "@minswap/felis-tx-builder": "workspace:*", - "@minswap/felis-build-tx": "workspace:*", - "@minswap/felis-dex-v2": "workspace:*", - "@minswap/felis-cip": "workspace:*", - "remeda": "^2.19.2", "@minswap/tiny-invariant": "^1.2.0", "bignumber.js": "^9.1.2", + "cbor": "^10.0.3", "json-bigint": "^1.0.0", - "cbor": "^10.0.3" + "remeda": "^2.19.2" } } diff --git a/packages/minswap-stableswap/package.json b/packages/minswap-stableswap/package.json index 6cf4306..f9e6d01 100644 --- a/packages/minswap-stableswap/package.json +++ b/packages/minswap-stableswap/package.json @@ -10,7 +10,9 @@ "bugs": { "url": "https://github.com/minswap/felis/issues" }, - "files": ["dist"], + "files": [ + "dist" + ], "exports": { ".": "./dist/index.js" }, @@ -25,23 +27,21 @@ "check-circular-imports": "dpdm --no-warning --no-tree src" }, "devDependencies": { - "@repo/eslint-config": "workspace:*", "@repo/typescript-config": "workspace:*", "@types/json-bigint": "^1.0.4", "@types/node": "^22.15.3", - "dpdm": "^3.14.0", - "eslint": "^9.31.0", + "dpdm": "^3.15.1", "fast-check": "^3.23.2", "typescript": "5.8.2", "vitest": "^3.2.4" }, "dependencies": { - "@minswap/tiny-invariant": "^1.2.0", - "@minswap/felis-ledger-core": "workspace:*", - "@minswap/felis-ledger-utils": "workspace:*", "@minswap/felis-build-tx": "workspace:*", "@minswap/felis-dex-v2": "workspace:*", + "@minswap/felis-ledger-core": "workspace:*", + "@minswap/felis-ledger-utils": "workspace:*", "@minswap/felis-tx-builder": "workspace:*", + "@minswap/tiny-invariant": "^1.2.0", "bignumber.js": "^9.1.2", "json-bigint": "^1.0.0", "remeda": "^2.19.2" diff --git a/packages/provider/package.json b/packages/provider/package.json index 9db2571..a5c990e 100644 --- a/packages/provider/package.json +++ b/packages/provider/package.json @@ -10,7 +10,9 @@ "bugs": { "url": "https://github.com/minswap/felis/issues" }, - "files": ["dist"], + "files": [ + "dist" + ], "exports": { ".": "./dist/index.js" }, @@ -23,19 +25,17 @@ "check-circular-imports": "dpdm --no-warning --no-tree src" }, "devDependencies": { - "@repo/eslint-config": "workspace:*", + "@minswap/felis-dex-v2": "workspace:*", "@repo/typescript-config": "workspace:*", "@types/json-bigint": "^1.0.4", "@types/node": "^22.15.3", - "dpdm": "^3.14.0", - "eslint": "^9.31.0", - "typescript": "5.8.2", - "@minswap/felis-dex-v2": "workspace:*" + "dpdm": "^3.15.1", + "typescript": "5.8.2" }, "dependencies": { - "@minswap/tiny-invariant": "^1.2.0", "@minswap/felis-ledger-core": "workspace:*", "@minswap/felis-ledger-utils": "workspace:*", + "@minswap/tiny-invariant": "^1.2.0", "remeda": "^2.19.2" } } diff --git a/packages/splash/package.json b/packages/splash/package.json index 7827981..9a550fb 100644 --- a/packages/splash/package.json +++ b/packages/splash/package.json @@ -10,7 +10,9 @@ "bugs": { "url": "https://github.com/minswap/felis/issues" }, - "files": ["dist"], + "files": [ + "dist" + ], "exports": { ".": "./dist/index.js" }, @@ -23,13 +25,11 @@ "check-circular-imports": "dpdm --no-warning --no-tree src" }, "devDependencies": { - "@repo/eslint-config": "workspace:*", "@repo/typescript-config": "workspace:*", - "@types/node": "^22.15.3", - "eslint": "^9.31.0", - "typescript": "5.8.2", "@types/json-bigint": "^1.0.4", - "dpdm": "^3.14.0" + "@types/node": "^22.15.3", + "dpdm": "^3.15.1", + "typescript": "5.8.2" }, "dependencies": { "@minswap/felis-ledger-core": "workspace:*", diff --git a/packages/sundaeswap-v1/package.json b/packages/sundaeswap-v1/package.json index 06565f5..477b486 100644 --- a/packages/sundaeswap-v1/package.json +++ b/packages/sundaeswap-v1/package.json @@ -10,7 +10,9 @@ "bugs": { "url": "https://github.com/minswap/felis/issues" }, - "files": ["dist"], + "files": [ + "dist" + ], "exports": { ".": "./dist/index.js" }, @@ -25,13 +27,11 @@ "check-circular-imports": "dpdm --no-warning --no-tree src" }, "devDependencies": { - "@repo/eslint-config": "workspace:*", "@repo/typescript-config": "workspace:*", + "@types/json-bigint": "^1.0.4", "@types/node": "^22.15.3", - "eslint": "^9.31.0", + "dpdm": "^3.15.1", "typescript": "5.8.2", - "@types/json-bigint": "^1.0.4", - "dpdm": "^3.14.0", "vitest": "^3.2.4" }, "dependencies": { diff --git a/packages/sundaeswap-v3/package.json b/packages/sundaeswap-v3/package.json index 2668b43..ab07408 100644 --- a/packages/sundaeswap-v3/package.json +++ b/packages/sundaeswap-v3/package.json @@ -10,7 +10,9 @@ "bugs": { "url": "https://github.com/minswap/felis/issues" }, - "files": ["dist"], + "files": [ + "dist" + ], "exports": { ".": "./dist/index.js" }, @@ -23,13 +25,11 @@ "check-circular-imports": "dpdm --no-warning --no-tree src" }, "devDependencies": { - "@repo/eslint-config": "workspace:*", "@repo/typescript-config": "workspace:*", - "@types/node": "^22.15.3", - "eslint": "^9.31.0", - "typescript": "5.8.2", "@types/json-bigint": "^1.0.4", - "dpdm": "^3.14.0" + "@types/node": "^22.15.3", + "dpdm": "^3.15.1", + "typescript": "5.8.2" }, "dependencies": { "@minswap/felis-ledger-core": "workspace:*", diff --git a/packages/syncer/package.json b/packages/syncer/package.json index 2be198d..a8a23df 100644 --- a/packages/syncer/package.json +++ b/packages/syncer/package.json @@ -10,7 +10,9 @@ "bugs": { "url": "https://github.com/minswap/felis/issues" }, - "files": ["dist"], + "files": [ + "dist" + ], "exports": { ".": "./dist/index.js" }, @@ -25,27 +27,25 @@ "check-circular-imports": "dpdm --no-warning --no-tree src" }, "devDependencies": { - "@repo/eslint-config": "workspace:*", "@repo/typescript-config": "workspace:*", + "@types/json-bigint": "^1.0.4", "@types/node": "^22.15.3", - "eslint": "^9.31.0", + "dpdm": "^3.15.1", "typescript": "5.8.2", - "@types/json-bigint": "^1.0.4", - "dpdm": "^3.14.0", "vitest": "^3.2.4" }, "dependencies": { - "@minswap/tiny-invariant": "^1.2.0", - "@minswap/felis-ledger-core": "workspace:*", - "@minswap/felis-ledger-utils": "workspace:*", "@minswap/felis-dex-v1": "workspace:*", "@minswap/felis-dex-v2": "workspace:*", + "@minswap/felis-ledger-core": "workspace:*", + "@minswap/felis-ledger-utils": "workspace:*", + "@minswap/felis-splash": "workspace:*", "@minswap/felis-stableswap": "workspace:*", "@minswap/felis-sundaeswap-v1": "workspace:*", "@minswap/felis-sundaeswap-v3": "workspace:*", + "@minswap/felis-tx-builder": "workspace:*", "@minswap/felis-wingriders-v1": "workspace:*", "@minswap/felis-wingriders-v2": "workspace:*", - "@minswap/felis-splash": "workspace:*", - "@minswap/felis-tx-builder": "workspace:*" + "@minswap/tiny-invariant": "^1.2.0" } } diff --git a/packages/tx-builder/package.json b/packages/tx-builder/package.json index 77391f4..497c0f3 100644 --- a/packages/tx-builder/package.json +++ b/packages/tx-builder/package.json @@ -25,21 +25,19 @@ "check-circular-imports": "dpdm --no-warning --no-tree src" }, "devDependencies": { - "@repo/eslint-config": "workspace:*", "@repo/typescript-config": "workspace:*", - "@types/node": "^22.15.3", - "eslint": "^9.31.0", - "typescript": "5.8.2", "@types/json-bigint": "^1.0.4", - "dpdm": "^3.14.0" + "@types/node": "^22.15.3", + "dpdm": "^3.15.1", + "typescript": "5.8.2" }, "dependencies": { + "@minswap/felis-cip": "workspace:*", "@minswap/felis-ledger-core": "workspace:*", "@minswap/felis-ledger-utils": "workspace:*", - "@minswap/felis-cip": "workspace:*", - "remeda": "^2.19.2", "@minswap/tiny-invariant": "^1.2.0", "bignumber.js": "^9.1.2", - "json-bigint": "^1.0.0" + "json-bigint": "^1.0.0", + "remeda": "^2.19.2" } } diff --git a/packages/ui/eslint.config.mjs b/packages/ui/eslint.config.mjs deleted file mode 100644 index 19170f8..0000000 --- a/packages/ui/eslint.config.mjs +++ /dev/null @@ -1,4 +0,0 @@ -import { config } from "@repo/eslint-config/react-internal"; - -/** @type {import("eslint").Linter.Config} */ -export default config; diff --git a/packages/ui/package.json b/packages/ui/package.json deleted file mode 100644 index 3bb9d11..0000000 --- a/packages/ui/package.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "name": "@repo/ui", - "version": "0.0.0", - "private": true, - "exports": { - "./*": "./src/*.tsx" - }, - "scripts": { - "lint": "eslint . --max-warnings 0", - "generate:component": "turbo gen react-component", - "check-types": "tsc --noEmit" - }, - "devDependencies": { - "@repo/eslint-config": "workspace:*", - "@repo/typescript-config": "workspace:*", - "@types/node": "^22.15.3", - "@types/react": "19.2.7", - "@types/react-dom": "19.2.3", - "eslint": "^9.31.0", - "typescript": "5.8.3" - }, - "dependencies": { - "react": "^19.2.3", - "react-dom": "^19.2.3" - } -} \ No newline at end of file diff --git a/packages/ui/src/button.tsx b/packages/ui/src/button.tsx deleted file mode 100644 index 78e5420..0000000 --- a/packages/ui/src/button.tsx +++ /dev/null @@ -1,20 +0,0 @@ -"use client"; - -import { ReactNode } from "react"; - -interface ButtonProps { - children: ReactNode; - className?: string; - appName: string; -} - -export const Button = ({ children, className, appName }: ButtonProps) => { - return ( - - ); -}; diff --git a/packages/ui/src/card.tsx b/packages/ui/src/card.tsx deleted file mode 100644 index 7b98893..0000000 --- a/packages/ui/src/card.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import { type JSX } from "react"; - -export function Card({ - className, - title, - children, - href, -}: { - className?: string; - title: string; - children: React.ReactNode; - href: string; -}): JSX.Element { - return ( -
-

- {title} -> -

-

{children}

-
- ); -} diff --git a/packages/ui/src/code.tsx b/packages/ui/src/code.tsx deleted file mode 100644 index f7cbd22..0000000 --- a/packages/ui/src/code.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import { type JSX } from "react"; - -export function Code({ - children, - className, -}: { - children: React.ReactNode; - className?: string; -}): JSX.Element { - return {children}; -} diff --git a/packages/ui/tsconfig.json b/packages/ui/tsconfig.json deleted file mode 100644 index ca86687..0000000 --- a/packages/ui/tsconfig.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "@repo/typescript-config/react-library.json", - "compilerOptions": { - "outDir": "dist" - }, - "include": ["src"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/wingriders-v1/package.json b/packages/wingriders-v1/package.json index 7937ece..a524256 100644 --- a/packages/wingriders-v1/package.json +++ b/packages/wingriders-v1/package.json @@ -10,7 +10,9 @@ "bugs": { "url": "https://github.com/minswap/felis/issues" }, - "files": ["dist"], + "files": [ + "dist" + ], "exports": { ".": "./dist/index.js" }, @@ -23,13 +25,11 @@ "check-circular-imports": "dpdm --no-warning --no-tree src" }, "devDependencies": { - "@repo/eslint-config": "workspace:*", "@repo/typescript-config": "workspace:*", - "@types/node": "^22.15.3", - "eslint": "^9.31.0", - "typescript": "5.8.2", "@types/json-bigint": "^1.0.4", - "dpdm": "^3.14.0" + "@types/node": "^22.15.3", + "dpdm": "^3.15.1", + "typescript": "5.8.2" }, "dependencies": { "@minswap/felis-ledger-core": "workspace:*", diff --git a/packages/wingriders-v2/package.json b/packages/wingriders-v2/package.json index d8176ef..408966c 100644 --- a/packages/wingriders-v2/package.json +++ b/packages/wingriders-v2/package.json @@ -10,7 +10,9 @@ "bugs": { "url": "https://github.com/minswap/felis/issues" }, - "files": ["dist"], + "files": [ + "dist" + ], "exports": { ".": "./dist/index.js" }, @@ -23,13 +25,11 @@ "check-circular-imports": "dpdm --no-warning --no-tree src" }, "devDependencies": { - "@repo/eslint-config": "workspace:*", "@repo/typescript-config": "workspace:*", - "@types/node": "^22.15.3", - "eslint": "^9.31.0", - "typescript": "5.8.2", "@types/json-bigint": "^1.0.4", - "dpdm": "^3.14.0" + "@types/node": "^22.15.3", + "dpdm": "^3.15.1", + "typescript": "5.8.2" }, "dependencies": { "@minswap/felis-ledger-core": "workspace:*", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ffdf90c..eeb63c1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,6 +4,15 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false +overrides: + flatted@<=3.4.1: 3.4.2 + glob@>=10.2.0 <10.5.0: 10.5.0 + minimatch@<3.1.4: 3.1.5 + minimatch@>=9.0.0 <9.0.7: 9.0.9 + rollup@>=4.0.0 <4.59.0: 4.60.0 + socket.io-parser@>=4.0.0 <4.2.6: 4.2.6 + lodash@<4.17.23: 4.17.23 + importers: .: @@ -54,9 +63,6 @@ importers: specifier: ^4.8.3 version: 4.8.3 devDependencies: - '@repo/eslint-config': - specifier: workspace:* - version: link:../../packages/eslint-config '@repo/typescript-config': specifier: workspace:* version: link:../../packages/typescript-config @@ -67,11 +73,8 @@ importers: specifier: ^22.15.3 version: 22.15.3 dpdm: - specifier: ^3.14.0 - version: 3.14.0 - eslint: - specifier: ^9.31.0 - version: 9.31.0(jiti@2.6.1) + specifier: ^3.15.1 + version: 3.15.1 typescript: specifier: 5.8.2 version: 5.8.2 @@ -87,6 +90,12 @@ importers: '@fastify/cors': specifier: ^10.0.2 version: 10.1.0 + '@fastify/helmet': + specifier: ^13.0.2 + version: 13.0.2 + '@fastify/rate-limit': + specifier: ^10.3.0 + version: 10.3.0 '@minswap/felis-build-tx': specifier: workspace:* version: link:../../packages/minswap-build-tx @@ -142,8 +151,8 @@ importers: specifier: ^1.0.0 version: 1.0.0 kysely: - specifier: ^0.28.11 - version: 0.28.11 + specifier: ^0.28.14 + version: 0.28.14 p-timeout: specifier: ^7.0.1 version: 7.0.1 @@ -160,9 +169,6 @@ importers: '@cardano-ogmios/schema': specifier: ^6.11.0 version: 6.14.0 - '@repo/eslint-config': - specifier: workspace:* - version: link:../../packages/eslint-config '@repo/typescript-config': specifier: workspace:* version: link:../../packages/typescript-config @@ -179,17 +185,14 @@ importers: specifier: ^8.16.0 version: 8.16.0 dpdm: - specifier: ^3.14.0 - version: 3.14.0 - eslint: - specifier: ^9.31.0 - version: 9.31.0(jiti@2.6.1) + specifier: ^3.15.1 + version: 3.15.1 kysely-codegen: specifier: ^0.19.0 - version: 0.19.0(kysely@0.28.11)(pg@8.18.0)(typescript@5.8.2) + version: 0.19.0(kysely@0.28.14)(pg@8.18.0)(typescript@5.8.2) kysely-ctl: specifier: ^0.19.0 - version: 0.19.0(kysely@0.28.11)(typescript@5.8.2) + version: 0.19.0(kysely@0.28.14)(typescript@5.8.2) tsx: specifier: ^4.19.4 version: 4.20.3 @@ -200,79 +203,6 @@ importers: specifier: ^3.2.4 version: 3.2.4(@types/node@22.15.3)(jiti@2.6.1)(tsx@4.20.3) - apps/web: - dependencies: - '@ant-design/icons': - specifier: 5.x - version: 5.6.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@ant-design/nextjs-registry': - specifier: ^1.1.0 - version: 1.1.0(@ant-design/cssinjs@1.24.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(antd@5.27.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(next@16.0.10(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@minswap/felis-build-tx': - specifier: workspace:* - version: link:../../packages/minswap-build-tx - '@minswap/felis-cip': - specifier: workspace:* - version: link:../../packages/cip - '@minswap/felis-dex-v2': - specifier: workspace:* - version: link:../../packages/minswap-dex-v2 - '@minswap/felis-ledger-core': - specifier: workspace:* - version: link:../../packages/ledger-core - '@minswap/felis-ledger-utils': - specifier: workspace:* - version: link:../../packages/ledger-utils - '@minswap/felis-lending-market': - specifier: workspace:* - version: link:../../packages/minswap-lending-market - '@minswap/felis-tx-builder': - specifier: workspace:* - version: link:../../packages/tx-builder - '@minswap/tiny-invariant': - specifier: ^1.2.0 - version: 1.2.0 - '@repo/ui': - specifier: workspace:* - version: link:../../packages/ui - antd: - specifier: ^5.27.6 - version: 5.27.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - jotai: - specifier: ^2.10.2 - version: 2.15.1(@types/react@19.2.7)(react@19.2.3) - next: - specifier: ^16.0.10 - version: 16.0.10(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: - specifier: ^19.2.3 - version: 19.2.3 - react-dom: - specifier: ^19.2.3 - version: 19.2.3(react@19.2.3) - devDependencies: - '@repo/eslint-config': - specifier: workspace:* - version: link:../../packages/eslint-config - '@repo/typescript-config': - specifier: workspace:* - version: link:../../packages/typescript-config - '@types/node': - specifier: ^22.15.3 - version: 22.15.3 - '@types/react': - specifier: ^19.2.7 - version: 19.2.7 - '@types/react-dom': - specifier: ^19.2.3 - version: 19.2.3(@types/react@19.2.7) - eslint: - specifier: ^9.31.0 - version: 9.31.0(jiti@2.6.1) - typescript: - specifier: 5.8.2 - version: 5.8.2 - packages/cip: dependencies: '@minswap/felis-ledger-core': @@ -288,9 +218,6 @@ importers: specifier: ^3.1.0 version: 3.1.0 devDependencies: - '@repo/eslint-config': - specifier: workspace:* - version: link:../eslint-config '@repo/typescript-config': specifier: workspace:* version: link:../typescript-config @@ -301,11 +228,8 @@ importers: specifier: ^22.15.3 version: 22.15.3 dpdm: - specifier: ^3.14.0 - version: 3.14.0 - eslint: - specifier: ^9.31.0 - version: 9.31.0(jiti@2.6.1) + specifier: ^3.15.1 + version: 3.15.1 typescript: specifier: 5.8.2 version: 5.8.2 @@ -313,42 +237,6 @@ importers: specifier: ^3.2.4 version: 3.2.4(@types/node@22.15.3)(jiti@2.6.1)(tsx@4.20.3) - packages/eslint-config: - devDependencies: - '@eslint/js': - specifier: ^9.31.0 - version: 9.31.0 - '@next/eslint-plugin-next': - specifier: ^15.4.2 - version: 15.4.2 - eslint: - specifier: ^9.31.0 - version: 9.31.0(jiti@2.6.1) - eslint-config-prettier: - specifier: ^10.1.1 - version: 10.1.1(eslint@9.31.0(jiti@2.6.1)) - eslint-plugin-only-warn: - specifier: ^1.1.0 - version: 1.1.0 - eslint-plugin-react: - specifier: ^7.37.5 - version: 7.37.5(eslint@9.31.0(jiti@2.6.1)) - eslint-plugin-react-hooks: - specifier: ^5.2.0 - version: 5.2.0(eslint@9.31.0(jiti@2.6.1)) - eslint-plugin-turbo: - specifier: ^2.5.0 - version: 2.5.0(eslint@9.31.0(jiti@2.6.1))(turbo@2.5.5) - globals: - specifier: ^16.3.0 - version: 16.3.0 - typescript: - specifier: ^5.8.2 - version: 5.8.2 - typescript-eslint: - specifier: ^8.37.0 - version: 8.37.0(eslint@9.31.0(jiti@2.6.1))(typescript@5.8.2) - packages/felis: dependencies: '@minswap/felis-build-tx': @@ -400,9 +288,6 @@ importers: specifier: workspace:* version: link:../wingriders-v2 devDependencies: - '@repo/eslint-config': - specifier: workspace:* - version: link:../eslint-config '@repo/typescript-config': specifier: workspace:* version: link:../typescript-config @@ -410,11 +295,8 @@ importers: specifier: ^22.15.3 version: 22.15.3 dpdm: - specifier: ^3.14.0 - version: 3.14.0 - eslint: - specifier: ^9.31.0 - version: 9.31.0(jiti@2.6.1) + specifier: ^3.15.1 + version: 3.15.1 typescript: specifier: 5.8.2 version: 5.8.2 @@ -459,8 +341,8 @@ importers: specifier: ^22.15.3 version: 22.15.3 dpdm: - specifier: ^3.14.0 - version: 3.14.0 + specifier: ^3.15.1 + version: 3.15.1 fast-check: specifier: ^3.23.2 version: 3.23.2 @@ -504,9 +386,6 @@ importers: specifier: ^2.1.4 version: 2.1.4 devDependencies: - '@repo/eslint-config': - specifier: workspace:* - version: link:../eslint-config '@repo/typescript-config': specifier: workspace:* version: link:../typescript-config @@ -517,8 +396,8 @@ importers: specifier: ^22.15.3 version: 22.15.3 dpdm: - specifier: ^3.14.0 - version: 3.14.0 + specifier: ^3.15.1 + version: 3.15.1 fast-check: specifier: ^3.23.2 version: 3.23.2 @@ -550,9 +429,6 @@ importers: specifier: ^9.1.2 version: 9.3.1 devDependencies: - '@repo/eslint-config': - specifier: workspace:* - version: link:../eslint-config '@repo/typescript-config': specifier: workspace:* version: link:../typescript-config @@ -563,11 +439,8 @@ importers: specifier: ^22.15.3 version: 22.15.3 dpdm: - specifier: ^3.14.0 - version: 3.14.0 - eslint: - specifier: ^9.31.0 - version: 9.31.0(jiti@2.6.1) + specifier: ^3.15.1 + version: 3.15.1 typescript: specifier: 5.8.2 version: 5.8.2 @@ -587,9 +460,6 @@ importers: specifier: ^1.2.0 version: 1.2.0 devDependencies: - '@repo/eslint-config': - specifier: workspace:* - version: link:../eslint-config '@repo/typescript-config': specifier: workspace:* version: link:../typescript-config @@ -600,11 +470,8 @@ importers: specifier: ^22.15.3 version: 22.15.3 dpdm: - specifier: ^3.14.0 - version: 3.14.0 - eslint: - specifier: ^9.31.0 - version: 9.31.0(jiti@2.6.1) + specifier: ^3.15.1 + version: 3.15.1 typescript: specifier: 5.8.2 version: 5.8.2 @@ -621,9 +488,6 @@ importers: specifier: ^1.2.0 version: 1.2.0 devDependencies: - '@repo/eslint-config': - specifier: workspace:* - version: link:../eslint-config '@repo/typescript-config': specifier: workspace:* version: link:../typescript-config @@ -634,11 +498,8 @@ importers: specifier: ^22.15.3 version: 22.15.3 dpdm: - specifier: ^3.14.0 - version: 3.14.0 - eslint: - specifier: ^9.31.0 - version: 9.31.0(jiti@2.6.1) + specifier: ^3.15.1 + version: 3.15.1 typescript: specifier: 5.8.2 version: 5.8.2 @@ -682,9 +543,6 @@ importers: specifier: ^2.19.2 version: 2.28.0 devDependencies: - '@repo/eslint-config': - specifier: workspace:* - version: link:../eslint-config '@repo/typescript-config': specifier: workspace:* version: link:../typescript-config @@ -695,11 +553,8 @@ importers: specifier: ^22.15.3 version: 22.15.3 dpdm: - specifier: ^3.14.0 - version: 3.14.0 - eslint: - specifier: ^9.31.0 - version: 9.31.0(jiti@2.6.1) + specifier: ^3.15.1 + version: 3.15.1 typescript: specifier: 5.8.2 version: 5.8.2 @@ -734,9 +589,6 @@ importers: specifier: ^2.19.2 version: 2.28.0 devDependencies: - '@repo/eslint-config': - specifier: workspace:* - version: link:../eslint-config '@repo/typescript-config': specifier: workspace:* version: link:../typescript-config @@ -747,11 +599,8 @@ importers: specifier: ^22.15.3 version: 22.15.3 dpdm: - specifier: ^3.14.0 - version: 3.14.0 - eslint: - specifier: ^9.31.0 - version: 9.31.0(jiti@2.6.1) + specifier: ^3.15.1 + version: 3.15.1 fast-check: specifier: ^3.23.2 version: 3.23.2 @@ -780,9 +629,6 @@ importers: '@minswap/felis-dex-v2': specifier: workspace:* version: link:../minswap-dex-v2 - '@repo/eslint-config': - specifier: workspace:* - version: link:../eslint-config '@repo/typescript-config': specifier: workspace:* version: link:../typescript-config @@ -793,11 +639,8 @@ importers: specifier: ^22.15.3 version: 22.15.3 dpdm: - specifier: ^3.14.0 - version: 3.14.0 - eslint: - specifier: ^9.31.0 - version: 9.31.0(jiti@2.6.1) + specifier: ^3.15.1 + version: 3.15.1 typescript: specifier: 5.8.2 version: 5.8.2 @@ -817,9 +660,6 @@ importers: specifier: ^1.2.1 version: 1.2.1 devDependencies: - '@repo/eslint-config': - specifier: workspace:* - version: link:../eslint-config '@repo/typescript-config': specifier: workspace:* version: link:../typescript-config @@ -830,11 +670,8 @@ importers: specifier: ^22.15.3 version: 22.15.3 dpdm: - specifier: ^3.14.0 - version: 3.14.0 - eslint: - specifier: ^9.31.0 - version: 9.31.0(jiti@2.6.1) + specifier: ^3.15.1 + version: 3.15.1 typescript: specifier: 5.8.2 version: 5.8.2 @@ -851,9 +688,6 @@ importers: specifier: ^1.2.0 version: 1.2.0 devDependencies: - '@repo/eslint-config': - specifier: workspace:* - version: link:../eslint-config '@repo/typescript-config': specifier: workspace:* version: link:../typescript-config @@ -864,11 +698,8 @@ importers: specifier: ^22.15.3 version: 22.15.3 dpdm: - specifier: ^3.14.0 - version: 3.14.0 - eslint: - specifier: ^9.31.0 - version: 9.31.0(jiti@2.6.1) + specifier: ^3.15.1 + version: 3.15.1 typescript: specifier: 5.8.2 version: 5.8.2 @@ -888,9 +719,6 @@ importers: specifier: ^1.2.0 version: 1.2.0 devDependencies: - '@repo/eslint-config': - specifier: workspace:* - version: link:../eslint-config '@repo/typescript-config': specifier: workspace:* version: link:../typescript-config @@ -901,11 +729,8 @@ importers: specifier: ^22.15.3 version: 22.15.3 dpdm: - specifier: ^3.14.0 - version: 3.14.0 - eslint: - specifier: ^9.31.0 - version: 9.31.0(jiti@2.6.1) + specifier: ^3.15.1 + version: 3.15.1 typescript: specifier: 5.8.2 version: 5.8.2 @@ -949,9 +774,6 @@ importers: specifier: ^1.2.0 version: 1.2.0 devDependencies: - '@repo/eslint-config': - specifier: workspace:* - version: link:../eslint-config '@repo/typescript-config': specifier: workspace:* version: link:../typescript-config @@ -962,11 +784,8 @@ importers: specifier: ^22.15.3 version: 22.15.3 dpdm: - specifier: ^3.14.0 - version: 3.14.0 - eslint: - specifier: ^9.31.0 - version: 9.31.0(jiti@2.6.1) + specifier: ^3.15.1 + version: 3.15.1 typescript: specifier: 5.8.2 version: 5.8.2 @@ -998,9 +817,6 @@ importers: specifier: ^2.19.2 version: 2.28.0 devDependencies: - '@repo/eslint-config': - specifier: workspace:* - version: link:../eslint-config '@repo/typescript-config': specifier: workspace:* version: link:../typescript-config @@ -1011,48 +827,14 @@ importers: specifier: ^22.15.3 version: 22.15.3 dpdm: - specifier: ^3.14.0 - version: 3.14.0 - eslint: - specifier: ^9.31.0 - version: 9.31.0(jiti@2.6.1) + specifier: ^3.15.1 + version: 3.15.1 typescript: specifier: 5.8.2 version: 5.8.2 packages/typescript-config: {} - packages/ui: - dependencies: - react: - specifier: ^19.2.3 - version: 19.2.3 - react-dom: - specifier: ^19.2.3 - version: 19.2.3(react@19.2.3) - devDependencies: - '@repo/eslint-config': - specifier: workspace:* - version: link:../eslint-config - '@repo/typescript-config': - specifier: workspace:* - version: link:../typescript-config - '@types/node': - specifier: ^22.15.3 - version: 22.15.3 - '@types/react': - specifier: 19.2.7 - version: 19.2.7 - '@types/react-dom': - specifier: 19.2.3 - version: 19.2.3(@types/react@19.2.7) - eslint: - specifier: ^9.31.0 - version: 9.31.0(jiti@2.6.1) - typescript: - specifier: 5.8.3 - version: 5.8.3 - packages/uplc-node: {} packages/uplc-web: {} @@ -1069,9 +851,6 @@ importers: specifier: ^1.2.0 version: 1.2.0 devDependencies: - '@repo/eslint-config': - specifier: workspace:* - version: link:../eslint-config '@repo/typescript-config': specifier: workspace:* version: link:../typescript-config @@ -1082,11 +861,8 @@ importers: specifier: ^22.15.3 version: 22.15.3 dpdm: - specifier: ^3.14.0 - version: 3.14.0 - eslint: - specifier: ^9.31.0 - version: 9.31.0(jiti@2.6.1) + specifier: ^3.15.1 + version: 3.15.1 typescript: specifier: 5.8.2 version: 5.8.2 @@ -1106,9 +882,6 @@ importers: specifier: ^1.2.1 version: 1.2.1 devDependencies: - '@repo/eslint-config': - specifier: workspace:* - version: link:../eslint-config '@repo/typescript-config': specifier: workspace:* version: link:../typescript-config @@ -1119,60 +892,14 @@ importers: specifier: ^22.15.3 version: 22.15.3 dpdm: - specifier: ^3.14.0 - version: 3.14.0 - eslint: - specifier: ^9.31.0 - version: 9.31.0(jiti@2.6.1) + specifier: ^3.15.1 + version: 3.15.1 typescript: specifier: 5.8.2 version: 5.8.2 packages: - '@ant-design/colors@7.2.1': - resolution: {integrity: sha512-lCHDcEzieu4GA3n8ELeZ5VQ8pKQAWcGGLRTQ50aQM2iqPpq2evTxER84jfdPvsPAtEcZ7m44NI45edFMo8oOYQ==} - - '@ant-design/cssinjs-utils@1.1.3': - resolution: {integrity: sha512-nOoQMLW1l+xR1Co8NFVYiP8pZp3VjIIzqV6D6ShYF2ljtdwWJn5WSsH+7kvCktXL/yhEtWURKOfH5Xz/gzlwsg==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - '@ant-design/cssinjs@1.24.0': - resolution: {integrity: sha512-K4cYrJBsgvL+IoozUXYjbT6LHHNt+19a9zkvpBPxLjFHas1UpPM2A5MlhROb0BT8N8WoavM5VsP9MeSeNK/3mg==} - peerDependencies: - react: '>=16.0.0' - react-dom: '>=16.0.0' - - '@ant-design/fast-color@2.0.6': - resolution: {integrity: sha512-y2217gk4NqL35giHl72o6Zzqji9O7vHh9YmhUVkPtAOpoTCH4uWxo/pr4VE8t0+ChEPs0qo4eJRC5Q1eXWo3vA==} - engines: {node: '>=8.x'} - - '@ant-design/icons-svg@4.4.2': - resolution: {integrity: sha512-vHbT+zJEVzllwP+CM+ul7reTEfBR0vgxFe7+lREAsAA7YGsYpboiq2sQNeQeRvh09GfQgs/GyFEvZpJ9cLXpXA==} - - '@ant-design/icons@5.6.1': - resolution: {integrity: sha512-0/xS39c91WjPAZOWsvi1//zjx6kAp4kxWwctR6kuU6p133w8RU0D2dSCvZC19uQyharg/sAvYxGYWl01BbZZfg==} - engines: {node: '>=8'} - peerDependencies: - react: '>=16.0.0' - react-dom: '>=16.0.0' - - '@ant-design/nextjs-registry@1.1.0': - resolution: {integrity: sha512-zZ9SKyqTHonUK7l/z/ixpVx0LSnZTlIqYFOccSDI7jx7ojCMTGxzkl3ugGWPPh8xPYDsVxqzwJZBic84H7LgLw==} - peerDependencies: - '@ant-design/cssinjs': ^1.24.0 - antd: ^5.0.0 - next: ^14.0.0 || ^15.0.0 - react: '>=16.0.0' - react-dom: '>=16.0.0' - - '@ant-design/react-slick@1.1.2': - resolution: {integrity: sha512-EzlvzE6xQUBrZuuhSAFTdsr4P2bBBHGZwKFemEfq8gIGyIQCxalYfZW/T2ORbtQx5rU69o+WycP3exY/7T1hGA==} - peerDependencies: - react: '>=16.9.0' - '@babel/code-frame@7.29.0': resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} engines: {node: '>=6.9.0'} @@ -1181,10 +908,6 @@ packages: resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} engines: {node: '>=6.9.0'} - '@babel/runtime@7.28.4': - resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==} - engines: {node: '>=6.9.0'} - '@biomejs/biome@2.1.4': resolution: {integrity: sha512-QWlrqyxsU0FCebuMnkvBIkxvPqH89afiJzjMl+z67ybutse590jgeaFdDurE9XYtzpjRGTI1tlUZPGWmbKsElA==} engines: {node: '>=14.21.3'} @@ -1253,15 +976,6 @@ packages: '@cardanosolutions/json-bigint@1.1.0': resolution: {integrity: sha512-Pdgz18cSwLKKgheOqW/dqbzNI+CliNT4AdaKaKY/P++J9qLxIB8MITCurlzbaFWV3W4nmK0CRQwI1yvuArmjFg==} - '@emnapi/runtime@1.7.1': - resolution: {integrity: sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==} - - '@emotion/hash@0.8.0': - resolution: {integrity: sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==} - - '@emotion/unitless@0.7.5': - resolution: {integrity: sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==} - '@emurgo/cardano-message-signing-nodejs@1.1.0': resolution: {integrity: sha512-PQRc8K8wZshEdmQenNUzVtiI8oJNF/1uAnBhidee5C4o1l2mDLOW+ur46HWHIFKQ6x8mSJTllcjMscHgzju0gQ==} @@ -1427,44 +1141,6 @@ packages: cpu: [x64] os: [win32] - '@eslint-community/eslint-utils@4.7.0': - resolution: {integrity: sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - - '@eslint-community/regexpp@4.12.1': - resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} - engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - - '@eslint/config-array@0.21.0': - resolution: {integrity: sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@eslint/config-helpers@0.3.0': - resolution: {integrity: sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@eslint/core@0.15.1': - resolution: {integrity: sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@eslint/eslintrc@3.3.1': - resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@eslint/js@9.31.0': - resolution: {integrity: sha512-LOm5OVt7D4qiKCqoiPbA7LWmI+tbw1VbTUowBcUMgQSuM6poJufkFkYDcQpo5KfgD39TnNySV26QjOh7VFpSyw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@eslint/object-schema@2.1.6': - resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@eslint/plugin-kit@0.3.3': - resolution: {integrity: sha512-1+WqvgNMhmlAambTvT3KPtCl/Ibr68VldY2XY40SL1CE0ZXiakFR/cbTspaF5HsnpDMvcYYoJHfl4980NBjGag==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@fastify/ajv-compiler@4.0.5': resolution: {integrity: sha512-KoWKW+MhvfTRWL4qrhUwAAZoaChluo0m0vbiJlGMt2GXvL4LVPQEjt8kSpHI3IBq5Rez8fg+XeH3cneztq+C7A==} @@ -1480,168 +1156,17 @@ packages: '@fastify/forwarded@3.0.1': resolution: {integrity: sha512-JqDochHFqXs3C3Ml3gOY58zM7OqO9ENqPo0UqAjAjH8L01fRZqwX9iLeX34//kiJubF7r2ZQHtBRU36vONbLlw==} + '@fastify/helmet@13.0.2': + resolution: {integrity: sha512-tO1QMkOfNeCt9l4sG/FiWErH4QMm+RjHzbMTrgew1DYOQ2vb/6M1G2iNABBrD7Xq6dUk+HLzWW8u+rmmhQHifA==} + '@fastify/merge-json-schemas@0.2.1': resolution: {integrity: sha512-OA3KGBCy6KtIvLf8DINC5880o5iBlDX4SxzLQS8HorJAbqluzLRn80UXU0bxZn7UOFhFgpRJDasfwn9nG4FG4A==} '@fastify/proxy-addr@5.1.0': resolution: {integrity: sha512-INS+6gh91cLUjB+PVHfu1UqcB76Sqtpyp7bnL+FYojhjygvOPA9ctiD/JDKsyD9Xgu4hUhCSJBPig/w7duNajw==} - '@humanfs/core@0.19.1': - resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} - engines: {node: '>=18.18.0'} - - '@humanfs/node@0.16.6': - resolution: {integrity: sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==} - engines: {node: '>=18.18.0'} - - '@humanwhocodes/module-importer@1.0.1': - resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} - engines: {node: '>=12.22'} - - '@humanwhocodes/retry@0.3.1': - resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==} - engines: {node: '>=18.18'} - - '@humanwhocodes/retry@0.4.3': - resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} - engines: {node: '>=18.18'} - - '@img/colour@1.0.0': - resolution: {integrity: sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==} - engines: {node: '>=18'} - - '@img/sharp-darwin-arm64@0.34.5': - resolution: {integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [arm64] - os: [darwin] - - '@img/sharp-darwin-x64@0.34.5': - resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [x64] - os: [darwin] - - '@img/sharp-libvips-darwin-arm64@1.2.4': - resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==} - cpu: [arm64] - os: [darwin] - - '@img/sharp-libvips-darwin-x64@1.2.4': - resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==} - cpu: [x64] - os: [darwin] - - '@img/sharp-libvips-linux-arm64@1.2.4': - resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==} - cpu: [arm64] - os: [linux] - - '@img/sharp-libvips-linux-arm@1.2.4': - resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==} - cpu: [arm] - os: [linux] - - '@img/sharp-libvips-linux-ppc64@1.2.4': - resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==} - cpu: [ppc64] - os: [linux] - - '@img/sharp-libvips-linux-riscv64@1.2.4': - resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==} - cpu: [riscv64] - os: [linux] - - '@img/sharp-libvips-linux-s390x@1.2.4': - resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==} - cpu: [s390x] - os: [linux] - - '@img/sharp-libvips-linux-x64@1.2.4': - resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==} - cpu: [x64] - os: [linux] - - '@img/sharp-libvips-linuxmusl-arm64@1.2.4': - resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==} - cpu: [arm64] - os: [linux] - - '@img/sharp-libvips-linuxmusl-x64@1.2.4': - resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==} - cpu: [x64] - os: [linux] - - '@img/sharp-linux-arm64@0.34.5': - resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [arm64] - os: [linux] - - '@img/sharp-linux-arm@0.34.5': - resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [arm] - os: [linux] - - '@img/sharp-linux-ppc64@0.34.5': - resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [ppc64] - os: [linux] - - '@img/sharp-linux-riscv64@0.34.5': - resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [riscv64] - os: [linux] - - '@img/sharp-linux-s390x@0.34.5': - resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [s390x] - os: [linux] - - '@img/sharp-linux-x64@0.34.5': - resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [x64] - os: [linux] - - '@img/sharp-linuxmusl-arm64@0.34.5': - resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [arm64] - os: [linux] - - '@img/sharp-linuxmusl-x64@0.34.5': - resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [x64] - os: [linux] - - '@img/sharp-wasm32@0.34.5': - resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [wasm32] - - '@img/sharp-win32-arm64@0.34.5': - resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [arm64] - os: [win32] - - '@img/sharp-win32-ia32@0.34.5': - resolution: {integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [ia32] - os: [win32] - - '@img/sharp-win32-x64@0.34.5': - resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [x64] - os: [win32] + '@fastify/rate-limit@10.3.0': + resolution: {integrity: sha512-eIGkG9XKQs0nyynatApA3EVrojHOuq4l6fhB4eeCk4PIOeadvOJz9/4w3vGI44Go17uaXOWEcPkaD8kuKm7g6Q==} '@ioredis/commands@1.5.0': resolution: {integrity: sha512-eUgLqrMf8nJkZxT24JvVRrQya1vZkQh8BBeYNwGDqa5I0VUi8ACx7uFvAaLxintokpTenkK6DASvo/bvNbBGow==} @@ -1653,6 +1178,10 @@ packages: '@jridgewell/sourcemap-codec@1.5.5': resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + '@lukeed/ms@2.0.2': + resolution: {integrity: sha512-9I2Zn6+NJLfaGoz9jN3lpwDgAYvfGeNYdbAIjJOqzs4Tpc+VU3Jqq4IofSUBKajiDS8k9fZIg18/z13mpk1bsA==} + engines: {node: '>=8'} + '@minswap/cardano-serialization-lib-browser@11.1.0-minswap.33': resolution: {integrity: sha512-onMYkYLn1oC/GA0Qd1iLEeF5ipiPFYOTS7RXsViLptIzcNzEEVFcb4amNL5HwrF+XnlbzR3ZyUvfYBsnQUnK6g==} @@ -1662,76 +1191,10 @@ packages: '@minswap/tiny-invariant@1.2.0': resolution: {integrity: sha512-m4CMTsZ4MDB2WLND6rV0eti+f2WcMPhKvXf9ZHXlQMqvNmgoX1z//84FpEX0D1vVTPfJtNS8XIoIQ9pm6X590A==} - '@next/env@16.0.10': - resolution: {integrity: sha512-8tuaQkyDVgeONQ1MeT9Mkk8pQmZapMKFh5B+OrFUlG3rVmYTXcXlBetBgTurKXGaIZvkoqRT9JL5K3phXcgang==} - - '@next/eslint-plugin-next@15.4.2': - resolution: {integrity: sha512-k0rjdWjXBY6tAOty1ckrMETE6Mx66d85NsgcAIdDp7/cXOsTJ93ywmbg3uUcpxX5TUHFEcCWI5mb8nPhwCe9jg==} - - '@next/swc-darwin-arm64@16.0.10': - resolution: {integrity: sha512-4XgdKtdVsaflErz+B5XeG0T5PeXKDdruDf3CRpnhN+8UebNa5N2H58+3GDgpn/9GBurrQ1uWW768FfscwYkJRg==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [darwin] - - '@next/swc-darwin-x64@16.0.10': - resolution: {integrity: sha512-spbEObMvRKkQ3CkYVOME+ocPDFo5UqHb8EMTS78/0mQ+O1nqE8toHJVioZo4TvebATxgA8XMTHHrScPrn68OGw==} - engines: {node: '>= 10'} - cpu: [x64] - os: [darwin] - - '@next/swc-linux-arm64-gnu@16.0.10': - resolution: {integrity: sha512-uQtWE3X0iGB8apTIskOMi2w/MKONrPOUCi5yLO+v3O8Mb5c7K4Q5KD1jvTpTF5gJKa3VH/ijKjKUq9O9UhwOYw==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [linux] - - '@next/swc-linux-arm64-musl@16.0.10': - resolution: {integrity: sha512-llA+hiDTrYvyWI21Z0L1GiXwjQaanPVQQwru5peOgtooeJ8qx3tlqRV2P7uH2pKQaUfHxI/WVarvI5oYgGxaTw==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [linux] - - '@next/swc-linux-x64-gnu@16.0.10': - resolution: {integrity: sha512-AK2q5H0+a9nsXbeZ3FZdMtbtu9jxW4R/NgzZ6+lrTm3d6Zb7jYrWcgjcpM1k8uuqlSy4xIyPR2YiuUr+wXsavA==} - engines: {node: '>= 10'} - cpu: [x64] - os: [linux] - - '@next/swc-linux-x64-musl@16.0.10': - resolution: {integrity: sha512-1TDG9PDKivNw5550S111gsO4RGennLVl9cipPhtkXIFVwo31YZ73nEbLjNC8qG3SgTz/QZyYyaFYMeY4BKZR/g==} - engines: {node: '>= 10'} - cpu: [x64] - os: [linux] - - '@next/swc-win32-arm64-msvc@16.0.10': - resolution: {integrity: sha512-aEZIS4Hh32xdJQbHz121pyuVZniSNoqDVx1yIr2hy+ZwJGipeqnMZBJHyMxv2tiuAXGx6/xpTcQJ6btIiBjgmg==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [win32] - - '@next/swc-win32-x64-msvc@16.0.10': - resolution: {integrity: sha512-E+njfCoFLb01RAFEnGZn6ERoOqhK1Gl3Lfz1Kjnj0Ulfu7oJbuMyvBKNj/bw8XZnenHDASlygTjZICQW+rYW1Q==} - engines: {node: '>= 10'} - cpu: [x64] - os: [win32] - '@noble/hashes@1.8.0': resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==} engines: {node: ^14.21.3 || >=16} - '@nodelib/fs.scandir@2.1.5': - resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} - engines: {node: '>= 8'} - - '@nodelib/fs.stat@2.0.5': - resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} - engines: {node: '>= 8'} - - '@nodelib/fs.walk@1.2.8': - resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} - engines: {node: '>= 8'} - '@pinojs/redact@0.4.0': resolution: {integrity: sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==} @@ -1739,158 +1202,128 @@ packages: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} - '@rc-component/async-validator@5.0.4': - resolution: {integrity: sha512-qgGdcVIF604M9EqjNF0hbUTz42bz/RDtxWdWuU5EQe3hi7M8ob54B6B35rOsvX5eSvIHIzT9iH1R3n+hk3CGfg==} - engines: {node: '>=14.x'} - - '@rc-component/color-picker@2.0.1': - resolution: {integrity: sha512-WcZYwAThV/b2GISQ8F+7650r5ZZJ043E57aVBFkQ+kSY4C6wdofXgB0hBx+GPGpIU0Z81eETNoDUJMr7oy/P8Q==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - '@rc-component/context@1.4.0': - resolution: {integrity: sha512-kFcNxg9oLRMoL3qki0OMxK+7g5mypjgaaJp/pkOis/6rVxma9nJBF/8kCIuTYHUQNr0ii7MxqE33wirPZLJQ2w==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - '@rc-component/mini-decimal@1.1.0': - resolution: {integrity: sha512-jS4E7T9Li2GuYwI6PyiVXmxTiM6b07rlD9Ge8uGZSCz3WlzcG5ZK7g5bbuKNeZ9pgUuPK/5guV781ujdVpm4HQ==} - engines: {node: '>=8.x'} - - '@rc-component/mutate-observer@1.1.0': - resolution: {integrity: sha512-QjrOsDXQusNwGZPf4/qRQasg7UFEj06XiCJ8iuiq/Io7CrHrgVi6Uuetw60WAMG1799v+aM8kyc+1L/GBbHSlw==} - engines: {node: '>=8.x'} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - '@rc-component/portal@1.1.2': - resolution: {integrity: sha512-6f813C0IsasTZms08kfA8kPAGxbbkYToa8ALaiDIGGECU4i9hj8Plgbx0sNJDrey3EtHO30hmdaxtT0138xZcg==} - engines: {node: '>=8.x'} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - '@rc-component/qrcode@1.0.1': - resolution: {integrity: sha512-g8eeeaMyFXVlq8cZUeaxCDhfIYjpao0l9cvm5gFwKXy/Vm1yDWV7h2sjH5jHYzdFedlVKBpATFB1VKMrHzwaWQ==} - engines: {node: '>=8.x'} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - '@rc-component/tour@1.15.1': - resolution: {integrity: sha512-Tr2t7J1DKZUpfJuDZWHxyxWpfmj8EZrqSgyMZ+BCdvKZ6r1UDsfU46M/iWAAFBy961Ssfom2kv5f3UcjIL2CmQ==} - engines: {node: '>=8.x'} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - '@rc-component/trigger@2.3.0': - resolution: {integrity: sha512-iwaxZyzOuK0D7lS+0AQEtW52zUWxoGqTGkke3dRyb8pYiShmRpCjB/8TzPI4R6YySCH7Vm9BZj/31VPiiQTLBg==} - engines: {node: '>=8.x'} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - '@rollup/rollup-android-arm-eabi@4.46.3': - resolution: {integrity: sha512-UmTdvXnLlqQNOCJnyksjPs1G4GqXNGW1LrzCe8+8QoaLhhDeTXYBgJ3k6x61WIhlHX2U+VzEJ55TtIjR/HTySA==} + '@rollup/rollup-android-arm-eabi@4.60.0': + resolution: {integrity: sha512-WOhNW9K8bR3kf4zLxbfg6Pxu2ybOUbB2AjMDHSQx86LIF4rH4Ft7vmMwNt0loO0eonglSNy4cpD3MKXXKQu0/A==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.46.3': - resolution: {integrity: sha512-8NoxqLpXm7VyeI0ocidh335D6OKT0UJ6fHdnIxf3+6oOerZZc+O7r+UhvROji6OspyPm+rrIdb1gTXtVIqn+Sg==} + '@rollup/rollup-android-arm64@4.60.0': + resolution: {integrity: sha512-u6JHLll5QKRvjciE78bQXDmqRqNs5M/3GVqZeMwvmjaNODJih/WIrJlFVEihvV0MiYFmd+ZyPr9wxOVbPAG2Iw==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.46.3': - resolution: {integrity: sha512-csnNavqZVs1+7/hUKtgjMECsNG2cdB8F7XBHP6FfQjqhjF8rzMzb3SLyy/1BG7YSfQ+bG75Ph7DyedbUqwq1rA==} + '@rollup/rollup-darwin-arm64@4.60.0': + resolution: {integrity: sha512-qEF7CsKKzSRc20Ciu2Zw1wRrBz4g56F7r/vRwY430UPp/nt1x21Q/fpJ9N5l47WWvJlkNCPJz3QRVw008fi7yA==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.46.3': - resolution: {integrity: sha512-r2MXNjbuYabSIX5yQqnT8SGSQ26XQc8fmp6UhlYJd95PZJkQD1u82fWP7HqvGUf33IsOC6qsiV+vcuD4SDP6iw==} + '@rollup/rollup-darwin-x64@4.60.0': + resolution: {integrity: sha512-WADYozJ4QCnXCH4wPB+3FuGmDPoFseVCUrANmA5LWwGmC6FL14BWC7pcq+FstOZv3baGX65tZ378uT6WG8ynTw==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.46.3': - resolution: {integrity: sha512-uluObTmgPJDuJh9xqxyr7MV61Imq+0IvVsAlWyvxAaBSNzCcmZlhfYcRhCdMaCsy46ccZa7vtDDripgs9Jkqsw==} + '@rollup/rollup-freebsd-arm64@4.60.0': + resolution: {integrity: sha512-6b8wGHJlDrGeSE3aH5mGNHBjA0TTkxdoNHik5EkvPHCt351XnigA4pS7Wsj/Eo9Y8RBU6f35cjN9SYmCFBtzxw==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.46.3': - resolution: {integrity: sha512-AVJXEq9RVHQnejdbFvh1eWEoobohUYN3nqJIPI4mNTMpsyYN01VvcAClxflyk2HIxvLpRcRggpX1m9hkXkpC/A==} + '@rollup/rollup-freebsd-x64@4.60.0': + resolution: {integrity: sha512-h25Ga0t4jaylMB8M/JKAyrvvfxGRjnPQIR8lnCayyzEjEOx2EJIlIiMbhpWxDRKGKF8jbNH01NnN663dH638mA==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.46.3': - resolution: {integrity: sha512-byyflM+huiwHlKi7VHLAYTKr67X199+V+mt1iRgJenAI594vcmGGddWlu6eHujmcdl6TqSNnvqaXJqZdnEWRGA==} + '@rollup/rollup-linux-arm-gnueabihf@4.60.0': + resolution: {integrity: sha512-RzeBwv0B3qtVBWtcuABtSuCzToo2IEAIQrcyB/b2zMvBWVbjo8bZDjACUpnaafaxhTw2W+imQbP2BD1usasK4g==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.46.3': - resolution: {integrity: sha512-aLm3NMIjr4Y9LklrH5cu7yybBqoVCdr4Nvnm8WB7PKCn34fMCGypVNpGK0JQWdPAzR/FnoEoFtlRqZbBBLhVoQ==} + '@rollup/rollup-linux-arm-musleabihf@4.60.0': + resolution: {integrity: sha512-Sf7zusNI2CIU1HLzuu9Tc5YGAHEZs5Lu7N1ssJG4Tkw6e0MEsN7NdjUDDfGNHy2IU+ENyWT+L2obgWiguWibWQ==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.46.3': - resolution: {integrity: sha512-VtilE6eznJRDIoFOzaagQodUksTEfLIsvXymS+UdJiSXrPW7Ai+WG4uapAc3F7Hgs791TwdGh4xyOzbuzIZrnw==} + '@rollup/rollup-linux-arm64-gnu@4.60.0': + resolution: {integrity: sha512-DX2x7CMcrJzsE91q7/O02IJQ5/aLkVtYFryqCjduJhUfGKG6yJV8hxaw8pZa93lLEpPTP/ohdN4wFz7yp/ry9A==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.46.3': - resolution: {integrity: sha512-dG3JuS6+cRAL0GQ925Vppafi0qwZnkHdPeuZIxIPXqkCLP02l7ka+OCyBoDEv8S+nKHxfjvjW4OZ7hTdHkx8/w==} + '@rollup/rollup-linux-arm64-musl@4.60.0': + resolution: {integrity: sha512-09EL+yFVbJZlhcQfShpswwRZ0Rg+z/CsSELFCnPt3iK+iqwGsI4zht3secj5vLEs957QvFFXnzAT0FFPIxSrkQ==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-loongarch64-gnu@4.46.3': - resolution: {integrity: sha512-iU8DxnxEKJptf8Vcx4XvAUdpkZfaz0KWfRrnIRrOndL0SvzEte+MTM7nDH4A2Now4FvTZ01yFAgj6TX/mZl8hQ==} + '@rollup/rollup-linux-loong64-gnu@4.60.0': + resolution: {integrity: sha512-i9IcCMPr3EXm8EQg5jnja0Zyc1iFxJjZWlb4wr7U2Wx/GrddOuEafxRdMPRYVaXjgbhvqalp6np07hN1w9kAKw==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-loong64-musl@4.60.0': + resolution: {integrity: sha512-DGzdJK9kyJ+B78MCkWeGnpXJ91tK/iKA6HwHxF4TAlPIY7GXEvMe8hBFRgdrR9Ly4qebR/7gfUs9y2IoaVEyog==} cpu: [loong64] os: [linux] - '@rollup/rollup-linux-ppc64-gnu@4.46.3': - resolution: {integrity: sha512-VrQZp9tkk0yozJoQvQcqlWiqaPnLM6uY1qPYXvukKePb0fqaiQtOdMJSxNFUZFsGw5oA5vvVokjHrx8a9Qsz2A==} + '@rollup/rollup-linux-ppc64-gnu@4.60.0': + resolution: {integrity: sha512-RwpnLsqC8qbS8z1H1AxBA1H6qknR4YpPR9w2XX0vo2Sz10miu57PkNcnHVaZkbqyw/kUWfKMI73jhmfi9BRMUQ==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-ppc64-musl@4.60.0': + resolution: {integrity: sha512-Z8pPf54Ly3aqtdWC3G4rFigZgNvd+qJlOE52fmko3KST9SoGfAdSRCwyoyG05q1HrrAblLbk1/PSIV+80/pxLg==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.46.3': - resolution: {integrity: sha512-uf2eucWSUb+M7b0poZ/08LsbcRgaDYL8NCGjUeFMwCWFwOuFcZ8D9ayPl25P3pl+D2FH45EbHdfyUesQ2Lt9wA==} + '@rollup/rollup-linux-riscv64-gnu@4.60.0': + resolution: {integrity: sha512-3a3qQustp3COCGvnP4SvrMHnPQ9d1vzCakQVRTliaz8cIp/wULGjiGpbcqrkv0WrHTEp8bQD/B3HBjzujVWLOA==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-riscv64-musl@4.46.3': - resolution: {integrity: sha512-7tnUcDvN8DHm/9ra+/nF7lLzYHDeODKKKrh6JmZejbh1FnCNZS8zMkZY5J4sEipy2OW1d1Ncc4gNHUd0DLqkSg==} + '@rollup/rollup-linux-riscv64-musl@4.60.0': + resolution: {integrity: sha512-pjZDsVH/1VsghMJ2/kAaxt6dL0psT6ZexQVrijczOf+PeP2BUqTHYejk3l6TlPRydggINOeNRhvpLa0AYpCWSQ==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.46.3': - resolution: {integrity: sha512-MUpAOallJim8CsJK+4Lc9tQzlfPbHxWDrGXZm2z6biaadNpvh3a5ewcdat478W+tXDoUiHwErX/dOql7ETcLqg==} + '@rollup/rollup-linux-s390x-gnu@4.60.0': + resolution: {integrity: sha512-3ObQs0BhvPgiUVZrN7gqCSvmFuMWvWvsjG5ayJ3Lraqv+2KhOsp+pUbigqbeWqueGIsnn+09HBw27rJ+gYK4VQ==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.46.3': - resolution: {integrity: sha512-F42IgZI4JicE2vM2PWCe0N5mR5vR0gIdORPqhGQ32/u1S1v3kLtbZ0C/mi9FFk7C5T0PgdeyWEPajPjaUpyoKg==} + '@rollup/rollup-linux-x64-gnu@4.60.0': + resolution: {integrity: sha512-EtylprDtQPdS5rXvAayrNDYoJhIz1/vzN2fEubo3yLE7tfAw+948dO0g4M0vkTVFhKojnF+n6C8bDNe+gDRdTg==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.46.3': - resolution: {integrity: sha512-oLc+JrwwvbimJUInzx56Q3ujL3Kkhxehg7O1gWAYzm8hImCd5ld1F2Gry5YDjR21MNb5WCKhC9hXgU7rRlyegQ==} + '@rollup/rollup-linux-x64-musl@4.60.0': + resolution: {integrity: sha512-k09oiRCi/bHU9UVFqD17r3eJR9bn03TyKraCrlz5ULFJGdJGi7VOmm9jl44vOJvRJ6P7WuBi/s2A97LxxHGIdw==} cpu: [x64] os: [linux] - '@rollup/rollup-win32-arm64-msvc@4.46.3': - resolution: {integrity: sha512-lOrQ+BVRstruD1fkWg9yjmumhowR0oLAAzavB7yFSaGltY8klttmZtCLvOXCmGE9mLIn8IBV/IFrQOWz5xbFPg==} + '@rollup/rollup-openbsd-x64@4.60.0': + resolution: {integrity: sha512-1o/0/pIhozoSaDJoDcec+IVLbnRtQmHwPV730+AOD29lHEEo4F5BEUB24H0OBdhbBBDwIOSuf7vgg0Ywxdfiiw==} + cpu: [x64] + os: [openbsd] + + '@rollup/rollup-openharmony-arm64@4.60.0': + resolution: {integrity: sha512-pESDkos/PDzYwtyzB5p/UoNU/8fJo68vcXM9ZW2V0kjYayj1KaaUfi1NmTUTUpMn4UhU4gTuK8gIaFO4UGuMbA==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.60.0': + resolution: {integrity: sha512-hj1wFStD7B1YBeYmvY+lWXZ7ey73YGPcViMShYikqKT1GtstIKQAtfUI6yrzPjAy/O7pO0VLXGmUVWXQMaYgTQ==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.46.3': - resolution: {integrity: sha512-vvrVKPRS4GduGR7VMH8EylCBqsDcw6U+/0nPDuIjXQRbHJc6xOBj+frx8ksfZAh6+Fptw5wHrN7etlMmQnPQVg==} + '@rollup/rollup-win32-ia32-msvc@4.60.0': + resolution: {integrity: sha512-SyaIPFoxmUPlNDq5EHkTbiKzmSEmq/gOYFI/3HHJ8iS/v1mbugVa7dXUzcJGQfoytp9DJFLhHH4U3/eTy2Bq4w==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.46.3': - resolution: {integrity: sha512-fi3cPxCnu3ZeM3EwKZPgXbWoGzm2XHgB/WShKI81uj8wG0+laobmqy5wbgEwzstlbLu4MyO8C19FyhhWseYKNQ==} + '@rollup/rollup-win32-x64-gnu@4.60.0': + resolution: {integrity: sha512-RdcryEfzZr+lAr5kRm2ucN9aVlCCa2QNq4hXelZxb8GG0NJSazq44Z3PCCc8wISRuCVnGs0lQJVX5Vp6fKA+IA==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.60.0': + resolution: {integrity: sha512-PrsWNQ8BuE00O3Xsx3ALh2Df8fAj9+cvvX9AIA6o4KpATR98c9mud4XtDWVvsEuyia5U4tVSTKygawyJkjm60w==} cpu: [x64] os: [win32] @@ -1909,9 +1342,6 @@ packages: '@stricahq/typhonjs@2.0.0': resolution: {integrity: sha512-4EdoVAht+hKDaBT1m0qCBQsi5unP9ghM4eUUKGcQcQQiEUZGsP8CgxcGuQbsXL1/LU5wNKoBnxWXUiylZD9uRQ==} - '@swc/helpers@0.5.15': - resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} - '@types/bun@1.3.8': resolution: {integrity: sha512-3LvWJ2q5GerAXYxO2mffLTqOzEu5qnhEAlh48Vnu8WQfnmSwbgagjGZV6BoHKJztENYEDn6QmVd949W4uESRJA==} @@ -1930,82 +1360,12 @@ packages: '@types/json-bigint@1.0.4': resolution: {integrity: sha512-ydHooXLbOmxBbubnA7Eh+RpBzuaIiQjh8WGJYQB50JFGFrdxW7JzVlyEV7fAXw0T2sqJ1ysTneJbiyNLqZRAag==} - '@types/json-schema@7.0.15': - resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - '@types/node@22.15.3': resolution: {integrity: sha512-lX7HFZeHf4QG/J7tBZqrCAXwz9J5RD56Y6MpP0eJkka8p+K0RY/yBTW7CYFJ4VGCclxqOLKmiGP5juQc6MKgcw==} '@types/pg@8.16.0': resolution: {integrity: sha512-RmhMd/wD+CF8Dfo+cVIy3RR5cl8CyfXQ0tGgW6XBL8L4LM/UTEbNXYRbLwU6w+CgrKBNbrQWt4FUtTfaU5jSYQ==} - '@types/react-dom@19.2.3': - resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} - peerDependencies: - '@types/react': ^19.2.0 - - '@types/react@19.2.7': - resolution: {integrity: sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==} - - '@typescript-eslint/eslint-plugin@8.37.0': - resolution: {integrity: sha512-jsuVWeIkb6ggzB+wPCsR4e6loj+rM72ohW6IBn2C+5NCvfUVY8s33iFPySSVXqtm5Hu29Ne/9bnA0JmyLmgenA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - '@typescript-eslint/parser': ^8.37.0 - eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <5.9.0' - - '@typescript-eslint/parser@8.37.0': - resolution: {integrity: sha512-kVIaQE9vrN9RLCQMQ3iyRlVJpTiDUY6woHGb30JDkfJErqrQEmtdWH3gV0PBAfGZgQXoqzXOO0T3K6ioApbbAA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <5.9.0' - - '@typescript-eslint/project-service@8.37.0': - resolution: {integrity: sha512-BIUXYsbkl5A1aJDdYJCBAo8rCEbAvdquQ8AnLb6z5Lp1u3x5PNgSSx9A/zqYc++Xnr/0DVpls8iQ2cJs/izTXA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - typescript: '>=4.8.4 <5.9.0' - - '@typescript-eslint/scope-manager@8.37.0': - resolution: {integrity: sha512-0vGq0yiU1gbjKob2q691ybTg9JX6ShiVXAAfm2jGf3q0hdP6/BruaFjL/ManAR/lj05AvYCH+5bbVo0VtzmjOA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@typescript-eslint/tsconfig-utils@8.37.0': - resolution: {integrity: sha512-1/YHvAVTimMM9mmlPvTec9NP4bobA1RkDbMydxG8omqwJJLEW/Iy2C4adsAESIXU3WGLXFHSZUU+C9EoFWl4Zg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - typescript: '>=4.8.4 <5.9.0' - - '@typescript-eslint/type-utils@8.37.0': - resolution: {integrity: sha512-SPkXWIkVZxhgwSwVq9rqj/4VFo7MnWwVaRNznfQDc/xPYHjXnPfLWn+4L6FF1cAz6e7dsqBeMawgl7QjUMj4Ow==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <5.9.0' - - '@typescript-eslint/types@8.37.0': - resolution: {integrity: sha512-ax0nv7PUF9NOVPs+lmQ7yIE7IQmAf8LGcXbMvHX5Gm+YJUYNAl340XkGnrimxZ0elXyoQJuN5sbg6C4evKA4SQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@typescript-eslint/typescript-estree@8.37.0': - resolution: {integrity: sha512-zuWDMDuzMRbQOM+bHyU4/slw27bAUEcKSKKs3hcv2aNnc/tvE/h7w60dwVw8vnal2Pub6RT1T7BI8tFZ1fE+yg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - typescript: '>=4.8.4 <5.9.0' - - '@typescript-eslint/utils@8.37.0': - resolution: {integrity: sha512-TSFvkIW6gGjN2p6zbXo20FzCABbyUAuq6tBvNRGsKdsSQ6a7rnV6ADfZ7f4iI3lIiXc4F4WWvtUfDw9CJ9pO5A==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <5.9.0' - - '@typescript-eslint/visitor-keys@8.37.0': - resolution: {integrity: sha512-YzfhzcTnZVPiLfP/oeKtDp2evwvHLMe0LOy7oe+hb9KKIumLNohYS9Hgp1ifwpu42YWxhZE8yieggz6JpqO/1w==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@vitest/expect@3.2.4': resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==} @@ -2038,16 +1398,6 @@ packages: abstract-logging@2.0.1: resolution: {integrity: sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==} - acorn-jsx@5.3.2: - resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} - peerDependencies: - acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - - acorn@8.15.0: - resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} - engines: {node: '>=0.4.0'} - hasBin: true - ajv-formats@3.0.1: resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} peerDependencies: @@ -2056,9 +1406,6 @@ packages: ajv: optional: true - ajv@6.12.6: - resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} - ajv@8.17.1: resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} @@ -2066,8 +1413,8 @@ packages: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} - ansi-regex@6.1.0: - resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==} + ansi-regex@6.2.2: + resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} engines: {node: '>=12'} ansi-styles@3.2.1: @@ -2078,63 +1425,21 @@ packages: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} - ansi-styles@6.2.1: - resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + ansi-styles@6.2.3: + resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} engines: {node: '>=12'} - antd@5.27.6: - resolution: {integrity: sha512-70HrjVbzDXvtiUQ5MP1XdNudr/wGAk9Ivaemk6f36yrAeJurJSmZ8KngOIilolLRHdGuNc6/Vk+4T1OZpSjpag==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - array-buffer-byte-length@1.0.2: - resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} - engines: {node: '>= 0.4'} - - array-includes@3.1.9: - resolution: {integrity: sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==} - engines: {node: '>= 0.4'} - - array.prototype.findlast@1.2.5: - resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==} - engines: {node: '>= 0.4'} - - array.prototype.flat@1.3.3: - resolution: {integrity: sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==} - engines: {node: '>= 0.4'} - - array.prototype.flatmap@1.3.3: - resolution: {integrity: sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==} - engines: {node: '>= 0.4'} - - array.prototype.tosorted@1.1.4: - resolution: {integrity: sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==} - engines: {node: '>= 0.4'} - - arraybuffer.prototype.slice@1.0.4: - resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} - engines: {node: '>= 0.4'} - assertion-error@2.0.1: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} - async-function@1.0.0: - resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} - engines: {node: '>= 0.4'} - atomic-sleep@1.0.0: resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} engines: {node: '>=8.0.0'} - available-typed-arrays@1.0.7: - resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} - engines: {node: '>= 0.4'} - avvio@9.1.0: resolution: {integrity: sha512-fYASnYi600CsH/j9EQov7lECAniYiBFiiAtBNuZYLA2leLe9qOvZzqYHFjtIj6gD2VMoMLP14834LFWvr4IfDw==} @@ -2196,25 +1501,10 @@ packages: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} - call-bind-apply-helpers@1.0.2: - resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} - engines: {node: '>= 0.4'} - - call-bind@1.0.8: - resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} - engines: {node: '>= 0.4'} - - call-bound@1.0.4: - resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} - engines: {node: '>= 0.4'} - callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} - caniuse-lite@1.0.30001727: - resolution: {integrity: sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q==} - cbor@10.0.10: resolution: {integrity: sha512-EirvzAg0G4okCsdTfTjLWHU+tToQ2V2ptO3577Vyy2GOTeVJad99uCIuDqdK7ppFRRcEuigyJY6TJ59wv5JpSg==} engines: {node: '>=20'} @@ -2245,9 +1535,6 @@ packages: citty@0.2.0: resolution: {integrity: sha512-8csy5IBFI2ex2hTVpaHN2j+LNE199AgiI7y4dMintrr8i0lQiFn+0AWMZrWdHKIgMOer65f8IThysYhoReqjWA==} - classnames@2.5.1: - resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==} - cli-cursor@3.1.0: resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} engines: {node: '>=8'} @@ -2256,9 +1543,6 @@ packages: resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} engines: {node: '>=6'} - client-only@0.0.1: - resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} - cliui@8.0.1: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} engines: {node: '>=12'} @@ -2284,9 +1568,6 @@ packages: color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - compute-scroll-into-view@3.1.1: - resolution: {integrity: sha512-VRhuHOLoKYOy4UbilLbUzbYg93XLjv2PncJC50EuTWPA3gaja1UjBsUP/D/9/juV3vQFr6XBEzn9KCAHdUvOHw==} - concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} @@ -2301,9 +1582,6 @@ packages: resolution: {integrity: sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==} engines: {node: '>=18'} - copy-to-clipboard@3.3.3: - resolution: {integrity: sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==} - cosmiconfig@9.0.0: resolution: {integrity: sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==} engines: {node: '>=14'} @@ -2323,27 +1601,6 @@ packages: crypto-js@4.2.0: resolution: {integrity: sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==} - csstype@3.1.3: - resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} - - csstype@3.2.3: - resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} - - data-view-buffer@1.0.2: - resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} - engines: {node: '>= 0.4'} - - data-view-byte-length@1.0.2: - resolution: {integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==} - engines: {node: '>= 0.4'} - - data-view-byte-offset@1.0.1: - resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} - engines: {node: '>= 0.4'} - - dayjs@1.11.18: - resolution: {integrity: sha512-zFBQ7WFRvVRhKcWoUh+ZA1g2HVgUbsZm9sbddh8EC5iv93sui8DVVz1Npvz+r6meo9VKfa8NyLWBsQK1VvIKPA==} - debug@4.4.1: resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} engines: {node: '>=6.0'} @@ -2357,20 +1614,9 @@ packages: resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} engines: {node: '>=6'} - deep-is@0.1.4: - resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} - defaults@1.0.4: resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} - define-data-property@1.1.4: - resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} - engines: {node: '>= 0.4'} - - define-properties@1.2.1: - resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} - engines: {node: '>= 0.4'} - defu@6.1.4: resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} @@ -2385,26 +1631,14 @@ packages: destr@2.0.5: resolution: {integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==} - detect-libc@2.1.2: - resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} - engines: {node: '>=8'} - diff@3.5.1: resolution: {integrity: sha512-Z3u54A8qGyqFOSr2pk0ijYs8mOE9Qz8kTvtKeBI+upoG9j04Sq+oI7W8zAJiQybDcESET8/uIdHzs0p3k4fZlw==} engines: {node: '>=0.3.1'} - doctrine@2.1.0: - resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} - engines: {node: '>=0.10.0'} - dotenv-expand@12.0.3: resolution: {integrity: sha512-uc47g4b+4k/M/SeaW1y4OApx+mtLWl92l5LMPP0GNXctZqELk+YGgOPIIC5elYmUH4OuoK3JLhuRUYegeySiFA==} engines: {node: '>=12'} - dotenv@16.0.3: - resolution: {integrity: sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==} - engines: {node: '>=12'} - dotenv@16.6.1: resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==} engines: {node: '>=12'} @@ -2413,14 +1647,10 @@ packages: resolution: {integrity: sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==} engines: {node: '>=12'} - dpdm@3.14.0: - resolution: {integrity: sha512-YJzsFSyEtj88q5eTELg3UWU7TVZkG1dpbF4JDQ3t1b07xuzXmdoGeSz9TKOke1mUuOpWlk4q+pBh+aHzD6GBTg==} + dpdm@3.15.1: + resolution: {integrity: sha512-qa+BsZAGU3BhhQ6/Fdpd9YYYa3gdF0zMY/vW5rAj/QLJQgPbTX25h7cOe12dfRZvU0/JJP/g5LRgB6lTaVwILw==} hasBin: true - dunder-proto@1.0.1: - resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} - engines: {node: '>= 0.4'} - eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} @@ -2444,41 +1674,9 @@ packages: error-ex@1.3.4: resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} - es-abstract@1.24.0: - resolution: {integrity: sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==} - engines: {node: '>= 0.4'} - - es-define-property@1.0.1: - resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} - engines: {node: '>= 0.4'} - - es-errors@1.3.0: - resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} - engines: {node: '>= 0.4'} - - es-iterator-helpers@1.2.1: - resolution: {integrity: sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==} - engines: {node: '>= 0.4'} - es-module-lexer@1.7.0: resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} - es-object-atoms@1.1.1: - resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} - engines: {node: '>= 0.4'} - - es-set-tostringtag@2.1.0: - resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} - engines: {node: '>= 0.4'} - - es-shim-unscopables@1.1.0: - resolution: {integrity: sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==} - engines: {node: '>= 0.4'} - - es-to-primitive@1.3.0: - resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} - engines: {node: '>= 0.4'} - esbuild@0.25.8: resolution: {integrity: sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q==} engines: {node: '>=18'} @@ -2492,83 +1690,9 @@ packages: resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} engines: {node: '>=0.8.0'} - escape-string-regexp@4.0.0: - resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} - engines: {node: '>=10'} - - eslint-config-prettier@10.1.1: - resolution: {integrity: sha512-4EQQr6wXwS+ZJSzaR5ZCrYgLxqvUjdXctaEtBqHcbkW944B1NQyO4qpdHQbXBONfwxXdkAY81HH4+LUfrg+zPw==} - hasBin: true - peerDependencies: - eslint: '>=7.0.0' - - eslint-plugin-only-warn@1.1.0: - resolution: {integrity: sha512-2tktqUAT+Q3hCAU0iSf4xAN1k9zOpjK5WO8104mB0rT/dGhOa09582HN5HlbxNbPRZ0THV7nLGvzugcNOSjzfA==} - engines: {node: '>=6'} - - eslint-plugin-react-hooks@5.2.0: - resolution: {integrity: sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==} - engines: {node: '>=10'} - peerDependencies: - eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 - - eslint-plugin-react@7.37.5: - resolution: {integrity: sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==} - engines: {node: '>=4'} - peerDependencies: - eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7 - - eslint-plugin-turbo@2.5.0: - resolution: {integrity: sha512-qQk54MrUZv0gnpxV23sccTc+FL3UJ8q7vG7HmXuS2RP8gdjWDwI1CCJTJD8EdRIDjsMxF0xi0AKcMY0CwIlXVg==} - peerDependencies: - eslint: '>6.6.0' - turbo: '>2.0.0' - - eslint-scope@8.4.0: - resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - eslint-visitor-keys@3.4.3: - resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - - eslint-visitor-keys@4.2.1: - resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - eslint@9.31.0: - resolution: {integrity: sha512-QldCVh/ztyKJJZLr4jXNUByx3gR+TDYZCRXEktiZoUR3PGy4qCmSbkxcIle8GEwGpb5JBZazlaJ/CxLidXdEbQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - hasBin: true - peerDependencies: - jiti: '*' - peerDependenciesMeta: - jiti: - optional: true - - espree@10.4.0: - resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - esquery@1.6.0: - resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} - engines: {node: '>=0.10'} - - esrecurse@4.3.0: - resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} - engines: {node: '>=4.0'} - - estraverse@5.3.0: - resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} - engines: {node: '>=4.0'} - estree-walker@3.0.3: resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} - esutils@2.0.3: - resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} - engines: {node: '>=0.10.0'} - expect-type@1.2.2: resolution: {integrity: sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==} engines: {node: '>=12.0.0'} @@ -2589,23 +1713,9 @@ packages: fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - fast-glob@3.3.1: - resolution: {integrity: sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==} - engines: {node: '>=8.6.0'} - - fast-glob@3.3.3: - resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} - engines: {node: '>=8.6.0'} - - fast-json-stable-stringify@2.1.0: - resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} - fast-json-stringify@6.2.0: resolution: {integrity: sha512-Eaf/KNIDwHkzfyeQFNfLXJnQ7cl1XQI3+zRqmPlvtkMigbXnAcasTrvJQmquBSxKfFGeRA6PFog8t+hFmpDoWw==} - fast-levenshtein@2.0.6: - resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} - fast-querystring@1.1.2: resolution: {integrity: sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==} @@ -2630,10 +1740,6 @@ packages: picomatch: optional: true - file-entry-cache@8.0.0: - resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} - engines: {node: '>=16.0.0'} - fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} @@ -2642,21 +1748,6 @@ packages: resolution: {integrity: sha512-5Ye4vHsypZRYtS01ob/iwHzGRUDELlsoCftI/OZFhcLs1M0tkGPcXldE80TAZC5yYuJMBPJQQ43UHlqbJWiX2w==} engines: {node: '>=20'} - find-up@5.0.0: - resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} - engines: {node: '>=10'} - - flat-cache@4.0.1: - resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} - engines: {node: '>=16'} - - flatted@3.3.3: - resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} - - for-each@0.3.5: - resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} - engines: {node: '>= 0.4'} - foreground-child@3.3.1: resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} engines: {node: '>=14'} @@ -2676,29 +1767,10 @@ packages: function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - function.prototype.name@1.1.8: - resolution: {integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==} - engines: {node: '>= 0.4'} - - functions-have-names@1.2.3: - resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} - get-caller-file@2.0.5: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} - get-intrinsic@1.3.0: - resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} - engines: {node: '>= 0.4'} - - get-proto@1.0.1: - resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} - engines: {node: '>= 0.4'} - - get-symbol-description@1.1.0: - resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} - engines: {node: '>= 0.4'} - get-tsconfig@4.10.1: resolution: {integrity: sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==} @@ -2710,16 +1782,8 @@ packages: resolution: {integrity: sha512-/Iu4prUrydE3Pb3lCBMbcSNIf81tgGt0W1ZwknnyF62t3tHmtiJTRj0f+1ZIhp3+Rh0ktz1pJVoa7ZXUCskivA==} engines: {node: '>= 4.8.0'} - glob-parent@5.1.2: - resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} - engines: {node: '>= 6'} - - glob-parent@6.0.2: - resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} - engines: {node: '>=10.13.0'} - - glob@10.4.5: - resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} + glob@10.5.0: + resolution: {integrity: sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==} deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me hasBin: true @@ -2727,32 +1791,9 @@ packages: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me - globals@14.0.0: - resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} - engines: {node: '>=18'} - - globals@16.3.0: - resolution: {integrity: sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ==} - engines: {node: '>=18'} - - globalthis@1.0.4: - resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} - engines: {node: '>= 0.4'} - - gopd@1.2.0: - resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} - engines: {node: '>= 0.4'} - graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - graphemer@1.4.0: - resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} - - has-bigints@1.1.0: - resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} - engines: {node: '>= 0.4'} - has-flag@3.0.0: resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} engines: {node: '>=4'} @@ -2761,44 +1802,21 @@ packages: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} - has-property-descriptors@1.0.2: - resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} - - has-proto@1.2.0: - resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==} - engines: {node: '>= 0.4'} - - has-symbols@1.1.0: - resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} - engines: {node: '>= 0.4'} - - has-tostringtag@1.0.2: - resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} - engines: {node: '>= 0.4'} - hasown@2.0.2: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} + helmet@8.1.0: + resolution: {integrity: sha512-jOiHyAZsmnr8LqoPGmCjYAaiuWwjAPLgY8ZX2XrmHawt99/u1y6RgrZMTeoPfpUbV96HOalYgz1qzkRbw54Pmg==} + engines: {node: '>=18.0.0'} + ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} - ignore@5.3.2: - resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} - engines: {node: '>= 4'} - - ignore@7.0.5: - resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} - engines: {node: '>= 4'} - import-fresh@3.3.1: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} engines: {node: '>=6'} - imurmurhash@0.1.4: - resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} - engines: {node: '>=0.8.19'} - inflight@1.0.6: resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. @@ -2806,10 +1824,6 @@ packages: inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - internal-slot@1.1.0: - resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} - engines: {node: '>= 0.4'} - interpret@1.4.0: resolution: {integrity: sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==} engines: {node: '>= 0.10'} @@ -2822,124 +1836,29 @@ packages: resolution: {integrity: sha512-Zv/pA+ciVFbCSBBjGfaKUya/CcGmUHzTydLMaTwrUUEM2DIEO3iZvueGxmacvmN50fGpGVKeTXpb2LcYQxeVdg==} engines: {node: '>= 10'} - is-array-buffer@3.0.5: - resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} - engines: {node: '>= 0.4'} - is-arrayish@0.2.1: resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} - is-async-function@2.1.1: - resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==} - engines: {node: '>= 0.4'} - - is-bigint@1.1.0: - resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} - engines: {node: '>= 0.4'} - - is-boolean-object@1.2.2: - resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} - engines: {node: '>= 0.4'} - - is-callable@1.2.7: - resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} - engines: {node: '>= 0.4'} - is-core-module@2.16.1: resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} engines: {node: '>= 0.4'} - is-data-view@1.0.2: - resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==} - engines: {node: '>= 0.4'} - - is-date-object@1.1.0: - resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} - engines: {node: '>= 0.4'} - - is-extglob@2.1.1: - resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} - engines: {node: '>=0.10.0'} - - is-finalizationregistry@1.1.1: - resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==} - engines: {node: '>= 0.4'} - is-fullwidth-code-point@3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} - is-generator-function@1.1.0: - resolution: {integrity: sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==} - engines: {node: '>= 0.4'} - - is-glob@4.0.3: - resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} - engines: {node: '>=0.10.0'} - is-interactive@1.0.0: resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} engines: {node: '>=8'} - is-map@2.0.3: - resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} - engines: {node: '>= 0.4'} - - is-negative-zero@2.0.3: - resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} - engines: {node: '>= 0.4'} - - is-number-object@1.1.1: - resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} - engines: {node: '>= 0.4'} - is-number@7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} - is-regex@1.2.1: - resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} - engines: {node: '>= 0.4'} - - is-set@2.0.3: - resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} - engines: {node: '>= 0.4'} - - is-shared-array-buffer@1.0.4: - resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==} - engines: {node: '>= 0.4'} - - is-string@1.1.1: - resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==} - engines: {node: '>= 0.4'} - - is-symbol@1.1.1: - resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==} - engines: {node: '>= 0.4'} - - is-typed-array@1.1.15: - resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} - engines: {node: '>= 0.4'} - is-unicode-supported@0.1.0: resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} engines: {node: '>=10'} - is-weakmap@2.0.2: - resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} - engines: {node: '>= 0.4'} - - is-weakref@1.1.1: - resolution: {integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==} - engines: {node: '>= 0.4'} - - is-weakset@2.0.4: - resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} - engines: {node: '>= 0.4'} - - isarray@2.0.5: - resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} - isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} @@ -2948,10 +1867,6 @@ packages: peerDependencies: ws: '*' - iterator.prototype@1.1.5: - resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==} - engines: {node: '>= 0.4'} - jackspeak@3.4.3: resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} @@ -2959,24 +1874,6 @@ packages: resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} hasBin: true - jotai@2.15.1: - resolution: {integrity: sha512-yHT1HAZ3ba2Q8wgaUQ+xfBzEtcS8ie687I8XVCBinfg4bNniyqLIN+utPXWKQE93LMF5fPbQSVRZqgpcN5yd6Q==} - engines: {node: '>=12.20.0'} - peerDependencies: - '@babel/core': '>=7.0.0' - '@babel/template': '>=7.0.0' - '@types/react': '>=17.0.0' - react: '>=17.0.0' - peerDependenciesMeta: - '@babel/core': - optional: true - '@babel/template': - optional: true - '@types/react': - optional: true - react: - optional: true - js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -2990,37 +1887,18 @@ packages: json-bigint@1.0.0: resolution: {integrity: sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==} - json-buffer@3.0.1: - resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} - json-parse-even-better-errors@2.3.1: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} json-schema-ref-resolver@3.0.0: resolution: {integrity: sha512-hOrZIVL5jyYFjzk7+y7n5JDzGlU8rfWDuYyHwGa2WA8/pcmMHezp2xsVwxrebD/Q9t8Nc5DboieySDpCp4WG4A==} - json-schema-traverse@0.4.1: - resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} - json-schema-traverse@1.0.0: resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} - json-stable-stringify-without-jsonify@1.0.1: - resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} - - json2mq@0.2.0: - resolution: {integrity: sha512-SzoRg7ux5DWTII9J2qkrZrqV1gt+rTaoufMxEzXbS26Uid0NwaJd123HcoB80TgubEppxxIGdNxCx50fEoEWQA==} - jsonfile@6.1.0: resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} - jsx-ast-utils@3.3.5: - resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} - engines: {node: '>=4.0'} - - keyv@4.5.4: - resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} - kysely-codegen@0.19.0: resolution: {integrity: sha512-ZpdQQnpfY0kh45CA6yPA9vdFsBE+b06Fx7QVcbL5rX//yjbA0yYGZGhnH7GTd4P4BY/HIv5uAfuOD83JVZf95w==} engines: {node: '>=20.0.0'} @@ -3073,35 +1951,24 @@ packages: kysely-prisma-postgres: optional: true - kysely@0.28.11: - resolution: {integrity: sha512-zpGIFg0HuoC893rIjYX1BETkVWdDnzTzF5e0kWXJFg5lE0k1/LfNWBejrcnOFu8Q2Rfq/hTDTU7XLUM8QOrpzg==} + kysely@0.28.14: + resolution: {integrity: sha512-SU3lgh0rPvq7upc6vvdVrCsSMUG1h3ChvHVOY7wJ2fw4C9QEB7X3d5eyYEyULUX7UQtxZJtZXGuT6U2US72UYA==} engines: {node: '>=20.0.0'} - levn@0.4.1: - resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} - engines: {node: '>= 0.8.0'} - light-my-request@6.6.0: resolution: {integrity: sha512-CHYbu8RtboSIoVsHZ6Ye4cj4Aw/yg2oAFimlF7mNvfDV192LR7nDiKtSIfCuLT7KokPSTn/9kfVLm5OGN0A28A==} lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} - locate-path@6.0.0: - resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} - engines: {node: '>=10'} - lodash.defaults@4.2.0: resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==} lodash.isarguments@3.1.0: resolution: {integrity: sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==} - lodash.merge@4.6.2: - resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} - - lodash@4.17.21: - resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + lodash@4.17.23: + resolution: {integrity: sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==} log-symbols@4.1.0: resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} @@ -3111,10 +1978,6 @@ packages: resolution: {integrity: sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg==} engines: {node: '>= 0.6.0'} - loose-envify@1.4.0: - resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} - hasBin: true - loupe@3.2.0: resolution: {integrity: sha512-2NCfZcT5VGVNX9mSZIxLRkEAegDGBpuQZBy13desuHeVORmBDyAET4TkJr4SjqQy3A8JDofMN6LpkK8Xcm/dlw==} @@ -3124,14 +1987,6 @@ packages: magic-string@0.30.17: resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} - math-intrinsics@1.1.0: - resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} - engines: {node: '>= 0.4'} - - merge2@1.4.1: - resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} - engines: {node: '>= 8'} - micromatch@4.0.8: resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} engines: {node: '>=8.6'} @@ -3140,18 +1995,18 @@ packages: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} - minimatch@3.1.2: - resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + minimatch@3.1.5: + resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} - minimatch@9.0.5: - resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + minimatch@9.0.9: + resolution: {integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==} engines: {node: '>=16 || 14 >=14.17'} minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} - minipass@7.1.2: - resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + minipass@7.1.3: + resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} engines: {node: '>=16 || 14 >=14.17'} mnemonist@0.40.0: @@ -3165,30 +2020,6 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true - natural-compare@1.4.0: - resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - - next@16.0.10: - resolution: {integrity: sha512-RtWh5PUgI+vxlV3HdR+IfWA1UUHu0+Ram/JBO4vWB54cVPentCD0e+lxyAYEsDTqGGMg7qpjhKh6dc6aW7W/sA==} - engines: {node: '>=20.9.0'} - hasBin: true - peerDependencies: - '@opentelemetry/api': ^1.1.0 - '@playwright/test': ^1.51.1 - babel-plugin-react-compiler: '*' - react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 - react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 - sass: ^1.3.0 - peerDependenciesMeta: - '@opentelemetry/api': - optional: true - '@playwright/test': - optional: true - babel-plugin-react-compiler: - optional: true - sass: - optional: true - node-fetch-native@1.6.7: resolution: {integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==} @@ -3210,34 +2041,6 @@ packages: engines: {node: '>=18'} hasBin: true - object-assign@4.1.1: - resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} - engines: {node: '>=0.10.0'} - - object-inspect@1.13.4: - resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} - engines: {node: '>= 0.4'} - - object-keys@1.1.1: - resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} - engines: {node: '>= 0.4'} - - object.assign@4.1.7: - resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} - engines: {node: '>= 0.4'} - - object.entries@1.1.9: - resolution: {integrity: sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==} - engines: {node: '>= 0.4'} - - object.fromentries@2.0.8: - resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==} - engines: {node: '>= 0.4'} - - object.values@1.2.1: - resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} - engines: {node: '>= 0.4'} - obliterator@2.0.5: resolution: {integrity: sha512-42CPE9AhahZRsMNslczq0ctAEtqk8Eka26QofnqC346BZdHDySk3LWka23LI7ULIw11NmltpiLagIq8gBozxTw==} @@ -3258,26 +2061,10 @@ packages: resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} engines: {node: '>=6'} - optionator@0.9.4: - resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} - engines: {node: '>= 0.8.0'} - ora@5.4.1: resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} engines: {node: '>=10'} - own-keys@1.0.1: - resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} - engines: {node: '>= 0.4'} - - p-limit@3.1.0: - resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} - engines: {node: '>=10'} - - p-locate@5.0.0: - resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} - engines: {node: '>=10'} - p-timeout@7.0.1: resolution: {integrity: sha512-AxTM2wDGORHGEkPCt8yqxOTMgpfbEHqF51f/5fJCmwFC3C/zNcGT63SymH2ttOAaiIws2zVg4+izQCjrakcwHg==} engines: {node: '>=20'} @@ -3293,10 +2080,6 @@ packages: resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} engines: {node: '>=8'} - path-exists@4.0.0: - resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} - engines: {node: '>=8'} - path-is-absolute@1.0.1: resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} engines: {node: '>=0.10.0'} @@ -3384,14 +2167,6 @@ packages: resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} engines: {node: '>=4'} - possible-typed-array-names@1.1.0: - resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} - engines: {node: '>= 0.4'} - - postcss@8.4.31: - resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} - engines: {node: ^10 || ^12 || >=14} - postcss@8.5.6: resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} engines: {node: ^10 || ^12 || >=14} @@ -3412,278 +2187,21 @@ packages: resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==} engines: {node: '>=0.10.0'} - prelude-ls@1.2.1: - resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} - engines: {node: '>= 0.8.0'} - process-warning@4.0.1: resolution: {integrity: sha512-3c2LzQ3rY9d0hc1emcsHhfT9Jwz0cChib/QN89oME2R451w5fy3f0afAhERFZAwrbDU43wk12d0ORBpDVME50Q==} process-warning@5.0.0: resolution: {integrity: sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==} - prop-types@15.8.1: - resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} - - punycode@2.3.1: - resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} - engines: {node: '>=6'} - pure-rand@6.1.0: resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==} - queue-microtask@1.2.3: - resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - quick-format-unescaped@4.0.4: resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} - rc-cascader@3.34.0: - resolution: {integrity: sha512-KpXypcvju9ptjW9FaN2NFcA2QH9E9LHKq169Y0eWtH4e/wHQ5Wh5qZakAgvb8EKZ736WZ3B0zLLOBsrsja5Dag==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-checkbox@3.5.0: - resolution: {integrity: sha512-aOAQc3E98HteIIsSqm6Xk2FPKIER6+5vyEFMZfo73TqM+VVAIqOkHoPjgKLqSNtVLWScoaM7vY2ZrGEheI79yg==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-collapse@3.9.0: - resolution: {integrity: sha512-swDdz4QZ4dFTo4RAUMLL50qP0EY62N2kvmk2We5xYdRwcRn8WcYtuetCJpwpaCbUfUt5+huLpVxhvmnK+PHrkA==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-dialog@9.6.0: - resolution: {integrity: sha512-ApoVi9Z8PaCQg6FsUzS8yvBEQy0ZL2PkuvAgrmohPkN3okps5WZ5WQWPc1RNuiOKaAYv8B97ACdsFU5LizzCqg==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-drawer@7.3.0: - resolution: {integrity: sha512-DX6CIgiBWNpJIMGFO8BAISFkxiuKitoizooj4BDyee8/SnBn0zwO2FHrNDpqqepj0E/TFTDpmEBCyFuTgC7MOg==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-dropdown@4.2.1: - resolution: {integrity: sha512-YDAlXsPv3I1n42dv1JpdM7wJ+gSUBfeyPK59ZpBD9jQhK9jVuxpjj3NmWQHOBceA1zEPVX84T2wbdb2SD0UjmA==} - peerDependencies: - react: '>=16.11.0' - react-dom: '>=16.11.0' - - rc-field-form@2.7.0: - resolution: {integrity: sha512-hgKsCay2taxzVnBPZl+1n4ZondsV78G++XVsMIJCAoioMjlMQR9YwAp7JZDIECzIu2Z66R+f4SFIRrO2DjDNAA==} - engines: {node: '>=8.x'} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-image@7.12.0: - resolution: {integrity: sha512-cZ3HTyyckPnNnUb9/DRqduqzLfrQRyi+CdHjdqgsyDpI3Ln5UX1kXnAhPBSJj9pVRzwRFgqkN7p9b6HBDjmu/Q==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-input-number@9.5.0: - resolution: {integrity: sha512-bKaEvB5tHebUURAEXw35LDcnRZLq3x1k7GxfAqBMzmpHkDGzjAtnUL8y4y5N15rIFIg5IJgwr211jInl3cipag==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-input@1.8.0: - resolution: {integrity: sha512-KXvaTbX+7ha8a/k+eg6SYRVERK0NddX8QX7a7AnRvUa/rEH0CNMlpcBzBkhI0wp2C8C4HlMoYl8TImSN+fuHKA==} - peerDependencies: - react: '>=16.0.0' - react-dom: '>=16.0.0' - - rc-mentions@2.20.0: - resolution: {integrity: sha512-w8HCMZEh3f0nR8ZEd466ATqmXFCMGMN5UFCzEUL0bM/nGw/wOS2GgRzKBcm19K++jDyuWCOJOdgcKGXU3fXfbQ==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-menu@9.16.1: - resolution: {integrity: sha512-ghHx6/6Dvp+fw8CJhDUHFHDJ84hJE3BXNCzSgLdmNiFErWSOaZNsihDAsKq9ByTALo/xkNIwtDFGIl6r+RPXBg==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-motion@2.9.5: - resolution: {integrity: sha512-w+XTUrfh7ArbYEd2582uDrEhmBHwK1ZENJiSJVb7uRxdE7qJSYjbO2eksRXmndqyKqKoYPc9ClpPh5242mV1vA==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-notification@5.6.4: - resolution: {integrity: sha512-KcS4O6B4qzM3KH7lkwOB7ooLPZ4b6J+VMmQgT51VZCeEcmghdeR4IrMcFq0LG+RPdnbe/ArT086tGM8Snimgiw==} - engines: {node: '>=8.x'} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-overflow@1.5.0: - resolution: {integrity: sha512-Lm/v9h0LymeUYJf0x39OveU52InkdRXqnn2aYXfWmo8WdOonIKB2kfau+GF0fWq6jPgtdO9yMqveGcK6aIhJmg==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-pagination@5.1.0: - resolution: {integrity: sha512-8416Yip/+eclTFdHXLKTxZvn70duYVGTvUUWbckCCZoIl3jagqke3GLsFrMs0bsQBikiYpZLD9206Ej4SOdOXQ==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-picker@4.11.3: - resolution: {integrity: sha512-MJ5teb7FlNE0NFHTncxXQ62Y5lytq6sh5nUw0iH8OkHL/TjARSEvSHpr940pWgjGANpjCwyMdvsEV55l5tYNSg==} - engines: {node: '>=8.x'} - peerDependencies: - date-fns: '>= 2.x' - dayjs: '>= 1.x' - luxon: '>= 3.x' - moment: '>= 2.x' - react: '>=16.9.0' - react-dom: '>=16.9.0' - peerDependenciesMeta: - date-fns: - optional: true - dayjs: - optional: true - luxon: - optional: true - moment: - optional: true - - rc-progress@4.0.0: - resolution: {integrity: sha512-oofVMMafOCokIUIBnZLNcOZFsABaUw8PPrf1/y0ZBvKZNpOiu5h4AO9vv11Sw0p4Hb3D0yGWuEattcQGtNJ/aw==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-rate@2.13.1: - resolution: {integrity: sha512-QUhQ9ivQ8Gy7mtMZPAjLbxBt5y9GRp65VcUyGUMF3N3fhiftivPHdpuDIaWIMOTEprAjZPC08bls1dQB+I1F2Q==} - engines: {node: '>=8.x'} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-resize-observer@1.4.3: - resolution: {integrity: sha512-YZLjUbyIWox8E9i9C3Tm7ia+W7euPItNWSPX5sCcQTYbnwDb5uNpnLHQCG1f22oZWUhLw4Mv2tFmeWe68CDQRQ==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-segmented@2.7.0: - resolution: {integrity: sha512-liijAjXz+KnTRVnxxXG2sYDGd6iLL7VpGGdR8gwoxAXy2KglviKCxLWZdjKYJzYzGSUwKDSTdYk8brj54Bn5BA==} - peerDependencies: - react: '>=16.0.0' - react-dom: '>=16.0.0' - - rc-select@14.16.8: - resolution: {integrity: sha512-NOV5BZa1wZrsdkKaiK7LHRuo5ZjZYMDxPP6/1+09+FB4KoNi8jcG1ZqLE3AVCxEsYMBe65OBx71wFoHRTP3LRg==} - engines: {node: '>=8.x'} - peerDependencies: - react: '*' - react-dom: '*' - - rc-slider@11.1.9: - resolution: {integrity: sha512-h8IknhzSh3FEM9u8ivkskh+Ef4Yo4JRIY2nj7MrH6GQmrwV6mcpJf5/4KgH5JaVI1H3E52yCdpOlVyGZIeph5A==} - engines: {node: '>=8.x'} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-steps@6.0.1: - resolution: {integrity: sha512-lKHL+Sny0SeHkQKKDJlAjV5oZ8DwCdS2hFhAkIjuQt1/pB81M0cA0ErVFdHq9+jmPmFw1vJB2F5NBzFXLJxV+g==} - engines: {node: '>=8.x'} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-switch@4.1.0: - resolution: {integrity: sha512-TI8ufP2Az9oEbvyCeVE4+90PDSljGyuwix3fV58p7HV2o4wBnVToEyomJRVyTaZeqNPAp+vqeo4Wnj5u0ZZQBg==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-table@7.54.0: - resolution: {integrity: sha512-/wDTkki6wBTjwylwAGjpLKYklKo9YgjZwAU77+7ME5mBoS32Q4nAwoqhA2lSge6fobLW3Tap6uc5xfwaL2p0Sw==} - engines: {node: '>=8.x'} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-tabs@15.7.0: - resolution: {integrity: sha512-ZepiE+6fmozYdWf/9gVp7k56PKHB1YYoDsKeQA1CBlJ/POIhjkcYiv0AGP0w2Jhzftd3AVvZP/K+V+Lpi2ankA==} - engines: {node: '>=8.x'} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-textarea@1.10.2: - resolution: {integrity: sha512-HfaeXiaSlpiSp0I/pvWpecFEHpVysZ9tpDLNkxQbMvMz6gsr7aVZ7FpWP9kt4t7DB+jJXesYS0us1uPZnlRnwQ==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-tooltip@6.4.0: - resolution: {integrity: sha512-kqyivim5cp8I5RkHmpsp1Nn/Wk+1oeloMv9c7LXNgDxUpGm+RbXJGL+OPvDlcRnx9DBeOe4wyOIl4OKUERyH1g==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-tree-select@5.27.0: - resolution: {integrity: sha512-2qTBTzwIT7LRI1o7zLyrCzmo5tQanmyGbSaGTIf7sYimCklAToVVfpMC6OAldSKolcnjorBYPNSKQqJmN3TCww==} - peerDependencies: - react: '*' - react-dom: '*' - - rc-tree@5.13.1: - resolution: {integrity: sha512-FNhIefhftobCdUJshO7M8uZTA9F4OPGVXqGfZkkD/5soDeOhwO06T/aKTrg0WD8gRg/pyfq+ql3aMymLHCTC4A==} - engines: {node: '>=10.x'} - peerDependencies: - react: '*' - react-dom: '*' - - rc-upload@4.9.2: - resolution: {integrity: sha512-nHx+9rbd1FKMiMRYsqQ3NkXUv7COHPBo3X1Obwq9SWS6/diF/A0aJ5OHubvwUAIDs+4RMleljV0pcrNUc823GQ==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-util@5.44.4: - resolution: {integrity: sha512-resueRJzmHG9Q6rI/DfK6Kdv9/Lfls05vzMs1Sk3M2P+3cJa+MakaZyWY8IPfehVuhPJFKrIY1IK4GqbiaiY5w==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-virtual-list@3.19.2: - resolution: {integrity: sha512-Ys6NcjwGkuwkeaWBDqfI3xWuZ7rDiQXlH1o2zLfFzATfEgXcqpk8CkgMfbJD81McqjcJVez25a3kPxCR807evA==} - engines: {node: '>=8.x'} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - rc9@2.1.2: resolution: {integrity: sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==} - react-dom@19.2.3: - resolution: {integrity: sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==} - peerDependencies: - react: ^19.2.3 - - react-is@16.13.1: - resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} - - react-is@18.3.1: - resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} - - react@19.2.3: - resolution: {integrity: sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==} - engines: {node: '>=0.10.0'} - readable-stream@3.6.2: resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} engines: {node: '>= 6'} @@ -3708,14 +2226,6 @@ packages: resolution: {integrity: sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==} engines: {node: '>=4'} - reflect.getprototypeof@1.0.10: - resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} - engines: {node: '>= 0.4'} - - regexp.prototype.flags@1.5.4: - resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} - engines: {node: '>= 0.4'} - remeda@2.28.0: resolution: {integrity: sha512-943nIauNk4xBh/2DNcsZCc3DRpKncNXnNjtWlqfhM72FvD4bKuOXWSfuY0+k7yf8/Ob3+QzXtxLeobLoMn/INA==} @@ -3730,9 +2240,6 @@ packages: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} engines: {node: '>=0.10.0'} - resize-observer-polyfill@1.5.1: - resolution: {integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==} - resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} @@ -3745,10 +2252,6 @@ packages: engines: {node: '>= 0.4'} hasBin: true - resolve@2.0.0-next.5: - resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} - hasBin: true - restore-cursor@3.1.0: resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} engines: {node: '>=8'} @@ -3764,29 +2267,14 @@ packages: rfdc@1.4.1: resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} - rollup@4.46.3: - resolution: {integrity: sha512-RZn2XTjXb8t5g13f5YclGoilU/kwT696DIkY3sywjdZidNSi3+vseaQov7D7BZXVJCPv3pDWUN69C78GGbXsKw==} + rollup@4.60.0: + resolution: {integrity: sha512-yqjxruMGBQJ2gG4HtjZtAfXArHomazDHoFwFFmZZl0r7Pdo7qCIXKqKHZc8yeoMgzJJ+pO6pEEHa+V7uzWlrAQ==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true - run-parallel@1.2.0: - resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} - - safe-array-concat@1.1.3: - resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==} - engines: {node: '>=0.4'} - safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - safe-push-apply@1.0.0: - resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==} - engines: {node: '>= 0.4'} - - safe-regex-test@1.1.0: - resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} - engines: {node: '>= 0.4'} - safe-regex2@5.0.0: resolution: {integrity: sha512-YwJwe5a51WlK7KbOJREPdjNrpViQBI3p4T50lfwPuDhZnE3XGVTlGvi+aolc5+RvxDD6bnUmjVsU9n1eboLUYw==} @@ -3794,24 +2282,9 @@ packages: resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==} engines: {node: '>=10'} - scheduler@0.27.0: - resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} - - scroll-into-view-if-needed@3.1.0: - resolution: {integrity: sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ==} - secure-json-parse@4.1.0: resolution: {integrity: sha512-l4KnYfEyqYJxDwlNVyRfO2E4NTHfMKAWdUuA8J0yve2Dz/E/PdBepY03RvyJpssIpRFwJoCD55wA+mEDs6ByWA==} - semver@6.3.1: - resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} - hasBin: true - - semver@7.7.2: - resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} - engines: {node: '>=10'} - hasBin: true - semver@7.7.3: resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} engines: {node: '>=10'} @@ -3820,25 +2293,9 @@ packages: set-cookie-parser@2.7.2: resolution: {integrity: sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==} - set-function-length@1.2.2: - resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} - engines: {node: '>= 0.4'} - - set-function-name@2.0.2: - resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} - engines: {node: '>= 0.4'} - - set-proto@1.0.0: - resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} - engines: {node: '>= 0.4'} - sha3@2.1.4: resolution: {integrity: sha512-S8cNxbyb0UGUM2VhRD4Poe5N58gJnJsLJ5vC7FYWGUmGhcsj4++WaIOBFVDxlG0W3To6xBuiRh+i0Qp2oNCOtg==} - sharp@0.34.5: - resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -3856,22 +2313,6 @@ packages: engines: {node: '>=4'} hasBin: true - side-channel-list@1.0.0: - resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} - engines: {node: '>= 0.4'} - - side-channel-map@1.0.1: - resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} - engines: {node: '>= 0.4'} - - side-channel-weakmap@1.0.2: - resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} - engines: {node: '>= 0.4'} - - side-channel@1.1.0: - resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} - engines: {node: '>= 0.4'} - siginfo@2.0.0: resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} @@ -3886,8 +2327,8 @@ packages: resolution: {integrity: sha512-uP0bpjWrjQmUt5DTHq9RuoCBdFJF10cdX9X+a368j/Ft0wmaVgxlrjvK3kjvgCODOMMOz9lcaRzxmso0bTWZ/g==} engines: {node: '>=10.0.0'} - socket.io-parser@4.2.5: - resolution: {integrity: sha512-bPMmpy/5WWKHea5Y/jYAP6k74A+hvmRCQaJuJB6I/ML5JZq/KfNieUVo/3Mh7SAqn7TyFdIo6wqYHInG1MU1bQ==} + socket.io-parser@4.2.6: + resolution: {integrity: sha512-asJqbVBDsBCJx0pTqw3WfesSY0iRX+2xzWEWzrpcH7L6fLzrhyF8WPI8UaeM4YCuDfpwA/cgsdugMsmtz8EJeg==} engines: {node: '>=10.0.0'} sonic-boom@4.2.0: @@ -3910,13 +2351,6 @@ packages: std-env@3.9.0: resolution: {integrity: sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==} - stop-iteration-iterator@1.1.0: - resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} - engines: {node: '>= 0.4'} - - string-convert@0.2.1: - resolution: {integrity: sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A==} - string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} @@ -3925,25 +2359,6 @@ packages: resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} engines: {node: '>=12'} - string.prototype.matchall@4.0.12: - resolution: {integrity: sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==} - engines: {node: '>= 0.4'} - - string.prototype.repeat@1.0.0: - resolution: {integrity: sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==} - - string.prototype.trim@1.2.10: - resolution: {integrity: sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==} - engines: {node: '>= 0.4'} - - string.prototype.trimend@1.0.9: - resolution: {integrity: sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==} - engines: {node: '>= 0.4'} - - string.prototype.trimstart@1.0.8: - resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} - engines: {node: '>= 0.4'} - string_decoder@1.3.0: resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} @@ -3951,33 +2366,13 @@ packages: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} - strip-ansi@7.1.0: - resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + strip-ansi@7.2.0: + resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} engines: {node: '>=12'} - strip-json-comments@3.1.1: - resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} - engines: {node: '>=8'} - strip-literal@3.0.0: resolution: {integrity: sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==} - styled-jsx@5.1.6: - resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==} - engines: {node: '>= 12.0.0'} - peerDependencies: - '@babel/core': '*' - babel-plugin-macros: '*' - react: '>= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0' - peerDependenciesMeta: - '@babel/core': - optional: true - babel-plugin-macros: - optional: true - - stylis@4.3.6: - resolution: {integrity: sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==} - supports-color@5.5.0: resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} engines: {node: '>=4'} @@ -3994,10 +2389,6 @@ packages: resolution: {integrity: sha512-4iMVL6HAINXWf1ZKZjIPcz5wYaOdPhtO8ATvZ+Xqp3BTdaqtAwQkNmKORqcIo5YkQqGXq5cwfswDwMqqQNrpJA==} engines: {node: '>=20'} - throttle-debounce@5.0.2: - resolution: {integrity: sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A==} - engines: {node: '>=12.22'} - tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} @@ -4032,18 +2423,9 @@ packages: resolution: {integrity: sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==} engines: {node: '>=12'} - toggle-selection@1.0.6: - resolution: {integrity: sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==} - tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} - ts-api-utils@2.1.0: - resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==} - engines: {node: '>=18.12'} - peerDependencies: - typescript: '>=4.8.4' - ts-custom-error@3.3.1: resolution: {integrity: sha512-5OX1tzOjxWEgsr/YEUWSuPrQ00deKLh6D7OTWcvNHm12/7QPyRh8SYpyWvA4IZv8H/+GQWQEh/kwo95Q9OVW1A==} engines: {node: '>=14.0.0'} @@ -4100,37 +2482,10 @@ packages: resolution: {integrity: sha512-eZ7wI6KjtT1eBqCnh2JPXWNUAxtoxxfi6VdBdZFvil0ychCOTxbm7YLRBi1JSt7U3c+u3CLxpoPxLdvr/Npr3A==} hasBin: true - type-check@0.4.0: - resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} - engines: {node: '>= 0.8.0'} - type-fest@4.41.0: resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} engines: {node: '>=16'} - typed-array-buffer@1.0.3: - resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} - engines: {node: '>= 0.4'} - - typed-array-byte-length@1.0.3: - resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==} - engines: {node: '>= 0.4'} - - typed-array-byte-offset@1.0.4: - resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==} - engines: {node: '>= 0.4'} - - typed-array-length@1.0.7: - resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} - engines: {node: '>= 0.4'} - - typescript-eslint@8.37.0: - resolution: {integrity: sha512-TnbEjzkE9EmcO0Q2zM+GE8NQLItNAJpMmED1BdgoBMYNdqMhzlbqfdSwiRlAzEK2pA9UzVW0gzaaIzXWg2BjfA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <5.9.0' - typescript@5.8.2: resolution: {integrity: sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==} engines: {node: '>=14.17'} @@ -4144,10 +2499,6 @@ packages: ufo@1.6.3: resolution: {integrity: sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==} - unbox-primitive@1.1.0: - resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} - engines: {node: '>= 0.4'} - undici-types@6.21.0: resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} @@ -4155,9 +2506,6 @@ packages: resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} engines: {node: '>= 10.0.0'} - uri-js@4.4.1: - resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} - util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} @@ -4243,22 +2591,6 @@ packages: whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} - which-boxed-primitive@1.1.1: - resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} - engines: {node: '>= 0.4'} - - which-builtin-type@1.2.1: - resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==} - engines: {node: '>= 0.4'} - - which-collection@1.0.2: - resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} - engines: {node: '>= 0.4'} - - which-typed-array@1.1.19: - resolution: {integrity: sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==} - engines: {node: '>= 0.4'} - which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} @@ -4269,10 +2601,6 @@ packages: engines: {node: '>=8'} hasBin: true - word-wrap@1.2.5: - resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} - engines: {node: '>=0.10.0'} - wrap-ansi@7.0.0: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} @@ -4328,72 +2656,11 @@ packages: resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} engines: {node: '>=12'} - yocto-queue@0.1.0: - resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} - engines: {node: '>=10'} - zod@4.3.6: resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==} snapshots: - '@ant-design/colors@7.2.1': - dependencies: - '@ant-design/fast-color': 2.0.6 - - '@ant-design/cssinjs-utils@1.1.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@ant-design/cssinjs': 1.24.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@babel/runtime': 7.28.4 - rc-util: 5.44.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - '@ant-design/cssinjs@1.24.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@babel/runtime': 7.28.4 - '@emotion/hash': 0.8.0 - '@emotion/unitless': 0.7.5 - classnames: 2.5.1 - csstype: 3.1.3 - rc-util: 5.44.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - stylis: 4.3.6 - - '@ant-design/fast-color@2.0.6': - dependencies: - '@babel/runtime': 7.28.4 - - '@ant-design/icons-svg@4.4.2': {} - - '@ant-design/icons@5.6.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@ant-design/colors': 7.2.1 - '@ant-design/icons-svg': 4.4.2 - '@babel/runtime': 7.28.4 - classnames: 2.5.1 - rc-util: 5.44.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - '@ant-design/nextjs-registry@1.1.0(@ant-design/cssinjs@1.24.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(antd@5.27.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(next@16.0.10(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@ant-design/cssinjs': 1.24.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - antd: 5.27.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - next: 16.0.10(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - '@ant-design/react-slick@1.1.2(react@19.2.3)': - dependencies: - '@babel/runtime': 7.28.4 - classnames: 2.5.1 - json2mq: 0.2.0 - react: 19.2.3 - resize-observer-polyfill: 1.5.1 - throttle-debounce: 5.0.2 - '@babel/code-frame@7.29.0': dependencies: '@babel/helper-validator-identifier': 7.28.5 @@ -4402,8 +2669,6 @@ snapshots: '@babel/helper-validator-identifier@7.28.5': {} - '@babel/runtime@7.28.4': {} - '@biomejs/biome@2.1.4': optionalDependencies: '@biomejs/cli-darwin-arm64': 2.1.4 @@ -4462,15 +2727,6 @@ snapshots: '@cardanosolutions/json-bigint@1.1.0': {} - '@emnapi/runtime@1.7.1': - dependencies: - tslib: 2.8.1 - optional: true - - '@emotion/hash@0.8.0': {} - - '@emotion/unitless@0.7.5': {} - '@emurgo/cardano-message-signing-nodejs@1.1.0': {} '@emurgo/cardano-serialization-lib-browser@13.2.1': {} @@ -4555,50 +2811,6 @@ snapshots: '@esbuild/win32-x64@0.25.8': optional: true - '@eslint-community/eslint-utils@4.7.0(eslint@9.31.0(jiti@2.6.1))': - dependencies: - eslint: 9.31.0(jiti@2.6.1) - eslint-visitor-keys: 3.4.3 - - '@eslint-community/regexpp@4.12.1': {} - - '@eslint/config-array@0.21.0': - dependencies: - '@eslint/object-schema': 2.1.6 - debug: 4.4.1 - minimatch: 3.1.2 - transitivePeerDependencies: - - supports-color - - '@eslint/config-helpers@0.3.0': {} - - '@eslint/core@0.15.1': - dependencies: - '@types/json-schema': 7.0.15 - - '@eslint/eslintrc@3.3.1': - dependencies: - ajv: 6.12.6 - debug: 4.4.1 - espree: 10.4.0 - globals: 14.0.0 - ignore: 5.3.2 - import-fresh: 3.3.1 - js-yaml: 4.1.0 - minimatch: 3.1.2 - strip-json-comments: 3.1.1 - transitivePeerDependencies: - - supports-color - - '@eslint/js@9.31.0': {} - - '@eslint/object-schema@2.1.6': {} - - '@eslint/plugin-kit@0.3.3': - dependencies: - '@eslint/core': 0.15.1 - levn: 0.4.1 - '@fastify/ajv-compiler@4.0.5': dependencies: ajv: 8.17.1 @@ -4618,6 +2830,11 @@ snapshots: '@fastify/forwarded@3.0.1': {} + '@fastify/helmet@13.0.2': + dependencies: + fastify-plugin: 5.1.0 + helmet: 8.1.0 + '@fastify/merge-json-schemas@0.2.1': dependencies: dequal: 2.0.3 @@ -4627,115 +2844,11 @@ snapshots: '@fastify/forwarded': 3.0.1 ipaddr.js: 2.3.0 - '@humanfs/core@0.19.1': {} - - '@humanfs/node@0.16.6': + '@fastify/rate-limit@10.3.0': dependencies: - '@humanfs/core': 0.19.1 - '@humanwhocodes/retry': 0.3.1 - - '@humanwhocodes/module-importer@1.0.1': {} - - '@humanwhocodes/retry@0.3.1': {} - - '@humanwhocodes/retry@0.4.3': {} - - '@img/colour@1.0.0': - optional: true - - '@img/sharp-darwin-arm64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-darwin-arm64': 1.2.4 - optional: true - - '@img/sharp-darwin-x64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-darwin-x64': 1.2.4 - optional: true - - '@img/sharp-libvips-darwin-arm64@1.2.4': - optional: true - - '@img/sharp-libvips-darwin-x64@1.2.4': - optional: true - - '@img/sharp-libvips-linux-arm64@1.2.4': - optional: true - - '@img/sharp-libvips-linux-arm@1.2.4': - optional: true - - '@img/sharp-libvips-linux-ppc64@1.2.4': - optional: true - - '@img/sharp-libvips-linux-riscv64@1.2.4': - optional: true - - '@img/sharp-libvips-linux-s390x@1.2.4': - optional: true - - '@img/sharp-libvips-linux-x64@1.2.4': - optional: true - - '@img/sharp-libvips-linuxmusl-arm64@1.2.4': - optional: true - - '@img/sharp-libvips-linuxmusl-x64@1.2.4': - optional: true - - '@img/sharp-linux-arm64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linux-arm64': 1.2.4 - optional: true - - '@img/sharp-linux-arm@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linux-arm': 1.2.4 - optional: true - - '@img/sharp-linux-ppc64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linux-ppc64': 1.2.4 - optional: true - - '@img/sharp-linux-riscv64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linux-riscv64': 1.2.4 - optional: true - - '@img/sharp-linux-s390x@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linux-s390x': 1.2.4 - optional: true - - '@img/sharp-linux-x64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linux-x64': 1.2.4 - optional: true - - '@img/sharp-linuxmusl-arm64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 - optional: true - - '@img/sharp-linuxmusl-x64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linuxmusl-x64': 1.2.4 - optional: true - - '@img/sharp-wasm32@0.34.5': - dependencies: - '@emnapi/runtime': 1.7.1 - optional: true - - '@img/sharp-win32-arm64@0.34.5': - optional: true - - '@img/sharp-win32-ia32@0.34.5': - optional: true - - '@img/sharp-win32-x64@0.34.5': - optional: true + '@lukeed/ms': 2.0.2 + fastify-plugin: 5.1.0 + toad-cache: 3.7.0 '@ioredis/commands@1.5.0': {} @@ -4743,194 +2856,101 @@ snapshots: dependencies: string-width: 5.1.2 string-width-cjs: string-width@4.2.3 - strip-ansi: 7.1.0 + strip-ansi: 7.2.0 strip-ansi-cjs: strip-ansi@6.0.1 wrap-ansi: 8.1.0 wrap-ansi-cjs: wrap-ansi@7.0.0 '@jridgewell/sourcemap-codec@1.5.5': {} + '@lukeed/ms@2.0.2': {} + '@minswap/cardano-serialization-lib-browser@11.1.0-minswap.33': {} '@minswap/cardano-serialization-lib-nodejs@11.1.0-minswap.33': {} '@minswap/tiny-invariant@1.2.0': {} - '@next/env@16.0.10': {} - - '@next/eslint-plugin-next@15.4.2': - dependencies: - fast-glob: 3.3.1 - - '@next/swc-darwin-arm64@16.0.10': - optional: true + '@noble/hashes@1.8.0': {} - '@next/swc-darwin-x64@16.0.10': - optional: true + '@pinojs/redact@0.4.0': {} - '@next/swc-linux-arm64-gnu@16.0.10': + '@pkgjs/parseargs@0.11.0': optional: true - '@next/swc-linux-arm64-musl@16.0.10': + '@rollup/rollup-android-arm-eabi@4.60.0': optional: true - '@next/swc-linux-x64-gnu@16.0.10': + '@rollup/rollup-android-arm64@4.60.0': optional: true - '@next/swc-linux-x64-musl@16.0.10': + '@rollup/rollup-darwin-arm64@4.60.0': optional: true - '@next/swc-win32-arm64-msvc@16.0.10': + '@rollup/rollup-darwin-x64@4.60.0': optional: true - '@next/swc-win32-x64-msvc@16.0.10': + '@rollup/rollup-freebsd-arm64@4.60.0': optional: true - '@noble/hashes@1.8.0': {} - - '@nodelib/fs.scandir@2.1.5': - dependencies: - '@nodelib/fs.stat': 2.0.5 - run-parallel: 1.2.0 - - '@nodelib/fs.stat@2.0.5': {} - - '@nodelib/fs.walk@1.2.8': - dependencies: - '@nodelib/fs.scandir': 2.1.5 - fastq: 1.19.1 - - '@pinojs/redact@0.4.0': {} - - '@pkgjs/parseargs@0.11.0': - optional: true - - '@rc-component/async-validator@5.0.4': - dependencies: - '@babel/runtime': 7.28.4 - - '@rc-component/color-picker@2.0.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@ant-design/fast-color': 2.0.6 - '@babel/runtime': 7.28.4 - classnames: 2.5.1 - rc-util: 5.44.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - '@rc-component/context@1.4.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@babel/runtime': 7.28.4 - rc-util: 5.44.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - '@rc-component/mini-decimal@1.1.0': - dependencies: - '@babel/runtime': 7.28.4 - - '@rc-component/mutate-observer@1.1.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@babel/runtime': 7.28.4 - classnames: 2.5.1 - rc-util: 5.44.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - '@rc-component/portal@1.1.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@babel/runtime': 7.28.4 - classnames: 2.5.1 - rc-util: 5.44.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - '@rc-component/qrcode@1.0.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@babel/runtime': 7.28.4 - classnames: 2.5.1 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - '@rc-component/tour@1.15.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@babel/runtime': 7.28.4 - '@rc-component/portal': 1.1.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/trigger': 2.3.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - classnames: 2.5.1 - rc-util: 5.44.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - '@rc-component/trigger@2.3.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@babel/runtime': 7.28.4 - '@rc-component/portal': 1.1.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - classnames: 2.5.1 - rc-motion: 2.9.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-resize-observer: 1.4.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-util: 5.44.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - '@rollup/rollup-android-arm-eabi@4.46.3': + '@rollup/rollup-freebsd-x64@4.60.0': optional: true - '@rollup/rollup-android-arm64@4.46.3': + '@rollup/rollup-linux-arm-gnueabihf@4.60.0': optional: true - '@rollup/rollup-darwin-arm64@4.46.3': + '@rollup/rollup-linux-arm-musleabihf@4.60.0': optional: true - '@rollup/rollup-darwin-x64@4.46.3': + '@rollup/rollup-linux-arm64-gnu@4.60.0': optional: true - '@rollup/rollup-freebsd-arm64@4.46.3': + '@rollup/rollup-linux-arm64-musl@4.60.0': optional: true - '@rollup/rollup-freebsd-x64@4.46.3': + '@rollup/rollup-linux-loong64-gnu@4.60.0': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.46.3': + '@rollup/rollup-linux-loong64-musl@4.60.0': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.46.3': + '@rollup/rollup-linux-ppc64-gnu@4.60.0': optional: true - '@rollup/rollup-linux-arm64-gnu@4.46.3': + '@rollup/rollup-linux-ppc64-musl@4.60.0': optional: true - '@rollup/rollup-linux-arm64-musl@4.46.3': + '@rollup/rollup-linux-riscv64-gnu@4.60.0': optional: true - '@rollup/rollup-linux-loongarch64-gnu@4.46.3': + '@rollup/rollup-linux-riscv64-musl@4.60.0': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.46.3': + '@rollup/rollup-linux-s390x-gnu@4.60.0': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.46.3': + '@rollup/rollup-linux-x64-gnu@4.60.0': optional: true - '@rollup/rollup-linux-riscv64-musl@4.46.3': + '@rollup/rollup-linux-x64-musl@4.60.0': optional: true - '@rollup/rollup-linux-s390x-gnu@4.46.3': + '@rollup/rollup-openbsd-x64@4.60.0': optional: true - '@rollup/rollup-linux-x64-gnu@4.46.3': + '@rollup/rollup-openharmony-arm64@4.60.0': optional: true - '@rollup/rollup-linux-x64-musl@4.46.3': + '@rollup/rollup-win32-arm64-msvc@4.60.0': optional: true - '@rollup/rollup-win32-arm64-msvc@4.46.3': + '@rollup/rollup-win32-ia32-msvc@4.60.0': optional: true - '@rollup/rollup-win32-ia32-msvc@4.46.3': + '@rollup/rollup-win32-x64-gnu@4.60.0': optional: true - '@rollup/rollup-win32-x64-msvc@4.46.3': + '@rollup/rollup-win32-x64-msvc@4.60.0': optional: true '@sinclair/typebox@0.34.48': {} @@ -4955,11 +2975,7 @@ snapshots: blakejs: 1.2.1 bs58: 5.0.0 buffer: 6.0.3 - lodash: 4.17.21 - - '@swc/helpers@0.5.15': - dependencies: - tslib: 2.8.1 + lodash: 4.17.23 '@types/bun@1.3.8': dependencies: @@ -4977,8 +2993,6 @@ snapshots: '@types/json-bigint@1.0.4': {} - '@types/json-schema@7.0.15': {} - '@types/node@22.15.3': dependencies: undici-types: 6.21.0 @@ -4989,107 +3003,6 @@ snapshots: pg-protocol: 1.11.0 pg-types: 2.2.0 - '@types/react-dom@19.2.3(@types/react@19.2.7)': - dependencies: - '@types/react': 19.2.7 - - '@types/react@19.2.7': - dependencies: - csstype: 3.2.3 - - '@typescript-eslint/eslint-plugin@8.37.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0(jiti@2.6.1))(typescript@5.8.2))(eslint@9.31.0(jiti@2.6.1))(typescript@5.8.2)': - dependencies: - '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.37.0(eslint@9.31.0(jiti@2.6.1))(typescript@5.8.2) - '@typescript-eslint/scope-manager': 8.37.0 - '@typescript-eslint/type-utils': 8.37.0(eslint@9.31.0(jiti@2.6.1))(typescript@5.8.2) - '@typescript-eslint/utils': 8.37.0(eslint@9.31.0(jiti@2.6.1))(typescript@5.8.2) - '@typescript-eslint/visitor-keys': 8.37.0 - eslint: 9.31.0(jiti@2.6.1) - graphemer: 1.4.0 - ignore: 7.0.5 - natural-compare: 1.4.0 - ts-api-utils: 2.1.0(typescript@5.8.2) - typescript: 5.8.2 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/parser@8.37.0(eslint@9.31.0(jiti@2.6.1))(typescript@5.8.2)': - dependencies: - '@typescript-eslint/scope-manager': 8.37.0 - '@typescript-eslint/types': 8.37.0 - '@typescript-eslint/typescript-estree': 8.37.0(typescript@5.8.2) - '@typescript-eslint/visitor-keys': 8.37.0 - debug: 4.4.1 - eslint: 9.31.0(jiti@2.6.1) - typescript: 5.8.2 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/project-service@8.37.0(typescript@5.8.2)': - dependencies: - '@typescript-eslint/tsconfig-utils': 8.37.0(typescript@5.8.2) - '@typescript-eslint/types': 8.37.0 - debug: 4.4.1 - typescript: 5.8.2 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/scope-manager@8.37.0': - dependencies: - '@typescript-eslint/types': 8.37.0 - '@typescript-eslint/visitor-keys': 8.37.0 - - '@typescript-eslint/tsconfig-utils@8.37.0(typescript@5.8.2)': - dependencies: - typescript: 5.8.2 - - '@typescript-eslint/type-utils@8.37.0(eslint@9.31.0(jiti@2.6.1))(typescript@5.8.2)': - dependencies: - '@typescript-eslint/types': 8.37.0 - '@typescript-eslint/typescript-estree': 8.37.0(typescript@5.8.2) - '@typescript-eslint/utils': 8.37.0(eslint@9.31.0(jiti@2.6.1))(typescript@5.8.2) - debug: 4.4.1 - eslint: 9.31.0(jiti@2.6.1) - ts-api-utils: 2.1.0(typescript@5.8.2) - typescript: 5.8.2 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/types@8.37.0': {} - - '@typescript-eslint/typescript-estree@8.37.0(typescript@5.8.2)': - dependencies: - '@typescript-eslint/project-service': 8.37.0(typescript@5.8.2) - '@typescript-eslint/tsconfig-utils': 8.37.0(typescript@5.8.2) - '@typescript-eslint/types': 8.37.0 - '@typescript-eslint/visitor-keys': 8.37.0 - debug: 4.4.1 - fast-glob: 3.3.3 - is-glob: 4.0.3 - minimatch: 9.0.5 - semver: 7.7.2 - ts-api-utils: 2.1.0(typescript@5.8.2) - typescript: 5.8.2 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/utils@8.37.0(eslint@9.31.0(jiti@2.6.1))(typescript@5.8.2)': - dependencies: - '@eslint-community/eslint-utils': 4.7.0(eslint@9.31.0(jiti@2.6.1)) - '@typescript-eslint/scope-manager': 8.37.0 - '@typescript-eslint/types': 8.37.0 - '@typescript-eslint/typescript-estree': 8.37.0(typescript@5.8.2) - eslint: 9.31.0(jiti@2.6.1) - typescript: 5.8.2 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/visitor-keys@8.37.0': - dependencies: - '@typescript-eslint/types': 8.37.0 - eslint-visitor-keys: 4.2.1 - '@vitest/expect@3.2.4': dependencies: '@types/chai': 5.2.2 @@ -5134,23 +3047,10 @@ snapshots: abstract-logging@2.0.1: {} - acorn-jsx@5.3.2(acorn@8.15.0): - dependencies: - acorn: 8.15.0 - - acorn@8.15.0: {} - ajv-formats@3.0.1(ajv@8.17.1): optionalDependencies: ajv: 8.17.1 - ajv@6.12.6: - dependencies: - fast-deep-equal: 3.1.3 - fast-json-stable-stringify: 2.1.0 - json-schema-traverse: 0.4.1 - uri-js: 4.4.1 - ajv@8.17.1: dependencies: fast-deep-equal: 3.1.3 @@ -5160,7 +3060,7 @@ snapshots: ansi-regex@5.0.1: {} - ansi-regex@6.1.0: {} + ansi-regex@6.2.2: {} ansi-styles@3.2.1: dependencies: @@ -5170,135 +3070,14 @@ snapshots: dependencies: color-convert: 2.0.1 - ansi-styles@6.2.1: {} - - antd@5.27.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3): - dependencies: - '@ant-design/colors': 7.2.1 - '@ant-design/cssinjs': 1.24.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@ant-design/cssinjs-utils': 1.1.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@ant-design/fast-color': 2.0.6 - '@ant-design/icons': 5.6.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@ant-design/react-slick': 1.1.2(react@19.2.3) - '@babel/runtime': 7.28.4 - '@rc-component/color-picker': 2.0.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/mutate-observer': 1.1.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/qrcode': 1.0.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/tour': 1.15.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/trigger': 2.3.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - classnames: 2.5.1 - copy-to-clipboard: 3.3.3 - dayjs: 1.11.18 - rc-cascader: 3.34.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-checkbox: 3.5.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-collapse: 3.9.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-dialog: 9.6.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-drawer: 7.3.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-dropdown: 4.2.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-field-form: 2.7.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-image: 7.12.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-input: 1.8.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-input-number: 9.5.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-mentions: 2.20.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-menu: 9.16.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-motion: 2.9.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-notification: 5.6.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-pagination: 5.1.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-picker: 4.11.3(dayjs@1.11.18)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-progress: 4.0.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-rate: 2.13.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-resize-observer: 1.4.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-segmented: 2.7.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-select: 14.16.8(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-slider: 11.1.9(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-steps: 6.0.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-switch: 4.1.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-table: 7.54.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-tabs: 15.7.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-textarea: 1.10.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-tooltip: 6.4.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-tree: 5.13.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-tree-select: 5.27.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-upload: 4.9.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-util: 5.44.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - scroll-into-view-if-needed: 3.1.0 - throttle-debounce: 5.0.2 - transitivePeerDependencies: - - date-fns - - luxon - - moment + ansi-styles@6.2.3: {} argparse@2.0.1: {} - array-buffer-byte-length@1.0.2: - dependencies: - call-bound: 1.0.4 - is-array-buffer: 3.0.5 - - array-includes@3.1.9: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.4 - define-properties: 1.2.1 - es-abstract: 1.24.0 - es-object-atoms: 1.1.1 - get-intrinsic: 1.3.0 - is-string: 1.1.1 - math-intrinsics: 1.1.0 - - array.prototype.findlast@1.2.5: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - es-abstract: 1.24.0 - es-errors: 1.3.0 - es-object-atoms: 1.1.1 - es-shim-unscopables: 1.1.0 - - array.prototype.flat@1.3.3: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - es-abstract: 1.24.0 - es-shim-unscopables: 1.1.0 - - array.prototype.flatmap@1.3.3: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - es-abstract: 1.24.0 - es-shim-unscopables: 1.1.0 - - array.prototype.tosorted@1.1.4: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - es-abstract: 1.24.0 - es-errors: 1.3.0 - es-shim-unscopables: 1.1.0 - - arraybuffer.prototype.slice@1.0.4: - dependencies: - array-buffer-byte-length: 1.0.2 - call-bind: 1.0.8 - define-properties: 1.2.1 - es-abstract: 1.24.0 - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - is-array-buffer: 3.0.5 - assertion-error@2.0.1: {} - async-function@1.0.0: {} - atomic-sleep@1.0.0: {} - available-typed-arrays@1.0.7: - dependencies: - possible-typed-array-names: 1.1.0 - avvio@9.1.0: dependencies: '@fastify/error': 4.2.0 @@ -5374,27 +3153,8 @@ snapshots: cac@6.7.14: {} - call-bind-apply-helpers@1.0.2: - dependencies: - es-errors: 1.3.0 - function-bind: 1.1.2 - - call-bind@1.0.8: - dependencies: - call-bind-apply-helpers: 1.0.2 - es-define-property: 1.0.1 - get-intrinsic: 1.3.0 - set-function-length: 1.2.2 - - call-bound@1.0.4: - dependencies: - call-bind-apply-helpers: 1.0.2 - get-intrinsic: 1.3.0 - callsites@3.1.0: {} - caniuse-lite@1.0.30001727: {} - cbor@10.0.10: dependencies: nofilter: 3.1.0 @@ -5430,16 +3190,12 @@ snapshots: citty@0.2.0: {} - classnames@2.5.1: {} - cli-cursor@3.1.0: dependencies: restore-cursor: 3.1.0 cli-spinners@2.9.2: {} - client-only@0.0.1: {} - cliui@8.0.1: dependencies: string-width: 4.2.3 @@ -5462,8 +3218,6 @@ snapshots: color-name@1.1.4: {} - compute-scroll-into-view@3.1.1: {} - concat-map@0.0.1: {} confbox@0.2.2: {} @@ -5472,10 +3226,6 @@ snapshots: cookie@1.1.1: {} - copy-to-clipboard@3.3.3: - dependencies: - toggle-selection: 1.0.6 - cosmiconfig@9.0.0(typescript@5.8.2): dependencies: env-paths: 2.2.1 @@ -5499,54 +3249,16 @@ snapshots: crypto-js@4.2.0: {} - csstype@3.1.3: {} - - csstype@3.2.3: {} - - data-view-buffer@1.0.2: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - is-data-view: 1.0.2 - - data-view-byte-length@1.0.2: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - is-data-view: 1.0.2 - - data-view-byte-offset@1.0.1: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - is-data-view: 1.0.2 - - dayjs@1.11.18: {} - debug@4.4.1: dependencies: ms: 2.1.3 deep-eql@5.0.2: {} - deep-is@0.1.4: {} - defaults@1.0.4: dependencies: clone: 1.0.4 - define-data-property@1.1.4: - dependencies: - es-define-property: 1.0.1 - es-errors: 1.3.0 - gopd: 1.2.0 - - define-properties@1.2.1: - dependencies: - define-data-property: 1.1.4 - has-property-descriptors: 1.0.2 - object-keys: 1.1.1 - defu@6.1.4: {} denque@2.1.0: {} @@ -5555,41 +3267,26 @@ snapshots: destr@2.0.5: {} - detect-libc@2.1.2: - optional: true - diff@3.5.1: {} - doctrine@2.1.0: - dependencies: - esutils: 2.0.3 - dotenv-expand@12.0.3: dependencies: dotenv: 16.6.1 - dotenv@16.0.3: {} - dotenv@16.6.1: {} dotenv@17.2.3: {} - dpdm@3.14.0: + dpdm@3.15.1: dependencies: chalk: 4.1.2 fs-extra: 11.3.1 - glob: 10.4.5 + glob: 10.5.0 ora: 5.4.1 tslib: 2.8.1 typescript: 5.8.3 yargs: 17.7.2 - dunder-proto@1.0.1: - dependencies: - call-bind-apply-helpers: 1.0.2 - es-errors: 1.3.0 - gopd: 1.2.0 - eastasianwidth@0.2.0: {} emoji-regex@8.0.0: {} @@ -5616,109 +3313,8 @@ snapshots: dependencies: is-arrayish: 0.2.1 - es-abstract@1.24.0: - dependencies: - array-buffer-byte-length: 1.0.2 - arraybuffer.prototype.slice: 1.0.4 - available-typed-arrays: 1.0.7 - call-bind: 1.0.8 - call-bound: 1.0.4 - data-view-buffer: 1.0.2 - data-view-byte-length: 1.0.2 - data-view-byte-offset: 1.0.1 - es-define-property: 1.0.1 - es-errors: 1.3.0 - es-object-atoms: 1.1.1 - es-set-tostringtag: 2.1.0 - es-to-primitive: 1.3.0 - function.prototype.name: 1.1.8 - get-intrinsic: 1.3.0 - get-proto: 1.0.1 - get-symbol-description: 1.1.0 - globalthis: 1.0.4 - gopd: 1.2.0 - has-property-descriptors: 1.0.2 - has-proto: 1.2.0 - has-symbols: 1.1.0 - hasown: 2.0.2 - internal-slot: 1.1.0 - is-array-buffer: 3.0.5 - is-callable: 1.2.7 - is-data-view: 1.0.2 - is-negative-zero: 2.0.3 - is-regex: 1.2.1 - is-set: 2.0.3 - is-shared-array-buffer: 1.0.4 - is-string: 1.1.1 - is-typed-array: 1.1.15 - is-weakref: 1.1.1 - math-intrinsics: 1.1.0 - object-inspect: 1.13.4 - object-keys: 1.1.1 - object.assign: 4.1.7 - own-keys: 1.0.1 - regexp.prototype.flags: 1.5.4 - safe-array-concat: 1.1.3 - safe-push-apply: 1.0.0 - safe-regex-test: 1.1.0 - set-proto: 1.0.0 - stop-iteration-iterator: 1.1.0 - string.prototype.trim: 1.2.10 - string.prototype.trimend: 1.0.9 - string.prototype.trimstart: 1.0.8 - typed-array-buffer: 1.0.3 - typed-array-byte-length: 1.0.3 - typed-array-byte-offset: 1.0.4 - typed-array-length: 1.0.7 - unbox-primitive: 1.1.0 - which-typed-array: 1.1.19 - - es-define-property@1.0.1: {} - - es-errors@1.3.0: {} - - es-iterator-helpers@1.2.1: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.4 - define-properties: 1.2.1 - es-abstract: 1.24.0 - es-errors: 1.3.0 - es-set-tostringtag: 2.1.0 - function-bind: 1.1.2 - get-intrinsic: 1.3.0 - globalthis: 1.0.4 - gopd: 1.2.0 - has-property-descriptors: 1.0.2 - has-proto: 1.2.0 - has-symbols: 1.1.0 - internal-slot: 1.1.0 - iterator.prototype: 1.1.5 - safe-array-concat: 1.1.3 - es-module-lexer@1.7.0: {} - es-object-atoms@1.1.1: - dependencies: - es-errors: 1.3.0 - - es-set-tostringtag@2.1.0: - dependencies: - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - has-tostringtag: 1.0.2 - hasown: 2.0.2 - - es-shim-unscopables@1.1.0: - dependencies: - hasown: 2.0.2 - - es-to-primitive@1.3.0: - dependencies: - is-callable: 1.2.7 - is-date-object: 1.1.0 - is-symbol: 1.1.1 - esbuild@0.25.8: optionalDependencies: '@esbuild/aix-ppc64': 0.25.8 @@ -5744,127 +3340,18 @@ snapshots: '@esbuild/openbsd-x64': 0.25.8 '@esbuild/openharmony-arm64': 0.25.8 '@esbuild/sunos-x64': 0.25.8 - '@esbuild/win32-arm64': 0.25.8 - '@esbuild/win32-ia32': 0.25.8 - '@esbuild/win32-x64': 0.25.8 - - escalade@3.2.0: {} - - escape-string-regexp@1.0.5: {} - - escape-string-regexp@4.0.0: {} - - eslint-config-prettier@10.1.1(eslint@9.31.0(jiti@2.6.1)): - dependencies: - eslint: 9.31.0(jiti@2.6.1) - - eslint-plugin-only-warn@1.1.0: {} - - eslint-plugin-react-hooks@5.2.0(eslint@9.31.0(jiti@2.6.1)): - dependencies: - eslint: 9.31.0(jiti@2.6.1) - - eslint-plugin-react@7.37.5(eslint@9.31.0(jiti@2.6.1)): - dependencies: - array-includes: 3.1.9 - array.prototype.findlast: 1.2.5 - array.prototype.flatmap: 1.3.3 - array.prototype.tosorted: 1.1.4 - doctrine: 2.1.0 - es-iterator-helpers: 1.2.1 - eslint: 9.31.0(jiti@2.6.1) - estraverse: 5.3.0 - hasown: 2.0.2 - jsx-ast-utils: 3.3.5 - minimatch: 3.1.2 - object.entries: 1.1.9 - object.fromentries: 2.0.8 - object.values: 1.2.1 - prop-types: 15.8.1 - resolve: 2.0.0-next.5 - semver: 6.3.1 - string.prototype.matchall: 4.0.12 - string.prototype.repeat: 1.0.0 - - eslint-plugin-turbo@2.5.0(eslint@9.31.0(jiti@2.6.1))(turbo@2.5.5): - dependencies: - dotenv: 16.0.3 - eslint: 9.31.0(jiti@2.6.1) - turbo: 2.5.5 - - eslint-scope@8.4.0: - dependencies: - esrecurse: 4.3.0 - estraverse: 5.3.0 - - eslint-visitor-keys@3.4.3: {} - - eslint-visitor-keys@4.2.1: {} - - eslint@9.31.0(jiti@2.6.1): - dependencies: - '@eslint-community/eslint-utils': 4.7.0(eslint@9.31.0(jiti@2.6.1)) - '@eslint-community/regexpp': 4.12.1 - '@eslint/config-array': 0.21.0 - '@eslint/config-helpers': 0.3.0 - '@eslint/core': 0.15.1 - '@eslint/eslintrc': 3.3.1 - '@eslint/js': 9.31.0 - '@eslint/plugin-kit': 0.3.3 - '@humanfs/node': 0.16.6 - '@humanwhocodes/module-importer': 1.0.1 - '@humanwhocodes/retry': 0.4.3 - '@types/estree': 1.0.8 - '@types/json-schema': 7.0.15 - ajv: 6.12.6 - chalk: 4.1.2 - cross-spawn: 7.0.6 - debug: 4.4.1 - escape-string-regexp: 4.0.0 - eslint-scope: 8.4.0 - eslint-visitor-keys: 4.2.1 - espree: 10.4.0 - esquery: 1.6.0 - esutils: 2.0.3 - fast-deep-equal: 3.1.3 - file-entry-cache: 8.0.0 - find-up: 5.0.0 - glob-parent: 6.0.2 - ignore: 5.3.2 - imurmurhash: 0.1.4 - is-glob: 4.0.3 - json-stable-stringify-without-jsonify: 1.0.1 - lodash.merge: 4.6.2 - minimatch: 3.1.2 - natural-compare: 1.4.0 - optionator: 0.9.4 - optionalDependencies: - jiti: 2.6.1 - transitivePeerDependencies: - - supports-color - - espree@10.4.0: - dependencies: - acorn: 8.15.0 - acorn-jsx: 5.3.2(acorn@8.15.0) - eslint-visitor-keys: 4.2.1 - - esquery@1.6.0: - dependencies: - estraverse: 5.3.0 + '@esbuild/win32-arm64': 0.25.8 + '@esbuild/win32-ia32': 0.25.8 + '@esbuild/win32-x64': 0.25.8 - esrecurse@4.3.0: - dependencies: - estraverse: 5.3.0 + escalade@3.2.0: {} - estraverse@5.3.0: {} + escape-string-regexp@1.0.5: {} estree-walker@3.0.3: dependencies: '@types/estree': 1.0.8 - esutils@2.0.3: {} - expect-type@1.2.2: {} exponential-backoff@3.1.3: {} @@ -5879,24 +3366,6 @@ snapshots: fast-deep-equal@3.1.3: {} - fast-glob@3.3.1: - dependencies: - '@nodelib/fs.stat': 2.0.5 - '@nodelib/fs.walk': 1.2.8 - glob-parent: 5.1.2 - merge2: 1.4.1 - micromatch: 4.0.8 - - fast-glob@3.3.3: - dependencies: - '@nodelib/fs.stat': 2.0.5 - '@nodelib/fs.walk': 1.2.8 - glob-parent: 5.1.2 - merge2: 1.4.1 - micromatch: 4.0.8 - - fast-json-stable-stringify@2.1.0: {} - fast-json-stringify@6.2.0: dependencies: '@fastify/merge-json-schemas': 0.2.1 @@ -5906,8 +3375,6 @@ snapshots: json-schema-ref-resolver: 3.0.0 rfdc: 1.4.1 - fast-levenshtein@2.0.6: {} - fast-querystring@1.1.2: dependencies: fast-decode-uri-component: 1.0.1 @@ -5942,10 +3409,6 @@ snapshots: optionalDependencies: picomatch: 4.0.3 - file-entry-cache@8.0.0: - dependencies: - flat-cache: 4.0.1 - fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 @@ -5956,22 +3419,6 @@ snapshots: fast-querystring: 1.1.2 safe-regex2: 5.0.0 - find-up@5.0.0: - dependencies: - locate-path: 6.0.0 - path-exists: 4.0.0 - - flat-cache@4.0.1: - dependencies: - flatted: 3.3.3 - keyv: 4.5.4 - - flatted@3.3.3: {} - - for-each@0.3.5: - dependencies: - is-callable: 1.2.7 - foreground-child@3.3.1: dependencies: cross-spawn: 7.0.6 @@ -5990,43 +3437,8 @@ snapshots: function-bind@1.1.2: {} - function.prototype.name@1.1.8: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.4 - define-properties: 1.2.1 - functions-have-names: 1.2.3 - hasown: 2.0.2 - is-callable: 1.2.7 - - functions-have-names@1.2.3: {} - get-caller-file@2.0.5: {} - get-intrinsic@1.3.0: - dependencies: - call-bind-apply-helpers: 1.0.2 - es-define-property: 1.0.1 - es-errors: 1.3.0 - es-object-atoms: 1.1.1 - function-bind: 1.1.2 - get-proto: 1.0.1 - gopd: 1.2.0 - has-symbols: 1.1.0 - hasown: 2.0.2 - math-intrinsics: 1.1.0 - - get-proto@1.0.1: - dependencies: - dunder-proto: 1.0.1 - es-object-atoms: 1.1.1 - - get-symbol-description@1.1.0: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - get-tsconfig@4.10.1: dependencies: resolve-pkg-maps: 1.0.0 @@ -6048,20 +3460,12 @@ snapshots: shelljs: 0.8.5 shelljs.exec: 1.1.8 - glob-parent@5.1.2: - dependencies: - is-glob: 4.0.3 - - glob-parent@6.0.2: - dependencies: - is-glob: 4.0.3 - - glob@10.4.5: + glob@10.5.0: dependencies: foreground-child: 3.3.1 jackspeak: 3.4.3 - minimatch: 9.0.5 - minipass: 7.1.2 + minimatch: 9.0.9 + minipass: 7.1.3 package-json-from-dist: 1.0.1 path-scurry: 1.11.1 @@ -6070,62 +3474,29 @@ snapshots: fs.realpath: 1.0.0 inflight: 1.0.6 inherits: 2.0.4 - minimatch: 3.1.2 + minimatch: 3.1.5 once: 1.4.0 path-is-absolute: 1.0.1 - globals@14.0.0: {} - - globals@16.3.0: {} - - globalthis@1.0.4: - dependencies: - define-properties: 1.2.1 - gopd: 1.2.0 - - gopd@1.2.0: {} - graceful-fs@4.2.11: {} - graphemer@1.4.0: {} - - has-bigints@1.1.0: {} - has-flag@3.0.0: {} has-flag@4.0.0: {} - has-property-descriptors@1.0.2: - dependencies: - es-define-property: 1.0.1 - - has-proto@1.2.0: - dependencies: - dunder-proto: 1.0.1 - - has-symbols@1.1.0: {} - - has-tostringtag@1.0.2: - dependencies: - has-symbols: 1.1.0 - hasown@2.0.2: dependencies: function-bind: 1.1.2 - ieee754@1.2.1: {} - - ignore@5.3.2: {} + helmet@8.1.0: {} - ignore@7.0.5: {} + ieee754@1.2.1: {} import-fresh@3.3.1: dependencies: parent-module: 1.0.1 resolve-from: 4.0.0 - imurmurhash@0.1.4: {} - inflight@1.0.6: dependencies: once: 1.4.0 @@ -6133,12 +3504,6 @@ snapshots: inherits@2.0.4: {} - internal-slot@1.1.0: - dependencies: - es-errors: 1.3.0 - hasown: 2.0.2 - side-channel: 1.1.0 - interpret@1.4.0: {} ioredis@5.9.2: @@ -6157,138 +3522,26 @@ snapshots: ipaddr.js@2.3.0: {} - is-array-buffer@3.0.5: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.4 - get-intrinsic: 1.3.0 - is-arrayish@0.2.1: {} - is-async-function@2.1.1: - dependencies: - async-function: 1.0.0 - call-bound: 1.0.4 - get-proto: 1.0.1 - has-tostringtag: 1.0.2 - safe-regex-test: 1.1.0 - - is-bigint@1.1.0: - dependencies: - has-bigints: 1.1.0 - - is-boolean-object@1.2.2: - dependencies: - call-bound: 1.0.4 - has-tostringtag: 1.0.2 - - is-callable@1.2.7: {} - is-core-module@2.16.1: dependencies: hasown: 2.0.2 - is-data-view@1.0.2: - dependencies: - call-bound: 1.0.4 - get-intrinsic: 1.3.0 - is-typed-array: 1.1.15 - - is-date-object@1.1.0: - dependencies: - call-bound: 1.0.4 - has-tostringtag: 1.0.2 - - is-extglob@2.1.1: {} - - is-finalizationregistry@1.1.1: - dependencies: - call-bound: 1.0.4 - is-fullwidth-code-point@3.0.0: {} - is-generator-function@1.1.0: - dependencies: - call-bound: 1.0.4 - get-proto: 1.0.1 - has-tostringtag: 1.0.2 - safe-regex-test: 1.1.0 - - is-glob@4.0.3: - dependencies: - is-extglob: 2.1.1 - is-interactive@1.0.0: {} - is-map@2.0.3: {} - - is-negative-zero@2.0.3: {} - - is-number-object@1.1.1: - dependencies: - call-bound: 1.0.4 - has-tostringtag: 1.0.2 - is-number@7.0.0: {} - is-regex@1.2.1: - dependencies: - call-bound: 1.0.4 - gopd: 1.2.0 - has-tostringtag: 1.0.2 - hasown: 2.0.2 - - is-set@2.0.3: {} - - is-shared-array-buffer@1.0.4: - dependencies: - call-bound: 1.0.4 - - is-string@1.1.1: - dependencies: - call-bound: 1.0.4 - has-tostringtag: 1.0.2 - - is-symbol@1.1.1: - dependencies: - call-bound: 1.0.4 - has-symbols: 1.1.0 - safe-regex-test: 1.1.0 - - is-typed-array@1.1.15: - dependencies: - which-typed-array: 1.1.19 - is-unicode-supported@0.1.0: {} - is-weakmap@2.0.2: {} - - is-weakref@1.1.1: - dependencies: - call-bound: 1.0.4 - - is-weakset@2.0.4: - dependencies: - call-bound: 1.0.4 - get-intrinsic: 1.3.0 - - isarray@2.0.5: {} - isexe@2.0.0: {} isomorphic-ws@4.0.1(ws@7.5.10): dependencies: ws: 7.5.10 - iterator.prototype@1.1.5: - dependencies: - define-data-property: 1.1.4 - es-object-atoms: 1.1.1 - get-intrinsic: 1.3.0 - get-proto: 1.0.1 - has-symbols: 1.1.0 - set-function-name: 2.0.2 - jackspeak@3.4.3: dependencies: '@isaacs/cliui': 8.0.2 @@ -6297,11 +3550,6 @@ snapshots: jiti@2.6.1: {} - jotai@2.15.1(@types/react@19.2.7)(react@19.2.3): - optionalDependencies: - '@types/react': 19.2.7 - react: 19.2.3 - js-tokens@4.0.0: {} js-tokens@9.0.1: {} @@ -6314,49 +3562,28 @@ snapshots: dependencies: bignumber.js: 9.3.1 - json-buffer@3.0.1: {} - json-parse-even-better-errors@2.3.1: {} json-schema-ref-resolver@3.0.0: dependencies: dequal: 2.0.3 - json-schema-traverse@0.4.1: {} - json-schema-traverse@1.0.0: {} - json-stable-stringify-without-jsonify@1.0.1: {} - - json2mq@0.2.0: - dependencies: - string-convert: 0.2.1 - jsonfile@6.1.0: dependencies: universalify: 2.0.1 optionalDependencies: graceful-fs: 4.2.11 - jsx-ast-utils@3.3.5: - dependencies: - array-includes: 3.1.9 - array.prototype.flat: 1.3.3 - object.assign: 4.1.7 - object.values: 1.2.1 - - keyv@4.5.4: - dependencies: - json-buffer: 3.0.1 - - kysely-codegen@0.19.0(kysely@0.28.11)(pg@8.18.0)(typescript@5.8.2): + kysely-codegen@0.19.0(kysely@0.28.14)(pg@8.18.0)(typescript@5.8.2): dependencies: chalk: 4.1.2 cosmiconfig: 9.0.0(typescript@5.8.2) dotenv: 17.2.3 dotenv-expand: 12.0.3 git-diff: 2.0.6 - kysely: 0.28.11 + kysely: 0.28.14 micromatch: 4.0.8 minimist: 1.2.8 pluralize: 8.0.0 @@ -6366,14 +3593,14 @@ snapshots: transitivePeerDependencies: - typescript - kysely-ctl@0.19.0(kysely@0.28.11)(typescript@5.8.2): + kysely-ctl@0.19.0(kysely@0.28.14)(typescript@5.8.2): dependencies: c12: 3.3.3 citty: 0.1.6 confbox: 0.2.2 consola: 3.4.2 jiti: 2.6.1 - kysely: 0.28.11 + kysely: 0.28.14 nypm: 0.6.4 ofetch: 1.5.1 pathe: 2.0.3 @@ -6384,12 +3611,7 @@ snapshots: - magicast - typescript - kysely@0.28.11: {} - - levn@0.4.1: - dependencies: - prelude-ls: 1.2.1 - type-check: 0.4.0 + kysely@0.28.14: {} light-my-request@6.6.0: dependencies: @@ -6399,17 +3621,11 @@ snapshots: lines-and-columns@1.2.4: {} - locate-path@6.0.0: - dependencies: - p-locate: 5.0.0 - lodash.defaults@4.2.0: {} lodash.isarguments@3.1.0: {} - lodash.merge@4.6.2: {} - - lodash@4.17.21: {} + lodash@4.17.23: {} log-symbols@4.1.0: dependencies: @@ -6418,10 +3634,6 @@ snapshots: loglevel@1.9.2: {} - loose-envify@1.4.0: - dependencies: - js-tokens: 4.0.0 - loupe@3.2.0: {} lru-cache@10.4.3: {} @@ -6430,10 +3642,6 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 - math-intrinsics@1.1.0: {} - - merge2@1.4.1: {} - micromatch@4.0.8: dependencies: braces: 3.0.3 @@ -6441,17 +3649,17 @@ snapshots: mimic-fn@2.1.0: {} - minimatch@3.1.2: + minimatch@3.1.5: dependencies: brace-expansion: 1.1.12 - minimatch@9.0.5: + minimatch@9.0.9: dependencies: brace-expansion: 2.0.2 minimist@1.2.8: {} - minipass@7.1.2: {} + minipass@7.1.3: {} mnemonist@0.40.0: dependencies: @@ -6461,31 +3669,6 @@ snapshots: nanoid@3.3.11: {} - natural-compare@1.4.0: {} - - next@16.0.10(react-dom@19.2.3(react@19.2.3))(react@19.2.3): - dependencies: - '@next/env': 16.0.10 - '@swc/helpers': 0.5.15 - caniuse-lite: 1.0.30001727 - postcss: 8.4.31 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - styled-jsx: 5.1.6(react@19.2.3) - optionalDependencies: - '@next/swc-darwin-arm64': 16.0.10 - '@next/swc-darwin-x64': 16.0.10 - '@next/swc-linux-arm64-gnu': 16.0.10 - '@next/swc-linux-arm64-musl': 16.0.10 - '@next/swc-linux-x64-gnu': 16.0.10 - '@next/swc-linux-x64-musl': 16.0.10 - '@next/swc-win32-arm64-msvc': 16.0.10 - '@next/swc-win32-x64-msvc': 16.0.10 - sharp: 0.34.5 - transitivePeerDependencies: - - '@babel/core' - - babel-plugin-macros - node-fetch-native@1.6.7: {} node-fetch@2.7.0: @@ -6500,42 +3683,6 @@ snapshots: pathe: 2.0.3 tinyexec: 1.0.2 - object-assign@4.1.1: {} - - object-inspect@1.13.4: {} - - object-keys@1.1.1: {} - - object.assign@4.1.7: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.4 - define-properties: 1.2.1 - es-object-atoms: 1.1.1 - has-symbols: 1.1.0 - object-keys: 1.1.1 - - object.entries@1.1.9: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.4 - define-properties: 1.2.1 - es-object-atoms: 1.1.1 - - object.fromentries@2.0.8: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - es-abstract: 1.24.0 - es-object-atoms: 1.1.1 - - object.values@1.2.1: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.4 - define-properties: 1.2.1 - es-object-atoms: 1.1.1 - obliterator@2.0.5: {} ofetch@1.5.1: @@ -6556,15 +3703,6 @@ snapshots: dependencies: mimic-fn: 2.1.0 - optionator@0.9.4: - dependencies: - deep-is: 0.1.4 - fast-levenshtein: 2.0.6 - levn: 0.4.1 - prelude-ls: 1.2.1 - type-check: 0.4.0 - word-wrap: 1.2.5 - ora@5.4.1: dependencies: bl: 4.1.0 @@ -6577,20 +3715,6 @@ snapshots: strip-ansi: 6.0.1 wcwidth: 1.0.1 - own-keys@1.0.1: - dependencies: - get-intrinsic: 1.3.0 - object-keys: 1.1.1 - safe-push-apply: 1.0.0 - - p-limit@3.1.0: - dependencies: - yocto-queue: 0.1.0 - - p-locate@5.0.0: - dependencies: - p-limit: 3.1.0 - p-timeout@7.0.1: {} package-json-from-dist@1.0.1: {} @@ -6606,8 +3730,6 @@ snapshots: json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 - path-exists@4.0.0: {} - path-is-absolute@1.0.1: {} path-key@3.1.1: {} @@ -6617,7 +3739,7 @@ snapshots: path-scurry@1.11.1: dependencies: lru-cache: 10.4.3 - minipass: 7.1.2 + minipass: 7.1.3 pathe@2.0.3: {} @@ -6694,14 +3816,6 @@ snapshots: pluralize@8.0.0: {} - possible-typed-array-names@1.1.0: {} - - postcss@8.4.31: - dependencies: - nanoid: 3.3.11 - picocolors: 1.1.1 - source-map-js: 1.2.1 - postcss@8.5.6: dependencies: nanoid: 3.3.11 @@ -6718,361 +3832,19 @@ snapshots: dependencies: xtend: 4.0.2 - prelude-ls@1.2.1: {} - process-warning@4.0.1: {} process-warning@5.0.0: {} - prop-types@15.8.1: - dependencies: - loose-envify: 1.4.0 - object-assign: 4.1.1 - react-is: 16.13.1 - - punycode@2.3.1: {} - pure-rand@6.1.0: {} - queue-microtask@1.2.3: {} - quick-format-unescaped@4.0.4: {} - rc-cascader@3.34.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3): - dependencies: - '@babel/runtime': 7.28.4 - classnames: 2.5.1 - rc-select: 14.16.8(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-tree: 5.13.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-util: 5.44.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - rc-checkbox@3.5.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3): - dependencies: - '@babel/runtime': 7.28.4 - classnames: 2.5.1 - rc-util: 5.44.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - rc-collapse@3.9.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3): - dependencies: - '@babel/runtime': 7.28.4 - classnames: 2.5.1 - rc-motion: 2.9.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-util: 5.44.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - rc-dialog@9.6.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3): - dependencies: - '@babel/runtime': 7.28.4 - '@rc-component/portal': 1.1.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - classnames: 2.5.1 - rc-motion: 2.9.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-util: 5.44.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - rc-drawer@7.3.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3): - dependencies: - '@babel/runtime': 7.28.4 - '@rc-component/portal': 1.1.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - classnames: 2.5.1 - rc-motion: 2.9.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-util: 5.44.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - rc-dropdown@4.2.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3): - dependencies: - '@babel/runtime': 7.28.4 - '@rc-component/trigger': 2.3.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - classnames: 2.5.1 - rc-util: 5.44.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - rc-field-form@2.7.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3): - dependencies: - '@babel/runtime': 7.28.4 - '@rc-component/async-validator': 5.0.4 - rc-util: 5.44.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - rc-image@7.12.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3): - dependencies: - '@babel/runtime': 7.28.4 - '@rc-component/portal': 1.1.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - classnames: 2.5.1 - rc-dialog: 9.6.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-motion: 2.9.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-util: 5.44.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - rc-input-number@9.5.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3): - dependencies: - '@babel/runtime': 7.28.4 - '@rc-component/mini-decimal': 1.1.0 - classnames: 2.5.1 - rc-input: 1.8.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-util: 5.44.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - rc-input@1.8.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3): - dependencies: - '@babel/runtime': 7.28.4 - classnames: 2.5.1 - rc-util: 5.44.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - rc-mentions@2.20.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3): - dependencies: - '@babel/runtime': 7.28.4 - '@rc-component/trigger': 2.3.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - classnames: 2.5.1 - rc-input: 1.8.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-menu: 9.16.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-textarea: 1.10.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-util: 5.44.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - rc-menu@9.16.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3): - dependencies: - '@babel/runtime': 7.28.4 - '@rc-component/trigger': 2.3.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - classnames: 2.5.1 - rc-motion: 2.9.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-overflow: 1.5.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-util: 5.44.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - rc-motion@2.9.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3): - dependencies: - '@babel/runtime': 7.28.4 - classnames: 2.5.1 - rc-util: 5.44.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - rc-notification@5.6.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3): - dependencies: - '@babel/runtime': 7.28.4 - classnames: 2.5.1 - rc-motion: 2.9.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-util: 5.44.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - rc-overflow@1.5.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3): - dependencies: - '@babel/runtime': 7.28.4 - classnames: 2.5.1 - rc-resize-observer: 1.4.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-util: 5.44.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - rc-pagination@5.1.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3): - dependencies: - '@babel/runtime': 7.28.4 - classnames: 2.5.1 - rc-util: 5.44.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - rc-picker@4.11.3(dayjs@1.11.18)(react-dom@19.2.3(react@19.2.3))(react@19.2.3): - dependencies: - '@babel/runtime': 7.28.4 - '@rc-component/trigger': 2.3.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - classnames: 2.5.1 - rc-overflow: 1.5.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-resize-observer: 1.4.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-util: 5.44.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - optionalDependencies: - dayjs: 1.11.18 - - rc-progress@4.0.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3): - dependencies: - '@babel/runtime': 7.28.4 - classnames: 2.5.1 - rc-util: 5.44.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - rc-rate@2.13.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3): - dependencies: - '@babel/runtime': 7.28.4 - classnames: 2.5.1 - rc-util: 5.44.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - rc-resize-observer@1.4.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3): - dependencies: - '@babel/runtime': 7.28.4 - classnames: 2.5.1 - rc-util: 5.44.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - resize-observer-polyfill: 1.5.1 - - rc-segmented@2.7.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3): - dependencies: - '@babel/runtime': 7.28.4 - classnames: 2.5.1 - rc-motion: 2.9.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-util: 5.44.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - rc-select@14.16.8(react-dom@19.2.3(react@19.2.3))(react@19.2.3): - dependencies: - '@babel/runtime': 7.28.4 - '@rc-component/trigger': 2.3.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - classnames: 2.5.1 - rc-motion: 2.9.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-overflow: 1.5.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-util: 5.44.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-virtual-list: 3.19.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - rc-slider@11.1.9(react-dom@19.2.3(react@19.2.3))(react@19.2.3): - dependencies: - '@babel/runtime': 7.28.4 - classnames: 2.5.1 - rc-util: 5.44.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - rc-steps@6.0.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3): - dependencies: - '@babel/runtime': 7.28.4 - classnames: 2.5.1 - rc-util: 5.44.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - rc-switch@4.1.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3): - dependencies: - '@babel/runtime': 7.28.4 - classnames: 2.5.1 - rc-util: 5.44.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - rc-table@7.54.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3): - dependencies: - '@babel/runtime': 7.28.4 - '@rc-component/context': 1.4.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - classnames: 2.5.1 - rc-resize-observer: 1.4.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-util: 5.44.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-virtual-list: 3.19.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - rc-tabs@15.7.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3): - dependencies: - '@babel/runtime': 7.28.4 - classnames: 2.5.1 - rc-dropdown: 4.2.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-menu: 9.16.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-motion: 2.9.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-resize-observer: 1.4.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-util: 5.44.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - rc-textarea@1.10.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3): - dependencies: - '@babel/runtime': 7.28.4 - classnames: 2.5.1 - rc-input: 1.8.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-resize-observer: 1.4.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-util: 5.44.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - rc-tooltip@6.4.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3): - dependencies: - '@babel/runtime': 7.28.4 - '@rc-component/trigger': 2.3.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - classnames: 2.5.1 - rc-util: 5.44.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - rc-tree-select@5.27.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3): - dependencies: - '@babel/runtime': 7.28.4 - classnames: 2.5.1 - rc-select: 14.16.8(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-tree: 5.13.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-util: 5.44.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - rc-tree@5.13.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3): - dependencies: - '@babel/runtime': 7.28.4 - classnames: 2.5.1 - rc-motion: 2.9.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-util: 5.44.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-virtual-list: 3.19.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - rc-upload@4.9.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3): - dependencies: - '@babel/runtime': 7.28.4 - classnames: 2.5.1 - rc-util: 5.44.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - rc-util@5.44.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3): - dependencies: - '@babel/runtime': 7.28.4 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - react-is: 18.3.1 - - rc-virtual-list@3.19.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3): - dependencies: - '@babel/runtime': 7.28.4 - classnames: 2.5.1 - rc-resize-observer: 1.4.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-util: 5.44.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - rc9@2.1.2: dependencies: defu: 6.1.4 destr: 2.0.5 - react-dom@19.2.3(react@19.2.3): - dependencies: - react: 19.2.3 - scheduler: 0.27.0 - - react-is@16.13.1: {} - - react-is@18.3.1: {} - - react@19.2.3: {} - readable-stream@3.6.2: dependencies: inherits: 2.0.4 @@ -7093,26 +3865,6 @@ snapshots: dependencies: redis-errors: 1.2.0 - reflect.getprototypeof@1.0.10: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - es-abstract: 1.24.0 - es-errors: 1.3.0 - es-object-atoms: 1.1.1 - get-intrinsic: 1.3.0 - get-proto: 1.0.1 - which-builtin-type: 1.2.1 - - regexp.prototype.flags@1.5.4: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - es-errors: 1.3.0 - get-proto: 1.0.1 - gopd: 1.2.0 - set-function-name: 2.0.2 - remeda@2.28.0: dependencies: type-fest: 4.41.0 @@ -7123,8 +3875,6 @@ snapshots: require-from-string@2.0.2: {} - resize-observer-polyfill@1.5.1: {} - resolve-from@4.0.0: {} resolve-pkg-maps@1.0.0: {} @@ -7135,12 +3885,6 @@ snapshots: path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - resolve@2.0.0-next.5: - dependencies: - is-core-module: 2.16.1 - path-parse: 1.0.7 - supports-preserve-symlinks-flag: 1.0.0 - restore-cursor@3.1.0: dependencies: onetime: 5.1.2 @@ -7152,137 +3896,55 @@ snapshots: rfdc@1.4.1: {} - rollup@4.46.3: + rollup@4.60.0: dependencies: '@types/estree': 1.0.8 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.46.3 - '@rollup/rollup-android-arm64': 4.46.3 - '@rollup/rollup-darwin-arm64': 4.46.3 - '@rollup/rollup-darwin-x64': 4.46.3 - '@rollup/rollup-freebsd-arm64': 4.46.3 - '@rollup/rollup-freebsd-x64': 4.46.3 - '@rollup/rollup-linux-arm-gnueabihf': 4.46.3 - '@rollup/rollup-linux-arm-musleabihf': 4.46.3 - '@rollup/rollup-linux-arm64-gnu': 4.46.3 - '@rollup/rollup-linux-arm64-musl': 4.46.3 - '@rollup/rollup-linux-loongarch64-gnu': 4.46.3 - '@rollup/rollup-linux-ppc64-gnu': 4.46.3 - '@rollup/rollup-linux-riscv64-gnu': 4.46.3 - '@rollup/rollup-linux-riscv64-musl': 4.46.3 - '@rollup/rollup-linux-s390x-gnu': 4.46.3 - '@rollup/rollup-linux-x64-gnu': 4.46.3 - '@rollup/rollup-linux-x64-musl': 4.46.3 - '@rollup/rollup-win32-arm64-msvc': 4.46.3 - '@rollup/rollup-win32-ia32-msvc': 4.46.3 - '@rollup/rollup-win32-x64-msvc': 4.46.3 + '@rollup/rollup-android-arm-eabi': 4.60.0 + '@rollup/rollup-android-arm64': 4.60.0 + '@rollup/rollup-darwin-arm64': 4.60.0 + '@rollup/rollup-darwin-x64': 4.60.0 + '@rollup/rollup-freebsd-arm64': 4.60.0 + '@rollup/rollup-freebsd-x64': 4.60.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.60.0 + '@rollup/rollup-linux-arm-musleabihf': 4.60.0 + '@rollup/rollup-linux-arm64-gnu': 4.60.0 + '@rollup/rollup-linux-arm64-musl': 4.60.0 + '@rollup/rollup-linux-loong64-gnu': 4.60.0 + '@rollup/rollup-linux-loong64-musl': 4.60.0 + '@rollup/rollup-linux-ppc64-gnu': 4.60.0 + '@rollup/rollup-linux-ppc64-musl': 4.60.0 + '@rollup/rollup-linux-riscv64-gnu': 4.60.0 + '@rollup/rollup-linux-riscv64-musl': 4.60.0 + '@rollup/rollup-linux-s390x-gnu': 4.60.0 + '@rollup/rollup-linux-x64-gnu': 4.60.0 + '@rollup/rollup-linux-x64-musl': 4.60.0 + '@rollup/rollup-openbsd-x64': 4.60.0 + '@rollup/rollup-openharmony-arm64': 4.60.0 + '@rollup/rollup-win32-arm64-msvc': 4.60.0 + '@rollup/rollup-win32-ia32-msvc': 4.60.0 + '@rollup/rollup-win32-x64-gnu': 4.60.0 + '@rollup/rollup-win32-x64-msvc': 4.60.0 fsevents: 2.3.3 - run-parallel@1.2.0: - dependencies: - queue-microtask: 1.2.3 - - safe-array-concat@1.1.3: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.4 - get-intrinsic: 1.3.0 - has-symbols: 1.1.0 - isarray: 2.0.5 - safe-buffer@5.2.1: {} - safe-push-apply@1.0.0: - dependencies: - es-errors: 1.3.0 - isarray: 2.0.5 - - safe-regex-test@1.1.0: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - is-regex: 1.2.1 - safe-regex2@5.0.0: dependencies: ret: 0.5.0 safe-stable-stringify@2.5.0: {} - scheduler@0.27.0: {} - - scroll-into-view-if-needed@3.1.0: - dependencies: - compute-scroll-into-view: 3.1.1 - secure-json-parse@4.1.0: {} - semver@6.3.1: {} - - semver@7.7.2: {} - semver@7.7.3: {} set-cookie-parser@2.7.2: {} - set-function-length@1.2.2: - dependencies: - define-data-property: 1.1.4 - es-errors: 1.3.0 - function-bind: 1.1.2 - get-intrinsic: 1.3.0 - gopd: 1.2.0 - has-property-descriptors: 1.0.2 - - set-function-name@2.0.2: - dependencies: - define-data-property: 1.1.4 - es-errors: 1.3.0 - functions-have-names: 1.2.3 - has-property-descriptors: 1.0.2 - - set-proto@1.0.0: - dependencies: - dunder-proto: 1.0.1 - es-errors: 1.3.0 - es-object-atoms: 1.1.1 - sha3@2.1.4: dependencies: buffer: 6.0.3 - sharp@0.34.5: - dependencies: - '@img/colour': 1.0.0 - detect-libc: 2.1.2 - semver: 7.7.3 - optionalDependencies: - '@img/sharp-darwin-arm64': 0.34.5 - '@img/sharp-darwin-x64': 0.34.5 - '@img/sharp-libvips-darwin-arm64': 1.2.4 - '@img/sharp-libvips-darwin-x64': 1.2.4 - '@img/sharp-libvips-linux-arm': 1.2.4 - '@img/sharp-libvips-linux-arm64': 1.2.4 - '@img/sharp-libvips-linux-ppc64': 1.2.4 - '@img/sharp-libvips-linux-riscv64': 1.2.4 - '@img/sharp-libvips-linux-s390x': 1.2.4 - '@img/sharp-libvips-linux-x64': 1.2.4 - '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 - '@img/sharp-libvips-linuxmusl-x64': 1.2.4 - '@img/sharp-linux-arm': 0.34.5 - '@img/sharp-linux-arm64': 0.34.5 - '@img/sharp-linux-ppc64': 0.34.5 - '@img/sharp-linux-riscv64': 0.34.5 - '@img/sharp-linux-s390x': 0.34.5 - '@img/sharp-linux-x64': 0.34.5 - '@img/sharp-linuxmusl-arm64': 0.34.5 - '@img/sharp-linuxmusl-x64': 0.34.5 - '@img/sharp-wasm32': 0.34.5 - '@img/sharp-win32-arm64': 0.34.5 - '@img/sharp-win32-ia32': 0.34.5 - '@img/sharp-win32-x64': 0.34.5 - optional: true - shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 @@ -7297,34 +3959,6 @@ snapshots: interpret: 1.4.0 rechoir: 0.6.2 - side-channel-list@1.0.0: - dependencies: - es-errors: 1.3.0 - object-inspect: 1.13.4 - - side-channel-map@1.0.1: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - object-inspect: 1.13.4 - - side-channel-weakmap@1.0.2: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - object-inspect: 1.13.4 - side-channel-map: 1.0.1 - - side-channel@1.1.0: - dependencies: - es-errors: 1.3.0 - object-inspect: 1.13.4 - side-channel-list: 1.0.0 - side-channel-map: 1.0.1 - side-channel-weakmap: 1.0.2 - siginfo@2.0.0: {} signal-exit@3.0.7: {} @@ -7336,13 +3970,13 @@ snapshots: '@socket.io/component-emitter': 3.1.2 debug: 4.4.1 engine.io-client: 6.6.4 - socket.io-parser: 4.2.5 + socket.io-parser: 4.2.6 transitivePeerDependencies: - bufferutil - supports-color - utf-8-validate - socket.io-parser@4.2.5: + socket.io-parser@4.2.6: dependencies: '@socket.io/component-emitter': 3.1.2 debug: 4.4.1 @@ -7363,13 +3997,6 @@ snapshots: std-env@3.9.0: {} - stop-iteration-iterator@1.1.0: - dependencies: - es-errors: 1.3.0 - internal-slot: 1.1.0 - - string-convert@0.2.1: {} - string-width@4.2.3: dependencies: emoji-regex: 8.0.0 @@ -7380,51 +4007,7 @@ snapshots: dependencies: eastasianwidth: 0.2.0 emoji-regex: 9.2.2 - strip-ansi: 7.1.0 - - string.prototype.matchall@4.0.12: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.4 - define-properties: 1.2.1 - es-abstract: 1.24.0 - es-errors: 1.3.0 - es-object-atoms: 1.1.1 - get-intrinsic: 1.3.0 - gopd: 1.2.0 - has-symbols: 1.1.0 - internal-slot: 1.1.0 - regexp.prototype.flags: 1.5.4 - set-function-name: 2.0.2 - side-channel: 1.1.0 - - string.prototype.repeat@1.0.0: - dependencies: - define-properties: 1.2.1 - es-abstract: 1.24.0 - - string.prototype.trim@1.2.10: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.4 - define-data-property: 1.1.4 - define-properties: 1.2.1 - es-abstract: 1.24.0 - es-object-atoms: 1.1.1 - has-property-descriptors: 1.0.2 - - string.prototype.trimend@1.0.9: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.4 - define-properties: 1.2.1 - es-object-atoms: 1.1.1 - - string.prototype.trimstart@1.0.8: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - es-object-atoms: 1.1.1 + strip-ansi: 7.2.0 string_decoder@1.3.0: dependencies: @@ -7434,23 +4017,14 @@ snapshots: dependencies: ansi-regex: 5.0.1 - strip-ansi@7.1.0: + strip-ansi@7.2.0: dependencies: - ansi-regex: 6.1.0 - - strip-json-comments@3.1.1: {} + ansi-regex: 6.2.2 strip-literal@3.0.0: dependencies: js-tokens: 9.0.1 - styled-jsx@5.1.6(react@19.2.3): - dependencies: - client-only: 0.0.1 - react: 19.2.3 - - stylis@4.3.6: {} - supports-color@5.5.0: dependencies: has-flag: 3.0.0 @@ -7465,8 +4039,6 @@ snapshots: dependencies: real-require: 0.2.0 - throttle-debounce@5.0.2: {} - tinybench@2.9.0: {} tinyexec@0.3.2: {} @@ -7490,14 +4062,8 @@ snapshots: toad-cache@3.7.0: {} - toggle-selection@1.0.6: {} - tr46@0.0.3: {} - ts-api-utils@2.1.0(typescript@5.8.2): - dependencies: - typescript: 5.8.2 - ts-custom-error@3.3.1: {} tsconfck@3.1.6(typescript@5.8.2): @@ -7540,77 +4106,18 @@ snapshots: turbo-windows-64: 2.5.5 turbo-windows-arm64: 2.5.5 - type-check@0.4.0: - dependencies: - prelude-ls: 1.2.1 - type-fest@4.41.0: {} - typed-array-buffer@1.0.3: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - is-typed-array: 1.1.15 - - typed-array-byte-length@1.0.3: - dependencies: - call-bind: 1.0.8 - for-each: 0.3.5 - gopd: 1.2.0 - has-proto: 1.2.0 - is-typed-array: 1.1.15 - - typed-array-byte-offset@1.0.4: - dependencies: - available-typed-arrays: 1.0.7 - call-bind: 1.0.8 - for-each: 0.3.5 - gopd: 1.2.0 - has-proto: 1.2.0 - is-typed-array: 1.1.15 - reflect.getprototypeof: 1.0.10 - - typed-array-length@1.0.7: - dependencies: - call-bind: 1.0.8 - for-each: 0.3.5 - gopd: 1.2.0 - is-typed-array: 1.1.15 - possible-typed-array-names: 1.1.0 - reflect.getprototypeof: 1.0.10 - - typescript-eslint@8.37.0(eslint@9.31.0(jiti@2.6.1))(typescript@5.8.2): - dependencies: - '@typescript-eslint/eslint-plugin': 8.37.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0(jiti@2.6.1))(typescript@5.8.2))(eslint@9.31.0(jiti@2.6.1))(typescript@5.8.2) - '@typescript-eslint/parser': 8.37.0(eslint@9.31.0(jiti@2.6.1))(typescript@5.8.2) - '@typescript-eslint/typescript-estree': 8.37.0(typescript@5.8.2) - '@typescript-eslint/utils': 8.37.0(eslint@9.31.0(jiti@2.6.1))(typescript@5.8.2) - eslint: 9.31.0(jiti@2.6.1) - typescript: 5.8.2 - transitivePeerDependencies: - - supports-color - typescript@5.8.2: {} typescript@5.8.3: {} ufo@1.6.3: {} - unbox-primitive@1.1.0: - dependencies: - call-bound: 1.0.4 - has-bigints: 1.1.0 - has-symbols: 1.1.0 - which-boxed-primitive: 1.1.1 - undici-types@6.21.0: {} universalify@2.0.1: {} - uri-js@4.4.1: - dependencies: - punycode: 2.3.1 - util-deprecate@1.0.2: {} vite-node@3.2.4(@types/node@22.15.3)(jiti@2.6.1)(tsx@4.20.3): @@ -7640,7 +4147,7 @@ snapshots: fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 postcss: 8.5.6 - rollup: 4.46.3 + rollup: 4.60.0 tinyglobby: 0.2.14 optionalDependencies: '@types/node': 22.15.3 @@ -7700,47 +4207,6 @@ snapshots: tr46: 0.0.3 webidl-conversions: 3.0.1 - which-boxed-primitive@1.1.1: - dependencies: - is-bigint: 1.1.0 - is-boolean-object: 1.2.2 - is-number-object: 1.1.1 - is-string: 1.1.1 - is-symbol: 1.1.1 - - which-builtin-type@1.2.1: - dependencies: - call-bound: 1.0.4 - function.prototype.name: 1.1.8 - has-tostringtag: 1.0.2 - is-async-function: 2.1.1 - is-date-object: 1.1.0 - is-finalizationregistry: 1.1.1 - is-generator-function: 1.1.0 - is-regex: 1.2.1 - is-weakref: 1.1.1 - isarray: 2.0.5 - which-boxed-primitive: 1.1.1 - which-collection: 1.0.2 - which-typed-array: 1.1.19 - - which-collection@1.0.2: - dependencies: - is-map: 2.0.3 - is-set: 2.0.3 - is-weakmap: 2.0.2 - is-weakset: 2.0.4 - - which-typed-array@1.1.19: - dependencies: - available-typed-arrays: 1.0.7 - call-bind: 1.0.8 - call-bound: 1.0.4 - for-each: 0.3.5 - get-proto: 1.0.1 - gopd: 1.2.0 - has-tostringtag: 1.0.2 - which@2.0.2: dependencies: isexe: 2.0.0 @@ -7750,8 +4216,6 @@ snapshots: siginfo: 2.0.0 stackback: 0.0.2 - word-wrap@1.2.5: {} - wrap-ansi@7.0.0: dependencies: ansi-styles: 4.3.0 @@ -7760,9 +4224,9 @@ snapshots: wrap-ansi@8.1.0: dependencies: - ansi-styles: 6.2.1 + ansi-styles: 6.2.3 string-width: 5.1.2 - strip-ansi: 7.1.0 + strip-ansi: 7.2.0 wrappy@1.0.2: {} @@ -7788,6 +4252,4 @@ snapshots: y18n: 5.0.8 yargs-parser: 21.1.1 - yocto-queue@0.1.0: {} - zod@4.3.6: {} diff --git a/turbo.json b/turbo.json index 70801e2..8e3d7ee 100644 --- a/turbo.json +++ b/turbo.json @@ -5,7 +5,7 @@ "build": { "dependsOn": ["^build"], "inputs": ["$TURBO_DEFAULT$", ".env*"], - "outputs": [".next/**", "!.next/cache/**", "dist/**"] + "outputs": ["dist/**"] }, "@minswap/felis-ledger-utils#build": { "dependsOn": ["@minswap/felis-uplc-node#build", "@minswap/felis-uplc-web#build"], @@ -137,22 +137,6 @@ "inputs": ["$TURBO_DEFAULT$", ".env*"], "outputs": ["dist/**"] }, - "web#build": { - "dependsOn": [ - "@minswap/felis-ledger-core#build", - "@minswap/felis-ledger-utils#build", - "@minswap/felis-tx-builder#build", - "@minswap/felis-cip#build", - "@minswap/felis-lending-market#build", - "@minswap/felis-dex-v2#build", - "@minswap/felis-build-tx#build" - ], - "inputs": ["$TURBO_DEFAULT$", ".env*"], - "outputs": [".next/**", "!.next/cache/**"] - }, - "lint": { - "dependsOn": ["^lint"] - }, "check-types": { "dependsOn": ["^check-types"] }, From 2dcc6d6bca18c18a6c4d2da62ed1c49ecfd8c496 Mon Sep 17 00:00:00 2001 From: tony Date: Tue, 24 Mar 2026 10:34:18 +0700 Subject: [PATCH 05/10] Revert "debug" This reverts commit f023d228c28acb208e5f588851a6a9ebdcdc80ac. --- .../src/api/state-machine.ts | 44 +++++++------------ 1 file changed, 17 insertions(+), 27 deletions(-) diff --git a/apps/long-short-backend/src/api/state-machine.ts b/apps/long-short-backend/src/api/state-machine.ts index f29ab63..7ba13ce 100644 --- a/apps/long-short-backend/src/api/state-machine.ts +++ b/apps/long-short-backend/src/api/state-machine.ts @@ -1101,25 +1101,23 @@ export namespace StateMachine { // SHORT_REPAY_FRACTION ──── modifyBorrow(newDebt, keepCollateral) // │ // ▼ - // SHORT_BUY_FREED ────────── buy asset B with user's remaining ADA (A_TO_B swap) + // SHORT_WITHDRAW_FRACTION ── modifyBorrow(sameDebt, reducedCollateral, redeemCollateral=true) → ADA + // │ + // ▼ + // SHORT_BUY_FREED ────────── buy asset B with freed ADA (A_TO_B swap) // │ // ├── asset B received < remaining loan? ──► loop back to SHORT_REPAY_FRACTION // │ // └── asset B received >= remaining loan? ──► SHORT_REPAY (full repay) // │ // SHORT_WITHDRAW - // - // Note: Unlike LONG fractional flow, SHORT skips WITHDRAW_FRACTION because - // Liqwid's modifyBorrow with redeemCollateral=true requires MarketParams for - // the collateral market (qADA), which may not be available. Instead, collateral - // stays locked until full repay, and the user's existing ADA buys more asset B. // ============================================================================ /** * Build SHORT_REPAY_FRACTION: Partial debt repay via modifyBorrow. * Self-contained — fetches current loan from Liqwid API. * Reduces debt by available asset B while keeping full collateral (qADA) unchanged. - * After confirmation, transitions to SHORT_BUY_FREED to buy more asset B with user's ADA. + * Step 2 (SHORT_WITHDRAW_FRACTION) reduces collateral proportionally in a separate tx. */ export const handleShortRepayFraction = async (options: HandleBuildTxOptions): Promise => { const { order, marketConfig, userAddress, networkEnv, utxos } = options; @@ -1212,11 +1210,12 @@ export namespace StateMachine { * SHORT_REPAY_FRACTION is a Liqwid modifyBorrow tx that reduces debt while keeping * full collateral. Asset B is consumed as repayment — no tokens returned to user. * - * On confirmation → transition to SHORT_BUY_FREED: - * Buy more asset B with user's remaining ADA to cover remaining loan. + * On confirmation → transition to SHORT_WITHDRAW_FRACTION: + * amountIn = originalDebtRaw (for proportional collateral calc in handleShortWithdrawFraction) */ export const waitingShortRepayFraction = async (options: WaitingOptions): Promise => { - const { marketConfig, txHash, userAddress, cardanoscanProvider } = options; + const { marketConfig, txHash, userAddress, cardanoscanProvider, originalDebtLovelace } = options; + invariant(originalDebtLovelace, "originalDebtLovelace is required for waitingShortRepayFraction"); const txFoundOnChain = await cardanoscanProvider.findTransactionByHash( userAddress, @@ -1226,25 +1225,12 @@ export namespace StateMachine { ); if (txFoundOnChain) { - // After partial repay, transition to SHORT_BUY_FREED to buy more asset B - // with user's remaining ADA. The handler will use whatever ADA the user has. - const userAddressHex = userAddress.toHex(); - - // Find ADA balance from tx outputs to pass as amountIn for the buy - let adaBalance = 0n; - for (const output of txFoundOnChain.outputs) { - if (output.address === userAddressHex) { - adaBalance += BigInt(output.value); - } - } - return { isConfirmed: true, - nextOrderType: ShortOrderType.SHORT_BUY_FREED, - assetIn: marketConfig.assetA.toString(), // ADA - amountIn: adaBalance.toString(), - assetOut: marketConfig.assetB.toString(), // asset B to buy - amountOut: adaBalance.toString(), + nextOrderType: ShortOrderType.SHORT_WITHDRAW_FRACTION, + assetIn: marketConfig.assetA.toString(), // placeholder (handler fetches loan from API) + amountIn: originalDebtLovelace, // original debt for proportional collateral calc + assetOut: marketConfig.assetA.toString(), // ADA will be received after withdraw }; } @@ -1442,6 +1428,7 @@ export namespace StateMachine { amountOut: amountOut.toString(), additionalOrders: [ { orderType: ShortOrderType.SHORT_REPAY_FRACTION }, + { orderType: ShortOrderType.SHORT_WITHDRAW_FRACTION }, { orderType: ShortOrderType.SHORT_BUY_FREED }, ], originalDebtLovelace: loanAmountRaw.toString(), @@ -2104,6 +2091,7 @@ export namespace StateMachine { amountOut: amountOut.toString(), additionalOrders: [ { orderType: ShortOrderType.SHORT_REPAY_FRACTION }, + { orderType: ShortOrderType.SHORT_WITHDRAW_FRACTION }, { orderType: ShortOrderType.SHORT_BUY_FREED }, ], originalDebtLovelace: loanAmountRaw.toString(), @@ -2222,6 +2210,7 @@ export namespace StateMachine { [ShortOrderType.SHORT_BUY]: handleShortBuy, [ShortOrderType.SHORT_REPAY]: handleShortRepay, [ShortOrderType.SHORT_REPAY_FRACTION]: handleShortRepayFraction, + [ShortOrderType.SHORT_WITHDRAW_FRACTION]: handleShortWithdrawFraction, [ShortOrderType.SHORT_BUY_FREED]: handleShortBuyFreed, [ShortOrderType.SHORT_WITHDRAW]: handleShortWithdraw, }; @@ -2245,6 +2234,7 @@ export namespace StateMachine { [ShortOrderType.SHORT_BUY]: waitingShortBuy, [ShortOrderType.SHORT_REPAY]: waitingShortRepay, [ShortOrderType.SHORT_REPAY_FRACTION]: waitingShortRepayFraction, + [ShortOrderType.SHORT_WITHDRAW_FRACTION]: waitingShortWithdrawFraction, [ShortOrderType.SHORT_BUY_FREED]: waitingShortBuyFreed, [ShortOrderType.SHORT_WITHDRAW]: waitingShortWithdraw, }; From f16230efeee03ac569792ba1b730af1a39a21070 Mon Sep 17 00:00:00 2001 From: tony Date: Tue, 24 Mar 2026 12:57:30 +0700 Subject: [PATCH 06/10] small fixes --- .../documents/external-integrations.md | 2 +- .../src/provider/minswap-aggregator.ts | 7 ++++--- docker-compose.yml | 19 ------------------- 3 files changed, 5 insertions(+), 23 deletions(-) diff --git a/apps/long-short-backend/documents/external-integrations.md b/apps/long-short-backend/documents/external-integrations.md index 149d2e6..98e42b0 100644 --- a/apps/long-short-backend/documents/external-integrations.md +++ b/apps/long-short-backend/documents/external-integrations.md @@ -38,7 +38,7 @@ Price estimation for swaps. - `estimate(request)` -- GET swap output amount **URLs:** -- Mainnet: `https://monorepo-mainnet-prod.minswap.org/aggregator/estimate` +- Mainnet: `https://aggr-monorepo-mainnet-prod.minswap.org/aggregator/estimate` - Preview: `https://aggr.dev-3.minswap.org/aggregator/estimate` Used to estimate SHORT borrow amounts: `amount_in * shortLeverage` ADA worth of asset B. diff --git a/apps/long-short-backend/src/provider/minswap-aggregator.ts b/apps/long-short-backend/src/provider/minswap-aggregator.ts index ce8190e..300f703 100644 --- a/apps/long-short-backend/src/provider/minswap-aggregator.ts +++ b/apps/long-short-backend/src/provider/minswap-aggregator.ts @@ -2,7 +2,7 @@ import { NetworkEnvironment } from "@minswap/felis-ledger-core"; import { logger } from "../utils"; const BASE_URLS: Record = { - [NetworkEnvironment.MAINNET]: "https://monorepo-mainnet-prod.minswap.org", + [NetworkEnvironment.MAINNET]: "https://aggr-monorepo-mainnet-prod.minswap.org", [NetworkEnvironment.TESTNET_PREVIEW]: "https://aggr.dev-3.minswap.org", [NetworkEnvironment.TESTNET_PREPROD]: "https://aggr.dev-3.minswap.org", }; @@ -45,8 +45,9 @@ export class MinswapAggregatorProvider { token_in: tokenIn, token_out: tokenOut, slippage, - exclude_protocols: ["MuesliSwap"], - allow_multi_hops: true, + include: ["MinswapV2"], + allow_multi_hops: false, + allowNonAtomicMultiHops: false, }); const response = await fetch(url, { diff --git a/docker-compose.yml b/docker-compose.yml index c5ec3ab..99296e1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,11 +8,8 @@ x-base-backend: &base-backend depends_on: postgres: condition: service_healthy - redis: - condition: service_healthy environment: DATABASE_URL: postgres://postgres:JBNGlQ9wNFLlYWc2mG@postgres:5432/margin - REDIS_URL: redis://default:7obaQyYSDDLDk3ECYA@redis:6379 API_PORT: 9999 API_HOST: 0.0.0.0 NETWORK: testnet-preview @@ -32,21 +29,6 @@ services: - "9999:9999" command: ["pnpm", "--filter=long-short-backend", "start"] - redis: - image: redis:8 - container_name: margin-redis - restart: always - command: --requirepass 7obaQyYSDDLDk3ECYA - ports: - - "6380:6379" - volumes: - - redis-data:/data - healthcheck: - test: ["CMD", "redis-cli", "-a", "7obaQyYSDDLDk3ECYA", "--raw", "incr", "ping"] - interval: 5s - timeout: 3s - retries: 5 - postgres: image: postgres:18 container_name: margin-postgres @@ -67,4 +49,3 @@ services: volumes: postgres-data: {} - redis-data: {} From 9d716b4b8587d2f4660270c1ee4967d98dafd814 Mon Sep 17 00:00:00 2001 From: tony Date: Tue, 24 Mar 2026 15:01:22 +0700 Subject: [PATCH 07/10] add parameters.min_value --- apps/long-short-backend/src/api/routes/metadata.ts | 14 +++++++++----- apps/long-short-backend/src/api/schemas.ts | 6 ++++++ 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/apps/long-short-backend/src/api/routes/metadata.ts b/apps/long-short-backend/src/api/routes/metadata.ts index d96f2e7..1d00474 100644 --- a/apps/long-short-backend/src/api/routes/metadata.ts +++ b/apps/long-short-backend/src/api/routes/metadata.ts @@ -6,9 +6,10 @@ import { API_ENDPOINTS } from "../../constants"; import { logger } from "../../utils"; import { type MarketConfigResponseType, MetadataResponseSchema, type MetadataResponseType } from "../schemas"; -type LiqwidMarketApyMap = Map; +type LiqwidMarketData = { borrowAPY: number; supplyAPY: number; minValue: string }; +type LiqwidMarketDataMap = Map; -function marketConfigToResponse(config: MarketConfig, liqwidApys: LiqwidMarketApyMap): MarketConfigResponseType { +function marketConfigToResponse(config: MarketConfig, liqwidApys: LiqwidMarketDataMap): MarketConfigResponseType { const assetAApys = liqwidApys.get(config.borrowMarketIdLong); const assetBApys = liqwidApys.get(config.borrowMarketIdShort); @@ -28,18 +29,21 @@ function marketConfigToResponse(config: MarketConfig, liqwidApys: LiqwidMarketAp min_collateral: config.minCollateral.toString(), asset_a_borrow_apy: assetAApys?.borrowAPY ?? null, asset_a_supply_apy: assetAApys?.supplyAPY ?? null, + asset_a_min_value: assetAApys?.minValue ?? null, asset_b_borrow_apy: assetBApys?.borrowAPY ?? null, asset_b_supply_apy: assetBApys?.supplyAPY ?? null, + asset_b_min_value: assetBApys?.minValue ?? null, }; } -async function fetchLiqwidMarketApys(networkEnv: NetworkEnvironment): Promise { - const apyMap: LiqwidMarketApyMap = new Map(); +async function fetchLiqwidMarketApys(networkEnv: NetworkEnvironment): Promise { + const apyMap: LiqwidMarketDataMap = new Map(); const config = LiqwidProviderV2.createConfig(networkEnv); const result = await LiqwidProviderV2.Data.markets(config); if (result.type === "ok") { for (const market of result.value.results) { - apyMap.set(market.id, { borrowAPY: market.borrowAPY, supplyAPY: market.supplyAPY }); + const minValue = (market.parameters.minValue * 1_000_000).toString(); + apyMap.set(market.id, { borrowAPY: market.borrowAPY, supplyAPY: market.supplyAPY, minValue }); } } else { logger.error("Failed to fetch Liqwid market data", { error: result.error }); diff --git a/apps/long-short-backend/src/api/schemas.ts b/apps/long-short-backend/src/api/schemas.ts index 7831c38..e7e298e 100644 --- a/apps/long-short-backend/src/api/schemas.ts +++ b/apps/long-short-backend/src/api/schemas.ts @@ -183,12 +183,18 @@ export const MarketConfigResponseSchema = Type.Object({ asset_a_supply_apy: Type.Union([Type.Number(), Type.Null()], { description: "Liqwid supply APY for asset A", }), + asset_a_min_value: Type.Union([Type.String(), Type.Null()], { + description: "Liqwid minimum value for asset A in lovelace", + }), asset_b_borrow_apy: Type.Union([Type.Number(), Type.Null()], { description: "Liqwid borrow APY for asset B", }), asset_b_supply_apy: Type.Union([Type.Number(), Type.Null()], { description: "Liqwid supply APY for asset B", }), + asset_b_min_value: Type.Union([Type.String(), Type.Null()], { + description: "Liqwid minimum value for asset B in lovelace", + }), }); export type MarketConfigResponseType = Static; From ca0d976de7f9304c8f19372d4ef82f046ae8405a Mon Sep 17 00:00:00 2001 From: tony Date: Tue, 24 Mar 2026 18:02:23 +0700 Subject: [PATCH 08/10] new api --- .../src/api/routes/position.ts | 31 ++++++++++++++++ apps/long-short-backend/src/api/schemas.ts | 22 ++++++++++++ apps/long-short-backend/src/constants.ts | 5 +++ .../src/services/position-service.ts | 35 +++++++++++++++++++ 4 files changed, 93 insertions(+) diff --git a/apps/long-short-backend/src/api/routes/position.ts b/apps/long-short-backend/src/api/routes/position.ts index e562481..91b9ead 100644 --- a/apps/long-short-backend/src/api/routes/position.ts +++ b/apps/long-short-backend/src/api/routes/position.ts @@ -18,6 +18,10 @@ import { CreatePositionResponseSchema, type CreatePositionResponseType, ErrorResponseSchema, + GetDebtQuerySchema, + type GetDebtQueryType, + GetDebtResponseSchema, + type GetDebtResponseType, GetPositionQuerySchema, type GetPositionQueryType, GetPositionResponseSchema, @@ -247,4 +251,31 @@ export function registerPositionRoutes(fastify: FastifyInstance, positionService }); }, ); + + // GET /position/debt?user_address=...&market_id=... + fastify.get<{ + Querystring: GetDebtQueryType; + Reply: GetDebtResponseType; + }>( + API_ENDPOINTS.POSITION_DEBT, + { + schema: { + querystring: GetDebtQuerySchema, + response: { + 200: GetDebtResponseSchema, + 400: ErrorResponseSchema, + }, + }, + }, + async (request, reply) => { + const { user_address, market_id } = request.query; + + const debt = await positionService.getDebt(user_address, market_id); + + return reply.status(200).send({ + success: true, + data: debt, + }); + }, + ); } diff --git a/apps/long-short-backend/src/api/schemas.ts b/apps/long-short-backend/src/api/schemas.ts index e7e298e..09756ff 100644 --- a/apps/long-short-backend/src/api/schemas.ts +++ b/apps/long-short-backend/src/api/schemas.ts @@ -83,6 +83,28 @@ export const GetPositionResponseSchema = Type.Object({ export type GetPositionResponseType = Static; +// Debt schemas +export const GetDebtQuerySchema = Type.Object({ + user_address: Type.String({ minLength: 1, description: "User's Cardano address (bech32)" }), + market_id: Type.String({ minLength: 1, description: "Market ID (e.g., ADA-MIN)" }), +}); + +export type GetDebtQueryType = Static; + +export const GetDebtResponseSchema = Type.Object({ + success: Type.Boolean(), + data: Type.Union([ + Type.Object({ + amount: Type.String({ description: "Debt amount in raw units (with decimals 1e6)" }), + asset: Type.String({ description: "Debt asset identifier" }), + }), + Type.Null(), + ]), + error: Type.Optional(Type.String()), +}); + +export type GetDebtResponseType = Static; + // Build TX schemas export const BuildTxDataSchema = Type.Object({ market_id: Type.String({ description: "Market identifier (e.g., ADA-MIN)" }), diff --git a/apps/long-short-backend/src/constants.ts b/apps/long-short-backend/src/constants.ts index 242c6a3..841c36f 100644 --- a/apps/long-short-backend/src/constants.ts +++ b/apps/long-short-backend/src/constants.ts @@ -6,5 +6,10 @@ export const API_ENDPOINTS = { POSITION_BUILD_TX: "/position/build-tx", POSITION_CLOSE: "/position/close", POSITION_CREATE: "/position/create", + POSITION_DEBT: "/position/debt", POSITION_GET: "/position/get", }; + +export const ERROR_BUILD_TX_CODE = { + ERROR_CANNOT_REPAY: "ERROR_CANNOT_REPAY", +} diff --git a/apps/long-short-backend/src/services/position-service.ts b/apps/long-short-backend/src/services/position-service.ts index 6e4b495..510b020 100644 --- a/apps/long-short-backend/src/services/position-service.ts +++ b/apps/long-short-backend/src/services/position-service.ts @@ -487,6 +487,41 @@ export class PositionService { return PositionRepository.getOpenPositionByUser(this.db, userAddress); } + async getDebt( + userAddress: string, + marketId: string, + ): Promise<{ amount: string; asset: string } | null> { + const position = await PositionRepository.getOpenPositionByUserAndMarket(this.db, userAddress, marketId); + if (!position) return null; + + const marketConfig = getMarketConfig(marketId); + if (!marketConfig) return null; + + const borrowMarketId = + position.side === StateMachine.PositionSide.LONG + ? marketConfig.borrowMarketIdLong + : marketConfig.borrowMarketIdShort; + + const debtAsset = + position.side === StateMachine.PositionSide.LONG + ? marketConfig.assetA.toString() + : marketConfig.assetB.toString(); + + const apiConfig = LiqwidProviderV2.createConfig(this.networkEnv); + const userAddr = Address.fromBech32(userAddress); + const pkh = userAddr.toPubKeyHash()?.keyHash.hex; + if (!pkh) return null; + + const loansResult = await LiqwidProviderV2.Data.loansForUser(apiConfig, [pkh]); + if (loansResult.type === "err") return null; + + const loan = loansResult.value.find((l) => l.marketId === borrowMarketId); + if (!loan) return null; + + const amountRaw = BigInt(Math.round(loan.amount * 1_000_000)); + return { amount: amountRaw.toString(), asset: debtAsset }; + } + /** * Compute trading metrics for an OPEN position: * entry_price, liq_price, interest, unrealized_pnl, health From 489ca66ba09ae92aaf2f60d96fe627b7a9366dcb Mon Sep 17 00:00:00 2001 From: tony Date: Tue, 24 Mar 2026 19:40:49 +0700 Subject: [PATCH 09/10] handle short --- apps/long-short-backend/src/api/server.ts | 7 +- .../src/api/state-machine.ts | 113 ++++++++++++++++-- apps/long-short-backend/src/cmd/run-api.ts | 11 +- apps/long-short-backend/src/constants.ts | 8 +- .../src/repository/order-repository.ts | 31 +++++ .../src/services/position-service.ts | 35 +++++- docker-compose.yml | 1 + 7 files changed, 188 insertions(+), 18 deletions(-) diff --git a/apps/long-short-backend/src/api/server.ts b/apps/long-short-backend/src/api/server.ts index 598ccf3..c3eaa70 100644 --- a/apps/long-short-backend/src/api/server.ts +++ b/apps/long-short-backend/src/api/server.ts @@ -6,7 +6,7 @@ import Fastify, { type FastifyError, type FastifyInstance } from "fastify"; import type { Kysely } from "kysely"; import { API_ENDPOINTS } from "../constants"; import type { DB } from "../database"; -import { type CardanoscanProvider, MinswapAggregatorProvider } from "../provider"; +import { type CardanoscanProvider, type KupoService, MinswapAggregatorProvider } from "../provider"; import { PositionService } from "../services/position-service"; import { logger } from "../utils"; import { registerLiqwidRoutes } from "./routes/liqwid"; @@ -18,11 +18,12 @@ export type ApiServerOptions = { host: string; db: Kysely; cardanoscanProvider: CardanoscanProvider; + kupoService: KupoService; networkEnv: NetworkEnvironment; }; export async function createApiServer(options: ApiServerOptions): Promise { - const { port, host, db, networkEnv, cardanoscanProvider } = options; + const { port, host, db, networkEnv, cardanoscanProvider, kupoService } = options; const fastify = Fastify({ logger: { @@ -85,7 +86,7 @@ export async function createApiServer(options: ApiServerOptions): Promise => { @@ -999,7 +1016,12 @@ export namespace StateMachine { userAddress: string, marketConfig: MarketConfig, availableAssetB: number, - ): Promise<{ canFullRepay: boolean; loan: LiqwidProviderV2.Loan; loanAmountRaw: bigint }> => { + ): Promise<{ + canFullRepay: boolean; + canRepay: boolean; + loan: LiqwidProviderV2.Loan; + loanAmountRaw: bigint; + }> => { const apiConfig = LiqwidProviderV2.createConfig(networkEnv); const userAddr = Address.fromBech32(userAddress); const pkh = userAddr.toPubKeyHash()?.keyHash.hex; @@ -1011,7 +1033,28 @@ export namespace StateMachine { const loan = loansResult.value.find((l) => l.marketId === marketConfig.borrowMarketIdShort); invariant(loan, `No active loan found for market ${marketConfig.borrowMarketIdShort}`); const loanAmountRaw = BigInt(Math.round(loan.amount * 1_000_000)); - return { canFullRepay: BigInt(availableAssetB) >= loanAmountRaw, loan, loanAmountRaw }; + const canFullRepay = BigInt(availableAssetB) >= loanAmountRaw; + + // Fetch Liqwid market minValue to check if repay is possible + // Liqwid requires: repay >= minValue AND remaining debt >= minValue (or full repay) + // If debt <= minValue * REPAY_MIN_VALUE_THRESHOLD_NUM / DEFAULT_DENOMINATOR, neither full nor fractional repay is possible without more funds + const marketsResult = await LiqwidProviderV2.Data.markets(apiConfig, { + ids: [marketConfig.borrowMarketIdShort], + }); + let canRepay = true; + if (marketsResult.type === "ok" && marketsResult.value.results.length > 0) { + const liqwidMarket = marketsResult.value.results[0]; + const minValueRaw = BigInt(Math.round(liqwidMarket.parameters.minValue * 1_000_000)); + // Threshold: debt must be > minValue * multiplier for fractional repay to work + // (repay portion >= minValue AND leftover >= minValue) + const minThreshold = (minValueRaw * REPAY_MIN_VALUE_THRESHOLD_NUM) / DEFAULT_DENOMINATOR; + + if (!canFullRepay && loanAmountRaw <= minThreshold) { + canRepay = false; + } + } + + return { canFullRepay, canRepay, loan, loanAmountRaw }; }; /** @@ -1019,7 +1062,7 @@ export namespace StateMachine { * Fetches loan from Liqwid API to get current UTXO and collateral info. */ export const handleShortRepay = async (options: HandleBuildTxOptions): Promise => { - const { order, marketConfig, userAddress, networkEnv, utxos } = options; + const { order, marketConfig, userAddress, networkEnv, utxos, kupoService } = options; invariant(order.orderType === ShortOrderType.SHORT_REPAY, "Invalid order type for handleShortRepay"); invariant(order.assetIn, "assetIn is required for SHORT_REPAY order"); invariant(order.amountIn, "amountIn is required for SHORT_REPAY order"); @@ -1037,6 +1080,14 @@ export namespace StateMachine { const loan = loansResult.value.find((l) => l.marketId === marketConfig.borrowMarketIdShort); invariant(loan, `No active loan found for market ${marketConfig.borrowMarketIdShort}`); + // Check if the user has enough asset B in wallet to cover the full debt + const loanAmountRaw = BigInt(Math.round(loan.amount * 1_000_000)); + const walletBalance = await kupoService.getBalanceOfPubKeyAddress(userAddress); + const availableRaw = walletBalance.get(marketConfig.assetB); + if (availableRaw < loanAmountRaw) { + throw new Error(ERROR_BUILD_TX_CODE.ERROR_CANNOT_REPAY); + } + const loanUtxoId = `${loan.transactionId}-${loan.transactionIndex}`; const collateral = loan.collaterals[0]; @@ -1120,7 +1171,7 @@ export namespace StateMachine { * Step 2 (SHORT_WITHDRAW_FRACTION) reduces collateral proportionally in a separate tx. */ export const handleShortRepayFraction = async (options: HandleBuildTxOptions): Promise => { - const { order, marketConfig, userAddress, networkEnv, utxos } = options; + const { order, marketConfig, userAddress, networkEnv, utxos, kupoService } = options; invariant( order.orderType === ShortOrderType.SHORT_REPAY_FRACTION, "Invalid order type for handleShortRepayFraction", @@ -1157,6 +1208,35 @@ export namespace StateMachine { // loan.amount is human-readable; convert to raw units const currentDebtRaw = Math.round(loan.amount * 1_000_000); const availableRaw = Number(order.amountIn); // raw units of asset B + + // Query wallet balance via Kupo — if wallet has enough asset B, build full repay tx instead + const walletBalance = await kupoService.getBalanceOfPubKeyAddress(userAddress); + const walletAssetB = walletBalance.get(marketConfig.assetB); + if (walletAssetB >= BigInt(currentDebtRaw)) { + logger.info("handleShortRepayFraction: wallet has enough for full repay, redirecting", { + walletAssetB: walletAssetB.toString(), + currentDebtRaw, + }); + const fullRepayResult = await handleShortRepay({ + ...options, + order: { ...order, orderType: ShortOrderType.SHORT_REPAY, amountIn: walletAssetB.toString() }, + }); + return { + ...fullRepayResult, + redirectToFullRepay: { + targetOrderType: ShortOrderType.SHORT_REPAY, + amountIn: walletAssetB.toString(), + assetIn: marketConfig.assetB.toString(), + assetOut: marketConfig.assetAQTokenRaw, + deleteOrderTypes: [ + ShortOrderType.SHORT_REPAY_FRACTION, + ShortOrderType.SHORT_WITHDRAW_FRACTION, + ShortOrderType.SHORT_BUY_FREED, + ], + }, + }; + } + const collateral = loan.collaterals[0]; invariant(collateral, "Loan has no collateral — cannot compute partial repay"); const qTokenAmountRaw = Math.round(collateral.qTokenAmount * 1_000_000); @@ -1164,6 +1244,13 @@ export namespace StateMachine { // Remaining debt must stay >= minValue so the position stays protocol-legal. const newDebt = Math.max(currentDebtRaw - availableRaw, minValueRaw); + // Check: both the repay amount AND remaining debt must be >= minValue. + // If debt <= minValue * REPAY_MIN_VALUE_THRESHOLD_NUM / DEFAULT_DENOMINATOR, neither full nor fractional repay is possible. + const repayAmount = currentDebtRaw - newDebt; + if (repayAmount < minValueRaw || (availableRaw < currentDebtRaw && newDebt < minValueRaw)) { + throw new Error(ERROR_BUILD_TX_CODE.ERROR_CANNOT_REPAY); + } + invariant(collateral.market, "Loan collateral has no market info"); const collateralId = `${collateral.market.id}.${collateral.id}`; @@ -1393,19 +1480,24 @@ export namespace StateMachine { const amountOut = BigInt(matchingToken.value); // Check if we now have enough asset B to fully repay the remaining loan. - const { canFullRepay, loanAmountRaw } = await checkShortRepayFunds( + const { canFullRepay, canRepay, loanAmountRaw } = await checkShortRepayFunds( networkEnv, userAddress.bech32, marketConfig, Number(amountOut), ); - logger.info("waitingShortBuyFreed: canFullRepay check", { + logger.info("waitingShortBuyFreed: repay check", { available: amountOut.toString(), loanAmountRaw: loanAmountRaw.toString(), canFullRepay, + canRepay, }); + if (!canRepay) { + throw new Error(ERROR_BUILD_TX_CODE.ERROR_CANNOT_REPAY); + } + if (canFullRepay) { // Enough asset B → transition to SHORT_REPAY (full repay) return { @@ -2056,19 +2148,24 @@ export namespace StateMachine { const amountOut = BigInt(matchingToken.value); // Check if we have enough asset B to fully repay the loan - const { canFullRepay, loanAmountRaw } = await checkShortRepayFunds( + const { canFullRepay, canRepay, loanAmountRaw } = await checkShortRepayFunds( networkEnv, userAddress.bech32, marketConfig, Number(amountOut), ); - logger.info("waitingShortBuy: canFullRepay check", { + logger.info("waitingShortBuy: repay check", { available: amountOut.toString(), loanAmountRaw: loanAmountRaw.toString(), canFullRepay, + canRepay, }); + if (!canRepay) { + throw new Error(ERROR_BUILD_TX_CODE.ERROR_CANNOT_REPAY); + } + if (canFullRepay) { // Happy path: enough asset B → full repay return { diff --git a/apps/long-short-backend/src/cmd/run-api.ts b/apps/long-short-backend/src/cmd/run-api.ts index 6d290cb..b3d276f 100644 --- a/apps/long-short-backend/src/cmd/run-api.ts +++ b/apps/long-short-backend/src/cmd/run-api.ts @@ -4,7 +4,7 @@ import { createApiServer } from "../api/server"; import { loadMarketConfigs } from "../config/market"; import type { DB } from "../database"; import { newKyselyClient } from "../database/postgres"; -import { CardanoscanProvider } from "../provider"; +import { CardanoscanProvider, KupoService } from "../provider"; import { logger } from "../utils"; const API_PORT = Number(process.env.API_PORT) || 9999; @@ -12,6 +12,7 @@ const API_HOST = process.env.API_HOST || "0.0.0.0"; const DATABASE_URL = process.env.DATABASE_URL; const NETWORK = process.env.NETWORK || "mainnet"; const CARDANOSCAN_API_KEY = process.env.CARDANOSCAN_API_KEY; +const KUPO_HOST = process.env.KUPO_HOST; async function main() { // Validate environment @@ -21,6 +22,9 @@ async function main() { if (!CARDANOSCAN_API_KEY) { throw new Error("CARDANOSCAN_API_KEY environment variable is required"); } + if (!KUPO_HOST) { + throw new Error("KUPO_HOST environment variable is required"); + } // Parse network environment const networkEnv = NETWORK === "mainnet" ? NetworkEnvironment.MAINNET : NetworkEnvironment.TESTNET_PREVIEW; @@ -35,6 +39,10 @@ async function main() { const db = await newKyselyClient(DATABASE_URL, { logSQL: false }); logger.info("Database connected"); + logger.info("Connecting to Kupo service..."); + const kupoService = await KupoService.new(KUPO_HOST, true); + logger.info("Kupo service connected"); + // Load market configs from database logger.info("Loading market configs..."); const marketConfigs = await loadMarketConfigs(db); @@ -53,6 +61,7 @@ async function main() { db, networkEnv, cardanoscanProvider, + kupoService, }); logger.info("Long-Short Backend started successfully", { diff --git a/apps/long-short-backend/src/constants.ts b/apps/long-short-backend/src/constants.ts index 841c36f..514aa43 100644 --- a/apps/long-short-backend/src/constants.ts +++ b/apps/long-short-backend/src/constants.ts @@ -12,4 +12,10 @@ export const API_ENDPOINTS = { export const ERROR_BUILD_TX_CODE = { ERROR_CANNOT_REPAY: "ERROR_CANNOT_REPAY", -} +}; + +export const DEFAULT_DENOMINATOR = 10000n; + +/** Numerator for Liqwid minValue threshold (over DEFAULT_DENOMINATOR). + * Debt must be > minValue * REPAY_MIN_VALUE_THRESHOLD_NUM / DEFAULT_DENOMINATOR for fractional repay to work. */ +export const REPAY_MIN_VALUE_THRESHOLD_NUM = 21000n; diff --git a/apps/long-short-backend/src/repository/order-repository.ts b/apps/long-short-backend/src/repository/order-repository.ts index 3e9545d..ff00372 100644 --- a/apps/long-short-backend/src/repository/order-repository.ts +++ b/apps/long-short-backend/src/repository/order-repository.ts @@ -292,6 +292,37 @@ export namespace OrderRepository { }; } + export async function updateOrderForRedirect( + db: Kysely | Transaction, + orderId: bigint, + params: { assetIn: string; amountIn: string; assetOut: string; builtTxId: string; builtValidTo: Date }, + ): Promise { + await db + .updateTable("order") + .set({ + asset_in: params.assetIn, + amount_in: params.amountIn, + asset_out: params.assetOut, + built_tx_id: params.builtTxId, + built_valid_to: params.builtValidTo, + }) + .where("id", "=", orderId.toString()) + .execute(); + } + + export async function deleteOrdersByTypes( + db: Kysely | Transaction, + positionId: bigint, + orderTypes: string[], + ): Promise { + if (orderTypes.length === 0) return; + await db + .deleteFrom("order") + .where("position_id", "=", positionId.toString()) + .where("order_type", "in", orderTypes) + .execute(); + } + // biome-ignore lint/suspicious/noExplicitAny: DB row type function mapOrderRow(row: any): Order { return { diff --git a/apps/long-short-backend/src/services/position-service.ts b/apps/long-short-backend/src/services/position-service.ts index 510b020..3e2fd64 100644 --- a/apps/long-short-backend/src/services/position-service.ts +++ b/apps/long-short-backend/src/services/position-service.ts @@ -5,7 +5,7 @@ import type { Kysely } from "kysely"; import { StateMachine } from "../api/state-machine"; import { getMarketConfig, isSupportedMarket, type MarketConfig } from "../config/market"; import type { DB } from "../database"; -import { CardanoscanProvider, type MinswapAggregatorProvider } from "../provider"; +import { CardanoscanProvider, type KupoService, type MinswapAggregatorProvider } from "../provider"; import { OrderRepository } from "../repository/order-repository"; import { type Position, PositionRepository } from "../repository/position-repository"; import { logger } from "../utils"; @@ -51,6 +51,7 @@ export class PositionService { private readonly networkEnv: NetworkEnvironment, private readonly cardanoscanProvider: CardanoscanProvider, private readonly aggregatorProvider: MinswapAggregatorProvider, + private readonly kupoService: KupoService, ) {} async createPosition(input: CreatePositionInput): Promise { @@ -454,6 +455,7 @@ export class PositionService { networkEnv: this.networkEnv, utxos, amountBorrow: position.amountBorrow, + kupoService: this.kupoService, }; // LONG_REPAY, LONG_REPAY_FRACTION, SHORT_REPAY, SHORT_REPAY_FRACTION: no extra options needed — @@ -464,6 +466,32 @@ export class PositionService { const txResult = await buildFn(buildOptions); + // Handle redirect from fractional to full repay + if (txResult.redirectToFullRepay) { + const { targetOrderType, amountIn, assetIn, assetOut, deleteOrderTypes } = txResult.redirectToFullRepay; + await this.db.transaction().execute(async (trx) => { + await OrderRepository.deleteOrdersByTypes(trx, position.id, deleteOrderTypes); + const targetOrder = await OrderRepository.getOrderByPositionAndType(trx, position.id, targetOrderType); + invariant(targetOrder, "Target order for redirect not found"); + await OrderRepository.updateOrderForRedirect(trx, targetOrder.id, { + assetIn, + amountIn, + assetOut, + builtTxId: txResult.txId, + builtValidTo: new Date(txResult.validTo), + }); + logger.info("Redirected fractional to full repay", { + targetOrderType, + deletedTypes: deleteOrderTypes, + targetOrderId: targetOrder.id, + txId: txResult.txId, + validTo: new Date(txResult.validTo).toISOString(), + }); + }); + + return { success: true, txRaw: txResult.txRaw, txId: txResult.txId, orderType: targetOrderType }; + } + // Update order built_tx fields await OrderRepository.updateOrderBuiltTx(this.db, order.id, txResult.txId, new Date(txResult.validTo)); @@ -487,10 +515,7 @@ export class PositionService { return PositionRepository.getOpenPositionByUser(this.db, userAddress); } - async getDebt( - userAddress: string, - marketId: string, - ): Promise<{ amount: string; asset: string } | null> { + async getDebt(userAddress: string, marketId: string): Promise<{ amount: string; asset: string } | null> { const position = await PositionRepository.getOpenPositionByUserAndMarket(this.db, userAddress, marketId); if (!position) return null; diff --git a/docker-compose.yml b/docker-compose.yml index 99296e1..4c9bbc1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -14,6 +14,7 @@ x-base-backend: &base-backend API_HOST: 0.0.0.0 NETWORK: testnet-preview CARDANOSCAN_API_KEY: ${CARDANOSCAN_API_KEY} + KUPO_HOST: http://dev-3.tail2feb3.ts.net:1442 healthcheck: test: ["CMD-SHELL", "curl -f http://localhost:9999/health || exit 1"] interval: 10s From d24edf227c2af83bb0ee6b9c7f11ca0174f7cd6e Mon Sep 17 00:00:00 2001 From: tony Date: Tue, 24 Mar 2026 19:45:44 +0700 Subject: [PATCH 10/10] handle long --- .../src/api/state-machine.ts | 86 +++++++++++++++++-- 1 file changed, 77 insertions(+), 9 deletions(-) diff --git a/apps/long-short-backend/src/api/state-machine.ts b/apps/long-short-backend/src/api/state-machine.ts index 05bfdd0..a5bc093 100644 --- a/apps/long-short-backend/src/api/state-machine.ts +++ b/apps/long-short-backend/src/api/state-machine.ts @@ -286,7 +286,7 @@ export namespace StateMachine { userAddress: string, marketConfig: MarketConfig, availableADA: number, - ): Promise<{ canFullRepay: boolean; loan: LiqwidProviderV2.Loan; loanAmount: bigint }> => { + ): Promise<{ canFullRepay: boolean; canRepay: boolean; loan: LiqwidProviderV2.Loan; loanAmount: bigint }> => { const apiConfig = LiqwidProviderV2.createConfig(networkEnv); const userAddr = Address.fromBech32(userAddress); const pkh = userAddr.toPubKeyHash()?.keyHash.hex; @@ -297,8 +297,23 @@ export namespace StateMachine { } const loan = loansResult.value.find((l) => l.marketId === marketConfig.borrowMarketIdLong); invariant(loan, `No active loan found for market ${marketConfig.borrowMarketIdLong}`); - const loanAmount = BigInt(Math.round(loan.amount * 1_000_000)); // ADA → Lovelace (Math.round avoids BigInt non-integer rejection) - return { canFullRepay: BigInt(availableADA) >= loanAmount, loan, loanAmount }; + const loanAmount = BigInt(Math.round(loan.amount * 1_000_000)); + const canFullRepay = BigInt(availableADA) >= loanAmount; + + let canRepay = true; + const marketsResult = await LiqwidProviderV2.Data.markets(apiConfig, { + ids: [marketConfig.borrowMarketIdLong], + }); + if (marketsResult.type === "ok" && marketsResult.value.results.length > 0) { + const liqwidMarket = marketsResult.value.results[0]; + const minValueRaw = BigInt(Math.round(liqwidMarket.parameters.minValue * 1_000_000)); + const minThreshold = (minValueRaw * REPAY_MIN_VALUE_THRESHOLD_NUM) / DEFAULT_DENOMINATOR; + if (!canFullRepay && loanAmount <= minThreshold) { + canRepay = false; + } + } + + return { canFullRepay, canRepay, loan, loanAmount }; }; /** @@ -307,7 +322,7 @@ export namespace StateMachine { * Fetches loan from Liqwid API to get current UTXO and collateral info. */ export const handleLongRepay = async (options: HandleBuildTxOptions): Promise => { - const { order, marketConfig, userAddress, networkEnv, utxos } = options; + const { order, marketConfig, userAddress, networkEnv, utxos, kupoService } = options; invariant(order.orderType === LongOrderType.LONG_REPAY, "Invalid order type for handleLongRepay"); invariant(order.assetIn, "assetIn is required for LONG_REPAY order"); invariant(order.amountIn, "amountIn is required for LONG_REPAY order"); @@ -325,6 +340,14 @@ export namespace StateMachine { const loan = loansResult.value.find((l) => l.marketId === marketConfig.borrowMarketIdLong); invariant(loan, `No active loan found for market ${marketConfig.borrowMarketIdLong}`); + // Check if the user has enough ADA in wallet to cover the full debt + const loanAmountRaw = BigInt(Math.round(loan.amount * 1_000_000)); + const walletBalance = await kupoService.getBalanceOfPubKeyAddress(userAddress); + const availableAda = walletBalance.get(marketConfig.assetA); + if (availableAda < loanAmountRaw) { + throw new Error(ERROR_BUILD_TX_CODE.ERROR_CANNOT_REPAY); + } + const loanUtxoId = `${loan.transactionId}-${loan.transactionIndex}`; // Format collateral ID as "{MarketId}.{policyId}" @@ -424,7 +447,7 @@ export namespace StateMachine { * Step 2 (LONG_WITHDRAW_FRACTION) reduces collateral proportionally in a separate tx. */ export const handleLongRepayFraction = async (options: HandleBuildTxOptions): Promise => { - const { order, marketConfig, userAddress, networkEnv, utxos } = options; + const { order, marketConfig, userAddress, networkEnv, utxos, kupoService } = options; invariant(order.orderType === LongOrderType.LONG_REPAY_FRACTION, "Invalid order type for handleLongRepayFraction"); invariant(order.assetIn, "assetIn is required for LONG_REPAY_FRACTION order"); invariant(order.amountIn, "amountIn (ADA available to repay) is required for LONG_REPAY_FRACTION order"); @@ -458,6 +481,35 @@ export namespace StateMachine { // loan.amount is in ADA (float); convert to Lovelace to match order.amountIn and API expectations. const currentDebtLovelace = Math.round(loan.amount * 1_000_000); const availableLovelace = Number(order.amountIn); // already in Lovelace + + // Query wallet balance via Kupo — if wallet has enough ADA, build full repay tx instead + const walletBalance = await kupoService.getBalanceOfPubKeyAddress(userAddress); + const walletAda = walletBalance.get(marketConfig.assetA); + if (walletAda >= BigInt(currentDebtLovelace)) { + logger.info("handleLongRepayFraction: wallet has enough for full repay, redirecting", { + walletAda: walletAda.toString(), + currentDebtLovelace, + }); + const fullRepayResult = await handleLongRepay({ + ...options, + order: { ...order, orderType: LongOrderType.LONG_REPAY, amountIn: walletAda.toString() }, + }); + return { + ...fullRepayResult, + redirectToFullRepay: { + targetOrderType: LongOrderType.LONG_REPAY, + amountIn: walletAda.toString(), + assetIn: marketConfig.assetA.toString(), + assetOut: marketConfig.assetBQTokenRaw, + deleteOrderTypes: [ + LongOrderType.LONG_REPAY_FRACTION, + LongOrderType.LONG_WITHDRAW_FRACTION, + LongOrderType.LONG_SELL_FREED, + ], + }, + }; + } + const qTokenAmountFloat = loan.collaterals[0]?.qTokenAmount; invariant(qTokenAmountFloat !== undefined, "Loan has no collateral — cannot compute partial repay"); // qTokenAmount from API is human-readable (divided by 10^6), convert to raw on-chain amount @@ -466,6 +518,12 @@ export namespace StateMachine { // Remaining debt must stay >= minValue so the position stays protocol-legal. const newDebt = Math.max(currentDebtLovelace - availableLovelace, minValueLovelace); + // Check: both the repay amount AND remaining debt must be >= minValue. + const repayAmount = currentDebtLovelace - newDebt; + if (repayAmount < minValueLovelace || (availableLovelace < currentDebtLovelace && newDebt < minValueLovelace)) { + throw new Error(ERROR_BUILD_TX_CODE.ERROR_CANNOT_REPAY); + } + logger.info("handleLongRepayFraction", { currentDebtLovelace, availableLovelace, @@ -700,19 +758,24 @@ export namespace StateMachine { const amountOut = BigInt(output.value); // Check if we now have enough ADA to fully repay the remaining loan. - const { canFullRepay, loanAmount } = await checkLongRepayFunds( + const { canFullRepay, canRepay, loanAmount } = await checkLongRepayFunds( networkEnv, userAddress.bech32, marketConfig, Number(amountOut), ); - logger.info("waitingLongSellFreed: canFullRepay check", { + logger.info("waitingLongSellFreed: repay check", { available: amountOut.toString(), loanAmount: loanAmount.toString(), canFullRepay, + canRepay, }); + if (!canRepay) { + throw new Error(ERROR_BUILD_TX_CODE.ERROR_CANNOT_REPAY); + } + if (canFullRepay) { // Enough ADA → transition to existing LONG_REPAY (from closePosition) return { @@ -1841,19 +1904,24 @@ export namespace StateMachine { } // For LONG_SELL, check if we have enough ADA to fully repay the loan. - const { canFullRepay, loanAmount } = await checkLongRepayFunds( + const { canFullRepay, canRepay, loanAmount } = await checkLongRepayFunds( networkEnv, userAddress.bech32, marketConfig, Number(amountOut), ); - logger.info("waitingLongSell: canFullRepay check", { + logger.info("waitingLongSell: repay check", { available: amountOut.toString(), loanAmount: loanAmount.toString(), canFullRepay, + canRepay, }); + if (!canRepay) { + throw new Error(ERROR_BUILD_TX_CODE.ERROR_CANNOT_REPAY); + } + if (canFullRepay) { // Happy path: enough ADA → full repay return {