Как получить всегда правильную цену на все токены BSC? Эфиры.js

Я кодирую свое небольшое приложение, и у меня возникают проблемы с получением цен на токены BSC. Проблема в:

  1. Я получаю странные значения для некоторых определенных пар (например, SHARD/WBNB, SAFEMARS/WBNB)
  2. Как я могу получить базовую и ближайшую пару, из которой я могу определить цены - что-то вроде «Маршрутов» под формой обмена на pancakeswap. Все ли монеты имеют пару к WBNB, которую всегда следует использовать?
  3. Как получить рыночную капитализацию для такой монеты, как SAFEMOON, например, где некоторые токены выкурены, поэтому использование totalSupply() * Цена, вероятно, недостаточна? Как здесь (charts.bogged.finance)
  4. Есть ли способ определить дату/время создания токена?
  5. Когда я вызываю getjrice() для заполнения всех этих переменных (имя, десятичные знаки и т. д.), все они доставляются отдельными запросами с https://bsc-dataseed1.binance.org ? Если да, то есть ли у вас какие-либо советы о том, как сделать это наиболее эффективно, чтобы сократить передачу и время?

Я уже нашел несколько способов поиграть с ethers.js, и вот часть моего кода.

import { ethers } from 'ethers'
const fs = require('fs');

let url = 'https://bsc-dataseed1.binance.org';
let provider = new ethers.providers.JsonRpcProvider(url);


const USDT = '0x55d398326f99059ff775485246999027b3197955'
const BUSD = '0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56';
const WBNB = '0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c';
const UST = '0x23396cf899ca06c4472205fc903bdb4de249d6fc';
const DAI = '0x1af3f329e8be154074d8769d1ffa4ee058b1dbc3';
const SHARD = '0xD8a1734945b9Ba38eB19a291b475E31F49e59877';
const SAFEMARS = '0x3aD9594151886Ce8538C1ff615EFa2385a8C3A88';
let SAFEMOON = '0x8076C74C5e3F5852037F31Ff0093Eeb8c8ADd8D3';
let PHANTOM = '0xcECdC98AA5Ef7f687C914a3aAE00cCe17DdeaFa3';

// LP V2
const pancakeFactoryAddress = "0xcA143Ce32Fe78f1f7019d7d551a6402fC5350c73"

// JSON files from here: https://github.com/risingsun007/pancakeswap_get_price
const pancakeFactoryJson = "pancake_factory.json";
const pancakeFactory = JSON.parse(fs.readFileSync(pancakeFactoryJson));
const pancakePairJson = "pancakepair.json";
const pancakePair = JSON.parse(fs.readFileSync(pancakePairJson));

const pancake = new ethers.Contract(pancakeFactoryAddress, pancakeFactory, provider);


export const getPrice = async (token0: string, token1: string) => {
  const pairAddress = await pancake.getPair(token0, token1);

  if(pairAddress === '0x0000000000000000000000000000000000000000'){
    return {
      status: 'Pair not found'
    }
  }
  
  
  const tokenContract0 = new ethers.Contract(token0, pancakePair, provider),
        tokenContract1 = new ethers.Contract(token1, pancakePair, provider),
        tokenDecimals0 = tokenContract0.decimals(),
        tokenDecimals1 = tokenContract1.decimals(),
        pairContract = new ethers.Contract(pairAddress, pancakePair, provider),
        reserves = await pairContract.getReserves(),
        totalSupply = await pairContract.totalSupply()

  let r0, r1;
  r0 = reserves._reserve0;
  r1 = reserves._reserve1;

  return {
    tokens: [await tokenContract0.name(), await tokenContract1.name()],
    decimals: [await tokenDecimals0, await tokenDecimals1],
    pairAddress: pairAddress,
    totalSupply: totalSupply.toString(),
    reserves: [
      r0.toString(), 
      r1.toString()
    ],
    price: (r1 / 10 ** await tokenDecimals1) / (r0 / 10 ** await tokenDecimals0)
  }
}


getPrice(WBNB, BUSD).then((result) => {console.log(result)})
// RESULT OK - price: 354.66429096612507
// exchange.pancakeswap shows: 354.431 BUSD per WBNB


getPrice(PHANTOM, BUSD).then((result) => {console.log(result)})
// RESULT OK - price: 0.6465541804048065
// exchange.pancakeswap shows: 0.644938 BUSD per Phantom


getPrice(WBNB, UST).then((result) => {console.log(result)})
// WRONG RESULT - price: 0.0028052627557905675
// if I change the price formula for: reserve0 / reserve1 the result is ok (for this pair)


getPrice(SHARD, WBNB).then((result) => {console.log(result)})
// WRONG RESULT - price: 1215.2873592032508
// exchange.pancakeswap shows:  0.000000177215 WBNB per SHARD


getPrice(SAFEMOON, WBNB).then((result) => {console.log(result)})
// WRONG RESULT - price: 1.1423152711429623e-8
// exchange.pancakeswap shows: 0.0000000112533 WBNB per SAFEMOON


getPrice(SAFEMARS, WBNB).then((result) => {console.log(result)})
// WRONG RESULT - price: 5.565101362273654e-10
// exchange.pancakeswap shows: 0.000000195489 BUSD per SAFEMARS
Этот вопрос был бы лучше как несколько отдельных вопросов. Один вопрос на пост SE работает лучше всего.

Ответы (2)

Это СЕРЬЕЗНО болезненный путь. Контракт маршрутизатора имеет функции, называемые getAmountsOutи getAmountsIn, используйте их. Просто введите путь к токену и введенную или выведенную сумму, и он вернет сумму, которую вы получили бы, если бы эта передача произошла. Он учитывает само влияние на цену, вам не нужно об этом беспокоиться.

Я смотрю на код контракта с фабрикой блинов и не вижу ни одного из этих событий. Не могли бы вы уточнить в своем ответе свой метод получения цен на блины с помощью API ether.js, пожалуйста?

результирующие token0 и token1 упорядочены в соответствии с порядком сортировки ASC парных токенов.

для WBNB, BUSD: адрес контракта WBNB «меньше», чем BUSD, поэтому вы получаете правильную сумму.

для WBNB, UST: 0x23.. меньше, чем 0xbb, поэтому порядок вывода должен быть обратным.

token0 должен быть строго меньше token1 по порядку сортировки.

let WHITELIST: string[] = [ "0x0e09fabb73bd3ade0a17ecc321fd13a19e81ce82", // CAKE "0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c", // WBNB "0xe9e7cea3dedca5984780bafc599bd69add087d56", // BUSD "0x55d398326f99059ff775485246999027b3197955", // USDT "0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d", // USDC "0x23396cf899ca06c4472205fc903bdb4de249d6fc", // UST "0x7130d2a12b9bcbfae4f2634d864a1ee1ce3ead9c", // BTCB "0x2170ed0880ac9a755fd29b2688956bd959f933f8", // WETH]; @levent, можете ли вы предложить мне правильный порядок здесь? Какова логика правильного порядка. столкнулся с той же проблемой.
pairReserves = await contract.getReserves(); if (token1.toLowerCase() > token2.toLowerCase()) { pairIndex=1; } else { pairIndex=0; } return pairReserves[pairIndex];Вы можете найти правильный порядок, сравнив их, как в этом коде, выглядит глупо, но работает.