Как получить номер блока, ближайший к заданной метке времени?

Есть ли какой-либо метод (логия) в web3, который позволяет получить номер блока на заданную дату?

В частности, у меня есть две даты в качестве входных данных, и я хочу эффективно получать все события между этими двумя датами. Я знаю, что могу сделать это, имея номера блоков:

contract.Event({}, { fromBlock: 100000, toBlock: 121212 })

Но возможно ли это сделать с помощью Dates? Спасибо!

Ответы (6)

Ethfinex только что опубликовал функцию, которая делает именно это.

Это «пиксельное совершенство», но пока он работает очень хорошо, вы даете ему временную метку, и он будет проходить блокчейн в обратном направлении, пока не найдет блок, который очень близок.

Во время моих тестов он всегда находил ближайший блок, возможно, вам придется немного его настроить.

https://github.com/ethfinex/efx-trustless-vol/blob/master/src/lib/getBlockByTime.js

вот некоторые результаты

tgt timestamp   -> 1545523200
tgt date        -> 2018-12-23T00:00:00Z

block timestamp -> 1545523212
block date      -> 2018-12-23T00:00:12Z

requests made   -> 3

tgt timestamp   -> 1545609600
tgt date        -> 2018-12-24T00:00:00Z

block timestamp -> 1545609592
block date      -> 2018-12-23T23:59:52Z

requests made   -> 4
Спасибо, это самый близкий ответ на то, что я искал.

Это старый вопрос, но недавно я написал плагин для такого рода задач. Обычно он делает меньше запросов, чем скрипт Ethfinex (как правило, не всегда 😂).

Взгляните: https://github.com/monosux/ethereum-block-by-date

Отличная работа! Было бы еще лучше, если бы вы могли избежать использования moment.js в качестве зависимости (это действительно тяжелый модуль). Спасибо!
Спасибо :) Да, вы правы, момент тяжелый. Я использовал его, так как он уже был в зависимостях моего проекта. Я думаю, что могу переписать код, чтобы использовать момент как необязательную зависимость или вообще не использовать момент. Будет круто, если вы создадите issue в репо: github.com/monosux/ethereum-block-by-date/issues Так что эту идею я не забуду :)

QuickBlocks имеет программу командной строки под названием «whenBlock», которая принимает либо дату (или отметку времени) и номер блока, который произошел непосредственно перед этим, либо номер блока и возвращает дату и отметку времени этого блока. Вы можете скачать QuickBlocks здесь: http://github.com/Great-Hill-Corporation/quickBlocks .

У DeFiLlama есть новый API, который делает это. https://defillama.com/docs/api

Просто ПОЛУЧИТЕ

https://coins.llama.fi/block/ethereum/1658171864

Вы также можете проверить различные цепи

Не совсем.

Лучше всего взять цикл размера выборки от latestдо latest - {x}блока и получить скользящее среднее время создания блока. Оттуда вы можете оценить номер блока, чтобы получить время блока, и зацикливаться, пока не пересечете нужное время.

Я создал небольшую функцию для бинарного поиска с использованием ether.js.

export async function getBlockNumberByTimestamp(
    provider: JsonRpcProvider,
    timestamp: number
): Promise<number> {
    const getBlockTime = async (blockNumber: number) => (await provider.getBlock(blockNumber)).timestamp;
    
    let leftBlockNumber = 0;
    let leftTimestamp = await getBlockTime(leftBlockNumber);
    let rightBlockNumber = await provider.getBlockNumber();
    let rightTimestamp = await getBlockTime(rightBlockNumber);
    
    if (timestamp <= leftTimestamp) {
        return leftBlockNumber;
    }
    
    if (timestamp >= rightTimestamp) {
        return rightBlockNumber;
    }
    
    // eslint-disable-next-line no-constant-condition
    while (true) {
        const middleBlockNumber = leftBlockNumber + Math.floor((rightBlockNumber - leftBlockNumber) / 2);
        const middleTimestamp = await getBlockTime(rightBlockNumber);
        if (timestamp === middleTimestamp) {
            return middleBlockNumber;
        } else if (timestamp < middleTimestamp) {
            rightBlockNumber = middleBlockNumber;
            rightTimestamp = middleTimestamp;
        } else {
            leftBlockNumber = middleBlockNumber;
            leftTimestamp = middleTimestamp;
        }

        if (rightBlockNumber - leftBlockNumber <= 1) {
            return leftBlockNumber;
        }
    }
}

Еще не проверял.
Обновлю код, когда сделаю тест.