Интеграция с Moralis API
Moralis — это managed Web3 data API: NFT данные, балансы токенов, история транзакций, события контрактов. Основная ценность не в том, что «можно подключить за 10 минут», а в том, что Moralis индексирует исторические данные — запросить Transfer события за весь срок жизни контракта через их API дешевле, чем самостоятельно парсить блоки через getLogs.
Инициализация и аутентификация
import Moralis from 'moralis';
await Moralis.start({ apiKey: process.env.MORALIS_API_KEY });
API ключ должен быть на бэкенде — не в браузере. Free tier: 40,000 compute units/день. Большинство запросов стоят 2-25 CU.
Ключевые endpoints
NFT данные коллекции
import { EvmChain } from '@moralisweb3/common-evm-utils';
// NFT в кошельке пользователя
const nfts = await Moralis.EvmApi.nft.getWalletNFTs({
address: userAddress,
chain: EvmChain.ETHEREUM,
tokenAddresses: [COLLECTION_ADDRESS], // опционально — фильтр по коллекции
});
// Floor price и статистика коллекции
const stats = await Moralis.EvmApi.nft.getNFTCollectionStats({
address: COLLECTION_ADDRESS,
chain: EvmChain.ETHEREUM,
});
console.log(stats.result.floorPriceUsd);
console.log(stats.result.totalTrades);
Балансы токенов
// ERC-20 балансы
const tokenBalances = await Moralis.EvmApi.token.getWalletTokenBalances({
address: userAddress,
chain: EvmChain.ETHEREUM,
});
tokenBalances.result.forEach(token => {
console.log(`${token.symbol}: ${token.displayValue}`); // уже нормализовано по decimals
});
// Нативный баланс (ETH/BNB/etc)
const nativeBalance = await Moralis.EvmApi.balance.getNativeBalance({
address: userAddress,
chain: EvmChain.ETHEREUM,
});
История транзакций
const transfers = await Moralis.EvmApi.token.getWalletTokenTransfers({
address: userAddress,
chain: EvmChain.ETHEREUM,
contractAddresses: [TOKEN_ADDRESS],
limit: 100,
});
// Пагинация через cursor
if (transfers.hasNext()) {
const nextPage = await transfers.next();
}
Данные о токене и цена
const tokenPrice = await Moralis.EvmApi.token.getTokenPrice({
address: TOKEN_ADDRESS,
chain: EvmChain.ETHEREUM,
exchange: 'uniswapv3', // конкретная DEX или auto
});
console.log(tokenPrice.result.usdPrice);
console.log(tokenPrice.result.nativePrice?.value); // цена в ETH
Streams API (real-time события)
Moralis Streams — альтернатива самостоятельной WebSocket подписке. Moralis слушает блокчейн, вы получаете webhook:
const stream = await Moralis.Streams.add({
chains: [EvmChain.ETHEREUM],
description: 'Track transfers',
tag: 'token-transfers',
webhookUrl: 'https://yourserver.com/webhooks/moralis',
includeContractLogs: true,
abi: ERC20_ABI,
topic0: ['Transfer(address,address,uint256)'],
filter: { eq: ['address', TOKEN_ADDRESS] },
advancedOptions: [{ topic0: 'Transfer(address,address,uint256)', filter: { gte: ['value', '1000000000000000000'] } }],
});
await Moralis.Streams.addAddress({
id: stream.result.id,
address: [TOKEN_ADDRESS],
});
На вашем webhook endpoint:
app.post('/webhooks/moralis', async (req, reply) => {
const body = req.body;
// Верификация подписи
const providedSignature = req.headers['x-signature'];
const generatedSignature = crypto
.createHmac('sha3-256', process.env.MORALIS_API_KEY!)
.update(JSON.stringify(body))
.digest('hex');
if (providedSignature !== generatedSignature) {
return reply.code(401).send('Invalid signature');
}
// Обработка событий
body.logs.forEach((log: any) => {
// log.decoded содержит распарсенные аргументы
});
return reply.send({ received: true });
});
Ограничения и альтернативы
Moralis не индексирует кастомные контракты мгновенно. Для только что задеплоенного контракта исторические данные могут появляться с задержкой несколько часов. Для real-time событий нового контракта — Streams работает с момента добавления, исторические данные до этого момента нужно загружать отдельно через getLogs.
Для сложных аналитических запросов (aggregation, joins по нескольким коллекциям) Moralis уступает The Graph или собственному ClickHouse.
Ориентиры по срокам
NFT portfolio viewer с балансами + история — 1 день. Добавление Streams webhook для real-time уведомлений + интеграция с основным приложением — 2-3 дня.







