Реалізація інтерфейсу ліквідності (LP) на сайті

Наша компанія займається розробкою, підтримкою та обслуговуванням сайтів будь-якої складності. Від простих односторінкових сайтів до масштабних кластерних систем, побудованих на мікро сервісах. Досвід розробників підтверджено сертифікатами від вендорів.

Розробка та обслуговування будь-яких видів сайтів:

Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Реалізація інтерфейсу ліквідності (LP) на сайті
Складна
~5 робочих днів
Часті питання

Наші компетенції:

Етапи розробки

Останні роботи

  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    874
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851

Реалізація інтерфейсу ліквідності (LP) на веб-сайті

LP-інтерфейс дозволяє користувачам додавати і виводити ліквідність з пулу AMM. Це складніше за стейкинг: потрібно розуміти співвідношення токенів у пулі, розраховувати пропорції при додаванні, показувати долю в пулі та impermanent loss. Плюс — approve для двох токенів, а не одного.

Моделі AMM

Два основні варіанти, які зустрічаються в проектах:

Uniswap v2 / SushiSwap (constant product x·y=k): простої пропорція, ERC-20 LP-токени, фіксована 0.3% комісія.

Uniswap v3 (concentrated liquidity): користувач вибирає цінову діапазон, позиція — NFT, більш складний розрахунок.

Розбираємо Uniswap v2 як основу — більшість custom AMM будується на цій моделі.

Чтення стану пулу

// lib/pool.ts
import { createPublicClient, http, parseAbi } from 'viem';

const PAIR_ABI = parseAbi([
  'function getReserves() view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast)',
  'function totalSupply() view returns (uint256)',
  'function balanceOf(address) view returns (uint256)',
  'function token0() view returns (address)',
  'function token1() view returns (address)',
]);

export interface PoolState {
  reserve0: bigint;
  reserve1: bigint;
  totalSupply: bigint;
  userLPBalance: bigint;
  token0: `0x${string}`;
  token1: `0x${string}`;
  // Вичислювані
  userShare: number;       // доля користувача в пулі, 0–1
  userToken0: bigint;      // скільки token0 можна вивести
  userToken1: bigint;
}

export async function getPoolState(
  pairAddress: `0x${string}`,
  userAddress?: `0x${string}`,
): Promise<PoolState> {
  const client = createPublicClient({ transport: http() });

  const [reserves, supply, t0, t1, balance] = await client.multicall({
    contracts: [
      { address: pairAddress, abi: PAIR_ABI, functionName: 'getReserves' },
      { address: pairAddress, abi: PAIR_ABI, functionName: 'totalSupply' },
      { address: pairAddress, abi: PAIR_ABI, functionName: 'token0' },
      { address: pairAddress, abi: PAIR_ABI, functionName: 'token1' },
      ...(userAddress ? [{ address: pairAddress, abi: PAIR_ABI, functionName: 'balanceOf', args: [userAddress] }] : []),
    ],
  });

  const [r0, r1] = reserves.result as [bigint, bigint];
  const totalSupply = supply.result as bigint;
  const userLPBalance = balance?.result as bigint || 0n;

  const userShare = totalSupply > 0n ? Number(userLPBalance * 10000n / totalSupply) / 10000 : 0;
  const userToken0 = totalSupply > 0n ? r0 * userLPBalance / totalSupply : 0n;
  const userToken1 = totalSupply > 0n ? r1 * userLPBalance / totalSupply : 0n;

  return { reserve0: r0, reserve1: r1, totalSupply, userLPBalance, token0: t0.result as `0x${string}`, token1: t1.result as `0x${string}`, userShare, userToken0, userToken1 };
}

Розрахунок пропорцій при додаванні ліквідності

При додаванні ліквідності в непустий пул другий токен розраховується автоматично за поточною ціною пулу:

// Користувач вводить кількість token0 → розраховуємо token1
export function quoteToken1(
  amount0: bigint,
  reserve0: bigint,
  reserve1: bigint,
): bigint {
  if (reserve0 === 0n) return 0n; // порожний пул
  return (amount0 * reserve1) / reserve0;
}

// І навпаки
export function quoteToken0(amount1: bigint, reserve0: bigint, reserve1: bigint): bigint {
  if (reserve1 === 0n) return 0n;
  return (amount1 * reserve0) / reserve1;
}

// Розрахунок LP-токенів, які отримає користувач
export function calcLPOut(
  amount0: bigint,
  amount1: bigint,
  reserve0: bigint,
  reserve1: bigint,
  totalSupply: bigint,
): bigint {
  if (totalSupply === 0n) {
    // Перший постачальник ліквідності
    const MINIMUM_LIQUIDITY = 1000n;
    return sqrt(amount0 * amount1) - MINIMUM_LIQUIDITY;
  }
  const lp0 = (amount0 * totalSupply) / reserve0;
  const lp1 = (amount1 * totalSupply) / reserve1;
  return lp0 < lp1 ? lp0 : lp1;
}

Форма додавання ліквідності

export function AddLiquidityForm({ pool }: { pool: PoolState }) {
  const [amount0, setAmount0] = useState('');
  const [amount1, setAmount1] = useState('');
  const decimals0 = 18;
  const decimals1 = 6;  // USDC

  const handleAmount0Change = (val: string) => {
    setAmount0(val);
    if (!val || pool.reserve0 === 0n) return;
    const wei0 = parseUnits(val, decimals0);
    const wei1 = quoteToken1(wei0, pool.reserve0, pool.reserve1);
    setAmount1(formatUnits(wei1, decimals1));
  };

  return (
    <div>
      <input value={amount0} onChange={(e) => handleAmount0Change(e.target.value)} placeholder="Token 0" />
      <input value={amount1} onChange={(e) => setAmount1(e.target.value)} placeholder="Token 1" />
      <button>Додати ліквідність</button>
    </div>
  );
}

Ключові аспекти: дозвіл на токени, розрахунок impermanent loss, допуск на ковзання та відслідковування позицій.