Разработка системы подписки на on-chain события через Push
Push Protocol (бывший EPNS — Ethereum Push Notification Service) — это децентрализованный слой нотификаций для Web3. Пользователь подписывается на канал через подпись кошельком, получает push-уведомления в приложении, email, или мобильный телефон. Для dApp это решает проблему "пользователь не знает что происходит с его позицией" без требования хранить email адрес.
Архитектура Push Protocol
Push работает на трёх уровнях:
- On-chain часть — контракт на Ethereum mainnet (и Polygon) фиксирует создание каналов и подписки пользователей
- Push nodes — децентрализованная сеть нод, которые доставляют уведомления
-
SDK —
@pushprotocol/restapiи@pushprotocol/socketдля взаимодействия
Канал — это смарт-контракт или EOA, который имеет право отправлять нотификации своим подписчикам. Создание канала требует стейкинга 50 PUSH токенов.
Интеграция в dApp фронтенд
Подписка пользователя на канал
import { PushAPI, CONSTANTS } from '@pushprotocol/restapi'
import { useWalletClient } from 'wagmi'
export function useChannelSubscription(channelAddress: string) {
const { data: walletClient } = useWalletClient()
const subscribe = async () => {
if (!walletClient) return
const pushUser = await PushAPI.initialize(walletClient, {
env: CONSTANTS.ENV.PROD,
})
await pushUser.notification.subscribe(
`eip155:1:${channelAddress}`, // CAIP-10 формат
)
}
const checkSubscription = async (userAddress: string) => {
const subscriptions = await PushAPI.user.getSubscriptions({
user: `eip155:1:${userAddress}`,
env: CONSTANTS.ENV.PROD,
})
return subscriptions.some((s: any) =>
s.channel.toLowerCase() === channelAddress.toLowerCase()
)
}
return { subscribe, checkSubscription }
}
Подписка требует подписи транзакции (или EIP-712 подпись для gasless через Push relay). Пользователю не нужно платить gas для большинства операций — Push поддерживает gasless подписки через мета-транзакции.
Отображение нотификаций в UI
import { PushAPI, CONSTANTS } from '@pushprotocol/restapi'
export function useNotifications(userAddress: string) {
const [notifications, setNotifications] = useState([])
useEffect(() => {
const fetchNotifications = async () => {
const notifs = await PushAPI.user.getFeeds({
user: `eip155:1:${userAddress}`,
env: CONSTANTS.ENV.PROD,
limit: 20,
})
setNotifications(notifs)
}
fetchNotifications()
}, [userAddress])
return notifications
}
Real-time через WebSocket
import { createSocketConnection, CONSTANTS } from '@pushprotocol/socket'
const pushSocket = createSocketConnection({
user: `eip155:1:${userAddress}`,
env: CONSTANTS.ENV.PROD,
socketOptions: { autoConnect: false },
})
pushSocket.connect()
pushSocket.on(CONSTANTS.SOCKET.EVENTS.USER_FEEDS, (notification) => {
// Показываем toast уведомление
showToast({
title: notification.payload.notification.title,
body: notification.payload.notification.body,
})
})
Отправка нотификаций с бэкенда
Нотификации о on-chain событиях отправляются с сервера: мониторим события через RPC/webhook, при наступлении события отправляем нотификацию через Push SDK.
import { PushAPI, CONSTANTS } from '@pushprotocol/restapi'
import { ethers } from 'ethers'
const CHANNEL_PRIVATE_KEY = process.env.PUSH_CHANNEL_PRIVATE_KEY!
async function sendLiquidationWarning(userAddress: string, healthFactor: number) {
const signer = new ethers.Wallet(CHANNEL_PRIVATE_KEY)
const pushUser = await PushAPI.initialize(signer, {
env: CONSTANTS.ENV.PROD,
})
await pushUser.channel.send([`eip155:1:${userAddress}`], {
notification: {
title: 'Предупреждение о ликвидации',
body: `Health factor упал до ${healthFactor.toFixed(2)}. Пополните залог.`,
},
payload: {
title: 'Предупреждение о ликвидации',
body: `Health factor: ${healthFactor.toFixed(2)}`,
cta: 'https://yourdapp.com/positions',
category: CONSTANTS.NOTIFICATION.TYPE.TARGETED,
},
})
}
Мониторинг on-chain событий
Push Protocol — это слой доставки, но триггер событий нужно реализовать самостоятельно. Паттерны мониторинга:
Polling через RPC — cron job каждые N секунд опрашивает getLogs или readContract. Просто, надёжно, но latency = интервал polling.
Alchemy Notify / QuickNode Streams — webhook при наступлении on-chain события. Минимальная latency (~1 блок), но vendor lock-in.
WebSocket subscriptions — eth_subscribe через WSS endpoint, обрабатываем события в реальном времени:
const wsClient = createPublicClient({
chain: mainnet,
transport: webSocket(process.env.ALCHEMY_WSS_URL),
})
wsClient.watchContractEvent({
address: lendingPoolAddress,
abi: lendingPoolAbi,
eventName: 'HealthFactorUpdated',
onLogs: async (logs) => {
for (const log of logs) {
const { user, healthFactor } = log.args
if (healthFactor < parseUnits('1.1', 18)) {
await sendLiquidationWarning(user, Number(formatUnits(healthFactor, 18)))
}
}
},
})
Типичные use cases
- DeFi: предупреждения о ликвидации (health factor < threshold)
- DEX: исполнение limit order
- NFT: bid на ваш токен, floor price изменение
- DAO: новый пропозал на голосование
- Bridge: транзакция завершена на другой сети
- Yield: APY упал ниже целевого значения
Интеграция Push Protocol в существующий dApp с несколькими типами нотификаций — 2–3 дня. Включает: создание канала в Push, настройку мониторинга on-chain событий, компонент подписки в UI и real-time WebSocket нотификации.







