Daily signal
Upvotes are a repeatable activity signal inside the Abstract ecosystem. The bot protects the habit without removing human control.
A practical guide to the Telegram-confirmed daily upvote flow we built: Portal checks, session-key voting, cron reminders, safe preflight, and one-tap confirmation — wrapped in a playful Abstract design.

Abstract Portal streaks reward consistency. The annoying part is remembering the daily window, especially when you are busy, sleeping, or traveling.
Upvotes are a repeatable activity signal inside the Abstract ecosystem. The bot protects the habit without removing human control.
The script reads Portal vote data, checks whether today is already done, and waits until the safe daily window opens.
The bot does not silently fire. Telegram sends a card, and the vote broadcasts only after explicit confirmation.
A small self-hosted system: OpenClaw Telegram account, Abstract Portal API checks, AGW session key, cron scheduler, and narrow transaction policy.
The daily sender checks Portal state. If the wallet has not voted, it sends a Telegram card. When you tap confirm, a separate broadcast script uses a limited AGW session key to call the Portal vote contract.
A practical sequence someone can follow: setup, Telegram, session key, scripts, cron, testing, and recovery.
The bot should not vote blindly. It should watch the Abstract Portal window, notify you in Telegram, then broadcast only after you tap confirm.
voteForApp(appId) transaction through a scoped session key.Use a small VPS or always-on machine. Install Node.js 20+, create a project folder, and keep secrets outside git.
mkdir -p ~/abstract-upvote-bot
cd ~/abstract-upvote-bot
npm init -y
npm install viem @abstract-foundation/agw-client
out/ folder for logs and daily run output.In Telegram, talk to @BotFather, create a new bot, copy the token, then send your bot one message so it can identify your chat.
# Example env shape — token values stay private
TELEGRAM_BOT_TOKEN=123456:bot_token_here
TELEGRAM_CHAT_ID=your_numeric_chat_id
The daily sender should ask Portal whether the wallet already voted today. If votedToday=true, it exits quietly. If not, it sends a Telegram card.
// pseudo-flow
const status = await getPortalVoteStatus(wallet)
if (status.votedToday) return
await sendTelegramCard({ appId, currentStreak, nextVoteBy })
The session key is a separate signer. It must be registered with the Abstract Global Wallet and scoped to exactly the upvote action.
voteForApp(uint256).0.Before a vote can broadcast, preflight checks that the action is still valid and safe. This prevents duplicate votes, wrong app IDs, expired sessions, or policy mismatches.
node tools/abstract-wallet/prepare-session-vote-tx.mjs --app 207
votedToday=false.The daily card has two choices: confirm or skip. Confirm calls the broadcast script with an exact confirmation string. Skip does nothing.
✅ Confirm upvote → broadcast-session-vote-tx.mjs
⏭ Skip today → no transaction
Broadcast should be boring and narrow. It receives the confirmed app ID, re-runs preflight, then uses the session signer to submit the vote.
node tools/abstract-wallet/broadcast-session-vote-tx.mjs \
--app 207 \
--confirm "CONFIRM_ABSTRACT_UPVOTE"
Use cron to run the daily card sender after the Portal window opens. Log every run so you can inspect failures.
5 15 * * * cd ~/abstract-upvote-bot && \
node tools/abstract-upvotes/send-daily-card.mjs \
>> tools/abstract-upvotes/out/daily-card.log 2>&1
nextVoteBy value to derive the safe window and added a small grace period.Do not jump straight to production cron. Test each layer separately so any failure is easy to isolate.
votedToday=false.votedToday=true.The exact filenames from our build, shown as a reproducible map rather than leaking secrets.
Checks Portal state and sends the confirm/skip card.
tools/abstract-upvotes/send-daily-card.mjsValidates wallet state, app id, session policy and gas before anything is sent.
tools/abstract-wallet/prepare-session-vote-tx.mjsRuns only after confirmation and submits the vote through the session key.
tools/abstract-wallet/broadcast-session-vote-tx.mjsThe real job runs once per day and writes logs. Time should be adjusted to the Portal vote window for the wallet.
5 15 * * * cd /path/to/workspace && \
node tools/abstract-upvotes/send-daily-card.mjs \
>> tools/abstract-upvotes/out/daily-card.log 2>&1
This is not “give a bot your wallet.” The whole point is narrow permissions, simulation/preflight, and human confirmation.
The bot never needs the main wallet seed phrase or main private key. If a guide asks for that, stop.
The session policy is limited to the Portal vote contract, the vote selector, zero ETH value, and a small fee cap.
Before broadcast: check not already voted, app is allowed, session not expired, policy matches, gas is covered, simulation passes.
Production is mostly about boring reliability: logs, narrow permissions, and no duplicate retries.
The daily card is perfect for cron. If you add a long-running watcher later, use PM2 or systemd.
Check logs after the first scheduled windows. Confirm the card timing, button behavior, and Portal status after a successful vote.
If a local command times out but the gateway/server may still be processing, check logs/status before retrying. Telegram messages and votes are not things to spam twice.
A daily Abstract upvote assistant that protects streaks without taking wallet control.