Economy bots are community engagement machines. Users earn currency by participating, spend it in shops, and gamble it for excitement. When designed thoughtfully, they create compelling reasons for members to stay active and return daily.
Database Schema
CREATE TABLE users (
user_id TEXT PRIMARY KEY,
guild_id TEXT NOT NULL,
balance INTEGER DEFAULT 0,
bank INTEGER DEFAULT 0,
last_daily TIMESTAMP,
created_at TIMESTAMP DEFAULT NOW()
);
CREATE TABLE items (
item_id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
description TEXT,
price INTEGER NOT NULL,
role_id TEXT -- Optional: grant a Discord role on purchase
);
CREATE TABLE inventory (
user_id TEXT,
guild_id TEXT,
item_id INTEGER REFERENCES items(item_id),
quantity INTEGER DEFAULT 1,
PRIMARY KEY (user_id, guild_id, item_id)
);
Core Commands
// /daily - Claim daily reward
async function claimDaily(interaction) {
const data = await getUser(interaction.user.id, interaction.guildId);
const now = new Date();
const last = data.last_daily ? new Date(data.last_daily) : null;
if (last && (now - last) < 86400000) { // 24 hours
const remaining = Math.ceil((86400000 - (now - last)) / 3600000);
return interaction.reply(`⏰ Come back in ${remaining} hours for your daily reward.`);
}
const reward = 100 + Math.floor(Math.random() * 50);
await addBalance(interaction.user.id, interaction.guildId, reward);
await updateLastDaily(interaction.user.id, interaction.guildId, now);
interaction.reply(`✅ You claimed your daily reward of **${reward} coins**!`);
}
// /balance - Check balance
async function checkBalance(interaction) {
const data = await getUser(interaction.user.id, interaction.guildId);
interaction.reply(`💰 Wallet: **${data.balance}** | Bank: **${data.bank}**`);
}
Shop Implementation
// /shop - View items
async function viewShop(interaction) {
const items = await db.query('SELECT * FROM items ORDER BY price ASC');
const embed = new EmbedBuilder()
.setTitle('🛒 Server Shop')
.setDescription(items.rows.map(item =>
`**${item.name}** - ${item.price} coins
${item.description}`
).join('
'));
interaction.reply({ embeds: [embed] });
}
Gambling: Responsible Design
Gambling features drive engagement but require responsible limits:
// /flip - Coin flip with house edge
async function coinflip(interaction, amount, choice) {
const user = await getUser(interaction.user.id, interaction.guildId);
if (amount > user.balance) return interaction.reply('Insufficient funds.');
if (amount > 5000) return interaction.reply('Maximum bet is 5,000 coins.'); // Bet cap
const win = Math.random() < 0.48; // 48% win rate = 4% house edge
if (win) {
await addBalance(interaction.user.id, interaction.guildId, amount);
interaction.reply(`✅ You won **${amount} coins**!`);
} else {
await removeBalance(interaction.user.id, interaction.guildId, amount);
interaction.reply(`❌ You lost **${amount} coins**.`);
}
}
House edge directs coins toward sinks (shop purchases, special events), preventing currency hyperinflation.
Host your Discord economy bot on Space-Node
What an economy bot actually does
The basic feature set:
- Per-user balance stored in a database.
- Earning: chat (with cooldowns),
/work,/daily,/beg. - Spending:
/buy,/gamble, role unlocks. - Leaderboards.
- Anti-abuse: cooldowns, alt detection, transaction logs.
Most cookie-cutter economy bots fall apart on three things: race conditions, alt-farming, and Discord rate limits.
Schema that actually works
CREATE TABLE users (
guild_id BIGINT NOT NULL,
user_id BIGINT NOT NULL,
balance BIGINT NOT NULL DEFAULT 0,
bank BIGINT NOT NULL DEFAULT 0,
created TIMESTAMP DEFAULT NOW(),
PRIMARY KEY (guild_id, user_id)
);
CREATE TABLE transactions (
id BIGSERIAL PRIMARY KEY,
guild_id BIGINT NOT NULL,
user_id BIGINT NOT NULL,
delta BIGINT NOT NULL,
reason TEXT NOT NULL,
ts TIMESTAMP DEFAULT NOW()
);
CREATE TABLE cooldowns (
guild_id BIGINT NOT NULL,
user_id BIGINT NOT NULL,
command TEXT NOT NULL,
expires TIMESTAMP NOT NULL,
PRIMARY KEY (guild_id, user_id, command)
);
The transactions table is non-negotiable. Without it you cannot defend against "the bot stole my coins" tickets.
Atomic balance update
WITH upd AS (
UPDATE users SET balance = balance + $delta
WHERE guild_id = $g AND user_id = $u
RETURNING balance
)
INSERT INTO transactions (guild_id, user_id, delta, reason)
VALUES ($g, $u, $delta, $reason);
Wrap both statements in a transaction. Without that, two concurrent /work commands can each read 100, both write 110, and the user gets 10 instead of 20.
Anti-alt and anti-bot
| Attack | Defense |
|---|---|
| New accounts farming /daily | require account age > 7 days |
| Same IP / phone (you can't see) | role-gate /work behind a verified role |
| Self-bots automating chat | rate-limit by message-per-minute, ignore content with no real entropy |
| Multi-user gambling pyramid | cap daily transfer per pair |
Discord rate limits
/work returning a long embed at 1000 commands/min hits the per-channel rate limit (5 messages / 5 s). For high-traffic servers, send to a dedicated channel and consider deferred replies.
Hosting
A small bot needs ~150 MB RAM, ~0.1 vCPU. A bot serving 10k servers needs 1-2 GB and Postgres on a dedicated host.
