Оракулы цен на TON

Подключишь pull-оракул Pyth на TON: чтение цены on-chain и обновление фида через Hermes перед использованием в контракте.

Оракулы на TON

Оракул цен — источник внешней цены (BTC/USD, GRAM/USD и т.д.) для смарт-контрактов. На TON доминирует модель pull: данные не пишутся в сеть непрерывно. Ты (или твой контракт) сам подтягиваешь свежий подписанный пакет и обновляешь on-chain фид в той же транзакции, где цена нужна.

Два основных провайдера: Pyth и RedStone. Ниже — рабочий цикл Pyth.


Pyth: прочитать и обновить цену

Mainnet-контракт Pyth: EQA5NPyjfZztDm8jcTBwTAU9NGsgJEkw19z61yecX0TlseSB
(в SDK — PYTH_CONTRACT_ADDRESS_MAINNET)

typescript
// @pythnetwork/pyth-ton-js@0.4.0
// @pythnetwork/hermes-client@3.1.0
// @ton/ton@16.3.0
import { TonClient, Address, WalletContractV4 } from "@ton/ton";
import { mnemonicToPrivateKey } from "@ton/crypto";
import { HermesClient } from "@pythnetwork/hermes-client";
import {
  PythContract,
  PYTH_CONTRACT_ADDRESS_MAINNET,
  calculateUpdatePriceFeedsFee,
} from "@pythnetwork/pyth-ton-js";

// BTC/USD price feed id (общий для всех сетей Pyth)
const BTC_USD =
  "0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43";

async function readAndUpdateBtcPrice(mnemonic: string[]) {
  const client = new TonClient({
    endpoint: "https://toncenter.com/api/v2/jsonRPC",
  });

  const contract = client.open(
    PythContract.createFromAddress(
      Address.parse(PYTH_CONTRACT_ADDRESS_MAINNET),
    ),
  );

  // 1. Текущая on-chain цена (может быть устаревшей)
  const onChain = await contract.getPriceUnsafe(BTC_USD);
  console.log("on-chain price:", onChain.price, "expo:", onChain.expo);
  console.log("publishTime:", onChain.publishTime);

  // 2. Свежий пакет с Hermes (off-chain)
  const hermes = new HermesClient("https://hermes.pyth.network");
  const updates = await hermes.getLatestPriceUpdates([BTC_USD], {
    encoding: "hex",
  });
  const updateData = Buffer.from(updates.binary.data[0], "hex");

  // 3. Комиссия за update + gas-обёртка SDK
  const updateFee = await contract.getUpdateFee(updateData);
  const totalFee =
    calculateUpdatePriceFeedsFee(BigInt(updateFee)) + BigInt(updateFee);

  // 4. Запись фида on-chain (меняет state, нужна подпись)
  const key = await mnemonicToPrivateKey(mnemonic);
  const wallet = client.open(
    WalletContractV4.create({ publicKey: key.publicKey, workchain: 0 }),
  );

  await contract.sendUpdatePriceFeeds(
    wallet.sender(key.secretKey),
    updateData,
    totalFee,
  );

  const fresh = await contract.getPriceUnsafe(BTC_USD);
  // Цена = price * 10^expo  (expo обычно отрицательный)
  const human = Number(fresh.price) * 10 ** fresh.expo;
  console.log("BTC/USD ≈", human);
}

В продакшен-контракте паттерн тот же: пользователь (или keeper) сначала вызывает updatePriceFeeds, затем — твою бизнес-логику, которая читает фид. Не полагайся на getPriceUnsafe без проверки publishTime.

Как это работает

Pull vs push — push-оракул сам пишет цены по таймеру; pull отдаёт подписанные данные off-chain (Hermes), а on-chain попадает только то, за что кто-то заплатил газ.

getPriceUnsafe(feedId) — читает последнее сохранённое значение. «Unsafe» в имени: ты сам обязан проверить свежесть (publishTime) и уверенность (conf).

HermesClient.getLatestPriceUpdates — забирает VAAs/пакеты с зеркала Pyth. Без этого шага on-chain фид не обновится.

getUpdateFee + calculateUpdatePriceFeedsFee — сколько Gram приложить к sendUpdatePriceFeeds. Недоплата → update не пройдёт.

price * 10^expo — каноническое представление Pyth. Не дели на 1e8 «как у Chainlink», пока не сверишь expo конкретного фида.

RedStone — альтернатива

RedStone тоже pull: пакет подписей приносится в транзакции. SDK: @redstone-finance/ton-connector + @redstone-finance/sdk. Типичный поток — getPricesFromPayload (без записи в storage) или writePricesFromPayloadToContract (персистентно). Выбор Pyth vs RedStone — по доступности фидов и модели комиссий, не по «безопасности бренда».

Частые ошибки

Используешь цену без проверки publishTime → ликвидация / своп по вчерашнему курсу. Задай max age (например 60 с) и отклоняй устаревший фид в контракте.

Обновляешь фид в одной tx, читаешь в другой без гарантии порядка → между ними цена снова устареет. В одном пользовательском флоу: update → сразу бизнес-вызов.

Путаешь testnet и mainnet адреса Pyth → testnet: EQBeydTZBuv4nkqN8jkScqZbWcyx7TsmOtxsEDbFann0IRtD. Для продакшена — только PYTH_CONTRACT_ADDRESS_MAINNET.


Что дальше

Материалы gramdocs.tech носят образовательный характер и не являются финансовой, юридической или инвестиционной рекомендацией. Работа с блокчейном TON и токеном Gram связана с рисками потери средств. Правовая информация