-
Only tech topics about Waves: dev tools, libs, RIDE, dApps, Nodes Github: http://tiny.cc/github-waves IDE: https://ide.wavesplatform.com/ Online Course: https://stepik.org/course/54415 StackOverflow: #wavesplatform Jobs: https://t.me/wavesridejobs
async def distribute_tokens(user_id, fish_type, quantity, update, context):
try:
user_address = get_waves_address(user_id)
if not user_address:
await update.callback_query.message.reply_text("Link your Waves address first with /link!")
return
if fish_type not in TOKEN_IDS:
raise ValueError(f"Invalid fish type: {fish_type}")
recipient = pw.Address(user_address)
logging.info(f"Attempting to send {quantity} {fish_type} (Asset ID: {TOKEN_IDS[fish_type]}) to {user_address}")
tx = sender.sendAsset(recipient, quantity, TOKEN_IDS[fish_type])
logging.info(f"Raw transaction response: {tx}, type: {type(tx)}")
# Handle the response
if isinstance(tx, int):
error_msg = {
400: "Bad request - check asset ID or quantity",
403: "Insufficient funds or permission denied",
404: "Asset or address not found",
}.get(tx, f"Unknown error code: {tx}")
raise Exception(f"Transaction failed with status code {tx}: {error_msg}")
elif isinstance(tx, dict):
if 'id' not in tx:
raise Exception(f"Transaction dict missing 'id': {tx}")
tx_id = tx['id']
else:
raise Exception(f"Unexpected transaction response type: {type(tx)}, value: {tx}")
# If we reach here, tx is a dict with an 'id'
with get_db_connection() as conn:
c = conn.cursor()
c.execute(f"""
INSERT INTO {TABLE_INVENTORY} (user_id, fish_type, quantity)
VALUES (?, ?, ?)
ON CONFLICT(user_id, fish_type)
DO UPDATE SET quantity = quantity + ?""",
(user_id, fish_type, quantity, quantity))
conn.commit()
await update.callback_query.message.reply_text(
f"Caught {quantity} {fish_type}! Tokens sent to {user_address}.\nTxID: {tx_id}"
)
except Exception as e:
logging.error(f"Token distribution failed: {str(e)}")
await update.callback_query.message.reply_text(f"Failed to send {fish_type} tokens: {str(e)}")
def get_waves_address(user_id):
with get_db_connection() as conn:
c = conn.cursor()
c.execute(f"SELECT waves_address FROM {TABLE_USERS} WHERE user_id = ?", (user_id,))
result = c.fetchone()
return result[0] if result else None
def main():
validate_config()
setup_database()
bot = Bot(token=TOKEN)
app = Application.builder().token(TOKEN).build()
app.add_handler(CommandHandler("start", start))
app.add_handler(CommandHandler("help", start))
app.add_handler(CommandHandler("link", link_address))
app.add_handler(CommandHandler("fish", fish))
app.add_handler(CommandHandler("inventory", inventory))
app.add_handler(CommandHandler("leaderboard", leaderboard))
app.add_handler(CallbackQueryHandler(button))
logging.info("Bot initialized, starting polling...")
app.run_polling(allowed_updates=Update.ALL_TYPES)
logging.info("Polling stopped unexpectedly")
if name == 'main':
logging.info("Starting bot execution...")
main()
def setup_database():
with get_db_connection() as conn:
c = conn.cursor()
c.execute(f'''CREATE TABLE IF NOT EXISTS {TABLE_USERS}
(user_id INTEGER PRIMARY KEY, waves_address TEXT)''')
c.execute(f'''CREATE TABLE IF NOT EXISTS {TABLE_INVENTORY}
(user_id INTEGER, fish_type TEXT, quantity INTEGER,
FOREIGN KEY (user_id) REFERENCES {TABLE_USERS}(user_id))''')
c.execute(f'''CREATE TABLE IF NOT EXISTS {TABLE_FISHING_LOG}
(user_id INTEGER, fish_timestamp TEXT,
PRIMARY KEY (user_id, fish_timestamp))''')
conn.commit()
logging.info("Database setup completed")
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
await update.message.reply_text(
"Welcome to Fishing Bot!\n"
"Commands:\n"
"/link <address> - Link your Waves address\n"
"/fish - Start fishing\n"
"/inventory - Check your catches\n"
"/leaderboard - See top fishers\n"
"/help - Show this message"
)
async def link_address(update: Update, context: ContextTypes.DEFAULT_TYPE):
try:
if not context.args:
await update.message.reply_text("Please provide a Waves address. Example: /link 3P...")
return
waves_address = context.args[0]
if not waves_address.startswith('3P') or len(waves_address) != 35:
await update.message.reply_text("Invalid Waves address format!")
return
user_id = update.message.from_user.id
with get_db_connection() as conn:
c = conn.cursor()
c.execute(f"INSERT OR REPLACE INTO {TABLE_USERS} (user_id, waves_address) VALUES (?, ?)",
(user_id, waves_address))
conn.commit()
await update.message.reply_text("Your Waves address is linked successfully!")
except Exception as e:
logging.error(f"Error in link_address: {e}")
await update.message.reply_text("An error occurred while linking your address.")
async def fish(update: Update, context: ContextTypes.DEFAULT_TYPE):
try:
user_id = update.message.from_user.id
now = datetime.now()
if now - fishing_cooldowns[user_id] < timedelta(minutes=COOLDOWN_MINUTES):
remaining = int((timedelta(minutes=COOLDOWN_MINUTES) - (now - fishing_cooldowns[user_id])).total_seconds())
await update.message.reply_text(f"Please wait {remaining} seconds before fishing again!")
return
user_address = get_waves_address(user_id)
if not user_address:
await update.message.reply_text("Please link your Waves address first with /link!")
return
keyboard = [
[InlineKeyboardButton("North", callback_data='1'), InlineKeyboardButton("North-East", callback_data='2')],
[InlineKeyboardButton("East", callback_data='3'), InlineKeyboardButton("South-East", callback_data='4')],
[InlineKeyboardButton("South", callback_data='5'), InlineKeyboardButton("South-West", callback_data='6')],
[InlineKeyboardButton("West", callback_data='7'), InlineKeyboardButton("North-West", callback_data='8')]
]
reply_markup = InlineKeyboardMarkup(keyboard)
await update.message.reply_text(f"Fishing costs {KRILL_COST} Krill. Choose a direction:",
reply_markup=reply_markup)
except Exception as e:
logging.error(f"Error in fish command: {e}")
await update.message.reply_text("An error occurred while processing your fishing request.")
async def inventory(update: Update, context: ContextTypes.DEFAULT_TYPE):
try:
user_id = update.message.from_user.id
user_address = get_waves_address(user_id)
if not user_address:
await update.message.reply_text("Please link your Waves address first with /link!")
return
or explain why its so funny 😅
Читать полностью…
so im turning to the jedi lol
Читать полностью…
so ive basically got a telegram bot set up and working except it wont send tokens and i get this error: C:\Users\brian\PycharmProjects\PythonProject\.venv\Scripts\python.exe C:\Users\brian\PycharmProjects\PythonProject\fishing_bot.py
[ERROR] Token distribution failed: 'int' object has no attribute 'status'
to update UNIT0 deployments to the latest version, please do the following:
- make sure the image in docker-compose.yml is set to ghcr.io/unitsnetwork/consensus-client:latest
- do a docker compose pull
to ensure you have the correct image, run the following:
sudo docker image inspect ghcr.io/unitsnetwork/consensus-client:latest | jq -r '.[0].Config.Labels["org.opencontainers.image.revision"]'
07c178e4460c3fffc75d2618e3037202fc3cfa04.
https://github.com/wavesplatform/Waves/releases/tag/v1.5.9
This optional release includes the following fixes and improvements:
Blockchain locks are acquired uninterruptibly, which should make Units mining more reliable.
Docker image is based on Eclipse Temurin 21.
Units contract registry has been added for both Testnet and Mainnet.
Update notes
Even though this release is optional, we recommend everyone to update their nodes. There's no need to rebuild the state when updating from 1.5.8.
guys, our team is looking peoples for NFT-GAME
we are looking for beta-testers and chat-moderators
who interesting be a part of our team text me for details
Yes, then we miss whole beauty of having native tokens
Читать полностью…
And you would lose any integration in the platform. I think back in the days, someone suggested a similar, SC based, standard... Maybe Inal?
Читать полностью…
You mean to implement the token based on an SC as ERC-20 would suggest it?
Читать полностью…
If you need greater precision -- the only way is smart contracts. But then you should forget about native tokens support.
Читать полностью…
Just in case, /channel/tradisys_russia for topics in russian
Читать полностью…
and I hope that you all will provide understanding and can help me. because I'm sure you are all experts...
Читать полностью…
I do not only use it, i was the last maintainer (for a couple of years)...
Читать полностью…
with get_db_connection() as conn:
c = conn.cursor()
c.execute(f"SELECT fish_type, quantity FROM {TABLE_INVENTORY} WHERE user_id = ?", (user_id,))
items = c.fetchall()
user_wallet = pw.Address(user_address)
krill_balance = user_wallet.balance(TOKEN_IDS['krill']) / 10 8
inventory_text = f"Krill Balance: {krill_balance:.2f}\n"
if items:
inventory_text += "Your Catches:\n" + "\n".join([f"{item[0]}: {item[1]}" for item in items])
else:
inventory_text += "Your inventory is empty."
await update.message.reply_text(inventory_text)
except Exception as e:
logging.error(f"Error in inventory: {e}")
await update.message.reply_text("Error checking inventory!")
async def leaderboard(update: Update, context: ContextTypes.DEFAULT_TYPE):
try:
with get_db_connection() as conn:
c = conn.cursor()
c.execute(
f"SELECT user_id, SUM(quantity) FROM {TABLE_INVENTORY} GROUP BY user_id ORDER BY SUM(quantity) DESC LIMIT 5")
leaderboard_data = c.fetchall()
if leaderboard_data:
leaderboard_text = "Top Fishers:\n" + "\n".join(
[f"{i + 1}. User {user_id}: {total}" for i, (user_id, total) in enumerate(leaderboard_data)])
await update.message.reply_text(leaderboard_text)
else:
await update.message.reply_text("No one has caught fish yet!")
except Exception as e:
logging.error(f"Error in leaderboard: {e}")
await update.message.reply_text("Error generating leaderboard!")
async def button(update: Update, context: ContextTypes.DEFAULT_TYPE):
query = update.callback_query
await query.answer()
direction = query.data
user_id = query.from_user.id
now = datetime.now()
timestamp = now.strftime('%Y-%m-%d %H:%M:%S')
try:
if now - fishing_cooldowns[user_id] < timedelta(minutes=COOLDOWN_MINUTES):
remaining = int((timedelta(minutes=COOLDOWN_MINUTES) - (now - fishing_cooldowns[user_id])).total_seconds())
await query.edit_message_text(f"Please wait {remaining} seconds before fishing again!")
return
user_address = get_waves_address(user_id)
if not user_address:
await query.edit_message_text("Please link your Waves address first with /link!")
return
user_wallet = pw.Address(user_address)
krill_balance = user_wallet.balance(TOKEN_IDS['krill'])
if krill_balance < KRILL_COST:
await query.edit_message_text(
f"You need {KRILL_COST} Krill to fish! Balance: {krill_balance / 10 8:.2f}")
return
fishing_cooldowns[user_id] = now
with get_db_connection() as conn:
c = conn.cursor()
c.execute(f"INSERT INTO {TABLE_FISHING_LOG} (user_id, fish_timestamp) VALUES (?, ?)",
(user_id, timestamp))
conn.commit()
await query.edit_message_text(f"Fishing in direction {direction}...")
fish_caught = calculate_catch(direction)
if fish_caught and fish_caught != 'nothing':
quantity = calculate_catch_quantity(fish_caught)
await distribute_tokens(user_id, fish_caught, quantity, update, context)
else:
await query.message.reply_text("You caught nothing this time!")
except Exception as e:
logging.error(f"Error in button callback: {e}")
await query.edit_message_text("An error occurred while fishing!")
def calculate_catch(direction):
probabilities = FISH_CATCH_PROBABILITIES.get(direction, {})
return random.choices(list(probabilities.keys()), weights=list(probabilities.values()), k=1)[
0] if probabilities else None
def calculate_catch_quantity(fish_type):
min_catch, max_catch = CATCH_RANGES.get(fish_type, (0, 0))
return random.randint(min_catch, max_catch)
import logging
import random
import sqlite3
import os
import pywaves as pw
from telegram import Bot, Update, InlineKeyboardButton, InlineKeyboardMarkup
from telegram.ext import Application, CommandHandler, CallbackQueryHandler, ContextTypes
from dotenv import load_dotenv
from datetime import datetime, timedelta
from collections import defaultdict
# Constants
DATABASE_FILE = 'user_addresses.db'
TABLE_USERS = 'user_addresses'
TABLE_INVENTORY = 'user_inventory'
TABLE_FISHING_LOG = 'fishing_log'
COOLDOWN_MINUTES = 1
# Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(message)s',
handlers=[
logging.FileHandler('fishing_bot.log'),
logging.StreamHandler()
]
)
# Load environment variables
load_dotenv()
TOKEN = os.getenv('BOT_TOKEN')
CHAT_ID = os.getenv('CHAT_ID')
SENDER_ADDRESS = os.getenv('SENDER_ADDRESS')
SENDER_PRIVATE_KEY = os.getenv('SENDER_PRIVATE_KEY')
logging.info(f"Environment loaded - TOKEN: {TOKEN[:5] if TOKEN else None}..., "
f"ADDRESS: {SENDER_ADDRESS}, KEY: {SENDER_PRIVATE_KEY[:5] if SENDER_PRIVATE_KEY else None}...")
TOKEN_IDS = {
'sculpin': '9Rv2rf2Aia7itjaXfs27t51JBwKwyP29Thy6F8qdZXLk',
'crab': 'rtQ8rMRPLeTtFAFFAa3vrYGfpp5jvm3VPKXivhQSwXM',
'haddock': '3GmnizcybRQwLtku3AdQcmrJnyR4xgpDQygcSvEi7UPN',
'halibut': '8FpCUVrRNGG4tyJqknguzCdeW8ArcskP37Fz6eUhZSvk',
'swordfish': '4FNvaVD92yLn9xwXN94RfX1TsMWn2ennS4iw4Swsaptu',
'tuna': '3xipSS6JctxvmfWjbzUSpigDgqEneXZZpHTZAWxs3Hp2',
'krill': 'HrPA8VBPGicVukmy52EsgkGr5Appup4bfcoDjhioFkRE'
}
FISH_CATCH_PROBABILITIES = {
'1': {'sculpin': 0.25, 'crab': 0.20, 'haddock': 0.15, 'halibut': 0.10, 'swordfish': 0.05, 'tuna': 0.03,
'nothing': 0.22},
'2': {'sculpin': 0.27, 'crab': 0.22, 'haddock': 0.16, 'halibut': 0.11, 'swordfish': 0.04, 'tuna': 0.02,
'nothing': 0.18},
'3': {'sculpin': 0.23, 'crab': 0.18, 'haddock': 0.14, 'halibut': 0.09, 'swordfish': 0.06, 'tuna': 0.04,
'nothing': 0.26},
'4': {'sculpin': 0.28, 'crab': 0.23, 'haddock': 0.17, 'halibut': 0.12, 'swordfish': 0.05, 'tuna': 0.03,
'nothing': 0.12},
'5': {'sculpin': 0.24, 'crab': 0.19, 'haddock': 0.13, 'halibut': 0.10, 'swordfish': 0.06, 'tuna': 0.03,
'nothing': 0.25},
'6': {'sculpin': 0.26, 'crab': 0.21, 'haddock': 0.15, 'halibut': 0.11, 'swordfish': 0.04, 'tuna': 0.02,
'nothing': 0.21},
'7': {'sculpin': 0.22, 'crab': 0.17, 'haddock': 0.14, 'halibut': 0.08, 'swordfish': 0.07, 'tuna': 0.05,
'nothing': 0.27},
'8': {'sculpin': 0.29, 'crab': 0.24, 'haddock': 0.16, 'halibut': 0.13, 'swordfish': 0.05, 'tuna': 0.03,
'nothing': 0.10}
}
CATCH_RANGES = {
'sculpin': (500, 2000),
'crab': (250, 1000),
'haddock': (100, 500),
'halibut': (50, 250),
'swordfish': (20, 100),
'tuna': (5, 25)
}
KRILL_COST = 100
fishing_cooldowns = defaultdict(lambda: datetime.min)
# Waves setup
pw.setNode('https://nodes.wavesnodes.com', 'mainnet')
sender = pw.Address(privateKey=SENDER_PRIVATE_KEY)
def validate_config():
required_vars = {
'BOT_TOKEN': TOKEN,
'SENDER_PRIVATE_KEY': SENDER_PRIVATE_KEY,
'SENDER_ADDRESS': SENDER_ADDRESS
}
missing = [key for key, value in required_vars.items() if not value]
if missing:
raise ValueError(f"Missing required environment variables: {', '.join(missing)}")
logging.info("Configuration validated successfully")
def get_db_connection():
return sqlite3.connect(DATABASE_FILE, timeout=10)
grok cant figure it out for me
Читать полностью…
Hi everyone
Is there a bug bounty program ?
Deb (https://github.com/wavesplatform/Waves/releases/download/v1.5.9/waves_1.5.9_all.deb) and Docker versions (docker pull wavesplatform/wavesnode:1.5.9) updated for Waves Nodes but no info for UNiT0 Nodes yet!
Читать полностью…
Any blockchain developer here?
Dm me
Burned fees are not collected in any physical or digital location, and they are not sent to any address. Technically, these ETH are permanently "destroyed" by the Ethereum network's code and removed from the total supply.
Читать полностью…
But i agree, it would be non-sense!
Читать полностью…
Yes, or some another wrapper/converter. I do not know any RIDE token standards because usually 64-bit precision is enough.
Читать полностью…
Any blockchain developer here?
Dm me
Almost. You simple cannot have more than 64-bit value of any native token on Waves because you cannot issue such number. So its just nonsense to wish to use amounts greater than 64-bit values.
Читать полностью…
A signed transaction sent to the network must contain the amount and fee to be sent and a number-type commission, meaning that a large number cannot be represented as a string. The value of these fields is Long. For 512-bit accuracy, smart contracts must be used, and the tokens of the native platform have an accuracy of no more than 64 bits of numbers. It is proposed to use BigInt for further conversion into bytes. Did I understand you correctly?
Читать полностью…
wow, it's really nice to meet great people like you and everyone. I am very lucky. so I didn't hesitate to ask something. and I really hope I can understand this library.
Читать полностью…