mirror of
https://github.com/MAGICGrants/autoforward-autoconvert.git
synced 2026-01-06 20:23:52 -05:00
Update btc electrum, workaround for dynamic_fees config no longer available and add testnet autoforward support
This commit is contained in:
15
.env.example
15
.env.example
@@ -1,3 +1,6 @@
|
||||
# Using TESTNET
|
||||
TESTNET="1"
|
||||
|
||||
ELECTRUM_RPC_PASSWORD=""
|
||||
BITCOIN_WALLET_SEED=""
|
||||
BITCOIN_ELECTRUM_SERVER_ADDRESS=""
|
||||
@@ -11,14 +14,16 @@ MONERO_WALLET_SEED=""
|
||||
MONERO_WALLET_PASSWORD=""
|
||||
MONERO_WALLET_HEIGHT=""
|
||||
|
||||
KRAKEN_API_KEY=""
|
||||
KRAKEN_API_SECRET=""
|
||||
|
||||
MAX_NETWORK_FEE_PERCENT="5"
|
||||
MAX_SLIPPAGE_PERCENT="0.5"
|
||||
SETTLEMENT_CURRENCY="USD"
|
||||
|
||||
BITCOIN_FEE_SOURCE="https://mempool.space/api/v1/fees/recommended"
|
||||
# Using TESTNET API
|
||||
BITCOIN_FEE_SOURCE="https://mempool.space/testnet/api/v1/fees/recommended"
|
||||
BITCOIN_FEE_RATE="halfHourFee"
|
||||
LITECOIN_FEE_SOURCE="https://litecoinspace.org/api/v1/fees/recommended"
|
||||
LITECOIN_FEE_SOURCE="https://litecoinspace.org/testnet/api/v1/fees/recommended"
|
||||
LITECOIN_FEE_RATE="halfHourFee"
|
||||
|
||||
# If you are on testnet, you can leave this blank since autoconvert won't work
|
||||
KRAKEN_API_KEY=""
|
||||
KRAKEN_API_SECRET=""
|
||||
@@ -3,13 +3,14 @@ services:
|
||||
build:
|
||||
context: ./docker/btc-electrum
|
||||
args:
|
||||
VERSION: "4.5.5"
|
||||
CHECKSUM_SHA512: "3bdfce2187466fff20fd67736bdf257bf95d3517de47043be411ccda558a442b8fd81d6a8da094a39a1db39a7339dcd4282e73a7f00cf6bbd70473d7ce456b0b"
|
||||
VERSION: "4.6.2"
|
||||
CHECKSUM_SHA512: "890c1fae4cd2da5f1fea3f07c1c6b537f9557177b86e23c40333ec83743ed46182ec74e43a27416a3eaa9704d87d0a1702d0a789b23467bcc68a17d638b43655"
|
||||
container_name: btc-electrum
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- bitcoin-data:/home/electrum/.electrum
|
||||
environment:
|
||||
- TESTNET=${TESTNET}
|
||||
- ELECTRUM_RPC_USER=user
|
||||
- ELECTRUM_RPC_PASSWORD=${ELECTRUM_RPC_PASSWORD}
|
||||
- ELECTRUM_SERVER_ADDRESS=${BITCOIN_ELECTRUM_SERVER_ADDRESS}
|
||||
@@ -25,6 +26,7 @@ services:
|
||||
volumes:
|
||||
- litecoin-data:/home/electrum-ltc/.electrum
|
||||
environment:
|
||||
- TESTNET=${TESTNET}
|
||||
- ELECTRUM_RPC_USER=user
|
||||
- ELECTRUM_RPC_PASSWORD=${ELECTRUM_RPC_PASSWORD}
|
||||
- ELECTRUM_SERVER_ADDRESS=${LITECOIN_ELECTRUM_SERVER_ADDRESS}
|
||||
@@ -40,23 +42,24 @@ services:
|
||||
volumes:
|
||||
- litecoin-mweb-data:/home/electrum-ltc/.electrum
|
||||
environment:
|
||||
- TESTNET=${TESTNET}
|
||||
- ELECTRUM_RPC_USER=user
|
||||
- ELECTRUM_RPC_PASSWORD=${ELECTRUM_RPC_PASSWORD}
|
||||
- ELECTRUM_SERVER_ADDRESS=${LITECOIN_ELECTRUM_SERVER_ADDRESS}
|
||||
|
||||
monero-wallet-rpc:
|
||||
image: ghcr.io/sethforprivacy/simple-monero-wallet-rpc:latest
|
||||
build:
|
||||
context: ./docker/monero-wallet-rpc
|
||||
restart: unless-stopped
|
||||
container_name: monero-wallet-rpc
|
||||
volumes:
|
||||
- monero-wallet-rpc-data:/home/monero
|
||||
command:
|
||||
- "--trusted-daemon"
|
||||
- "--rpc-bind-port=18082"
|
||||
- "--rpc-login=user:${MONERO_RPC_PASSWORD}"
|
||||
- "--daemon-address=${MONERO_DAEMON_ADDRESS}"
|
||||
- "--wallet-dir=/home/monero/wallet"
|
||||
- "--log-level=4"
|
||||
- --trusted-daemon
|
||||
- --rpc-bind-port=18082
|
||||
- --rpc-login=user:${MONERO_RPC_PASSWORD}
|
||||
- --daemon-address=${MONERO_DAEMON_ADDRESS}
|
||||
- --wallet-dir=/home/monero/wallet
|
||||
|
||||
seed-importer:
|
||||
build:
|
||||
@@ -64,6 +67,7 @@ services:
|
||||
container_name: seed-importer
|
||||
environment:
|
||||
- PYTHONUNBUFFERED=1
|
||||
- TESTNET=${TESTNET}
|
||||
- BITCOIN_ELECTRUM_RPC_URL=http://btc-electrum:7000
|
||||
- LITECOIN_ELECTRUM_RPC_URL=http://ltc-electrum:7000
|
||||
- LITECOIN_MWEB_ELECTRUM_RPC_URL=http://ltc-mweb-electrum:7000
|
||||
@@ -91,6 +95,7 @@ services:
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- PYTHONUNBUFFERED=1
|
||||
- TESTNET=${TESTNET}
|
||||
- BITCOIN_ELECTRUM_RPC_URL=http://btc-electrum:7000
|
||||
- LITECOIN_ELECTRUM_RPC_URL=http://ltc-electrum:7000
|
||||
- LITECOIN_MWEB_ELECTRUM_RPC_URL=http://ltc-mweb-electrum:7000
|
||||
@@ -114,6 +119,7 @@ services:
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- PYTHONUNBUFFERED=1
|
||||
- TESTNET=${TESTNET}
|
||||
- KRAKEN_API_KEY=${KRAKEN_API_KEY}
|
||||
- KRAKEN_API_SECRET=${KRAKEN_API_SECRET}
|
||||
command: python ./src/autoconvert.py
|
||||
|
||||
@@ -15,7 +15,7 @@ RUN mkdir -p ${ELECTRUM_HOME}/.electrum && \
|
||||
# IMPORTANT: always verify gpg signature before changing a hash here!
|
||||
ENV ELECTRUM_CHECKSUM_SHA512 $CHECKSUM_SHA512
|
||||
|
||||
RUN apk --no-cache add --virtual build-dependencies gcc musl-dev libsecp256k1 libsecp256k1-dev libressl-dev
|
||||
RUN apk --no-cache add --virtual build-dependencies make gcc musl-dev libsecp256k1 libsecp256k1-dev libressl-dev autoconf automake libtool
|
||||
RUN wget https://download.electrum.org/${ELECTRUM_VERSION}/Electrum-${ELECTRUM_VERSION}.tar.gz
|
||||
RUN [ "${ELECTRUM_CHECKSUM_SHA512} Electrum-${ELECTRUM_VERSION}.tar.gz" = "$(sha512sum Electrum-${ELECTRUM_VERSION}.tar.gz)" ]
|
||||
RUN echo -e "**************************\n SHA 512 Checksum OK\n**************************"
|
||||
|
||||
@@ -1,17 +1,27 @@
|
||||
#!/usr/bin/env sh
|
||||
set -ex
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
trap 'pkill -TERM -P1; electrum stop; exit 0' SIGTERM
|
||||
# trap 'pkill -TERM -P1; electrum stop; exit 0' SIGTERM
|
||||
|
||||
rm -f .electrum/daemon
|
||||
electrum --offline setconfig rpcuser ${ELECTRUM_RPC_USER}
|
||||
electrum --offline setconfig rpcpassword ${ELECTRUM_RPC_PASSWORD}
|
||||
electrum --offline setconfig rpchost 0.0.0.0
|
||||
electrum --offline setconfig rpcport 7000
|
||||
rm -f .electrum/daemon .electrum/testnet/daemon
|
||||
|
||||
if [ -n "${ELECTRUM_SERVER_ADDRESS}" ]; then
|
||||
electrum daemon -1 -s "${ELECTRUM_SERVER_ADDRESS}" "$@"
|
||||
else
|
||||
electrum daemon "$@"
|
||||
ADDITIONAL_SETCONFIG_FLAGS=""
|
||||
ADDITIONAL_DAEMON_FLAGS=""
|
||||
|
||||
if [ "$TESTNET" = "1" ]; then
|
||||
ADDITIONAL_SETCONFIG_FLAGS="--testnet"
|
||||
ADDITIONAL_DAEMON_FLAGS="--testnet"
|
||||
fi
|
||||
|
||||
if [ -n "$ELECTRUM_SERVER_ADDRESS" ]; then
|
||||
ADDITIONAL_DAEMON_FLAGS="$ADDITIONAL_DAEMON_FLAGS -1 -s $ELECTRUM_SERVER_ADDRESS"
|
||||
fi
|
||||
|
||||
echo $ADDITIONAL_SETCONFIG_FLAGS
|
||||
|
||||
electrum $ADDITIONAL_SETCONFIG_FLAGS --offline setconfig rpcuser $ELECTRUM_RPC_USER
|
||||
electrum $ADDITIONAL_SETCONFIG_FLAGS --offline setconfig rpcpassword $ELECTRUM_RPC_PASSWORD
|
||||
electrum $ADDITIONAL_SETCONFIG_FLAGS --offline setconfig rpchost 0.0.0.0
|
||||
electrum $ADDITIONAL_SETCONFIG_FLAGS --offline setconfig rpcport 7000
|
||||
electrum daemon $ADDITIONAL_DAEMON_FLAGS "$@"
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ ARG VERSION
|
||||
ARG CHECKSUM_SHA512
|
||||
|
||||
RUN apt update && \
|
||||
apt install -y wget libfuse2 fuse3
|
||||
apt install -y wget libfuse2 fuse3 python3-pyqt6
|
||||
|
||||
ENV ELECTRUM_VERSION=$VERSION
|
||||
ENV ELECTRUM_USER=electrum
|
||||
|
||||
@@ -1,18 +1,25 @@
|
||||
#!/bin/bash
|
||||
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
trap 'pkill -TERM -P1; electrum-ltc.appimage stop; exit 0' SIGTERM
|
||||
# trap 'pkill -TERM -P1; electrum-ltc.appimage stop; exit 0' SIGTERM
|
||||
|
||||
rm -f $HOME/.electrum-ltc/daemon
|
||||
./squashfs-root/AppRun --offline setconfig rpcuser ${ELECTRUM_RPC_USER}
|
||||
./squashfs-root/AppRun --offline setconfig rpcpassword ${ELECTRUM_RPC_PASSWORD}
|
||||
./squashfs-root/AppRun --offline setconfig rpchost 0.0.0.0
|
||||
./squashfs-root/AppRun --offline setconfig rpcport 7000
|
||||
rm -f .electrum-ltc/daemon .electrum-ltc/testnet/daemon
|
||||
|
||||
if [ -n "${ELECTRUM_SERVER_ADDRESS}" ]; then
|
||||
./squashfs-root/AppRun daemon -1 -s "${ELECTRUM_SERVER_ADDRESS}" "$@"
|
||||
else
|
||||
./squashfs-root/AppRun daemon "$@"
|
||||
ADDITIONAL_SETCONFIG_FLAGS=""
|
||||
ADDITIONAL_DAEMON_FLAGS=""
|
||||
|
||||
if [ "$TESTNET" = "1" ]; then
|
||||
ADDITIONAL_SETCONFIG_FLAGS="--testnet"
|
||||
ADDITIONAL_DAEMON_FLAGS="--testnet"
|
||||
fi
|
||||
|
||||
if [ -n "$ELECTRUM_SERVER_ADDRESS" ]; then
|
||||
ADDITIONAL_DAEMON_FLAGS="$ADDITIONAL_DAEMON_FLAGS -1 -s $ELECTRUM_SERVER_ADDRESS"
|
||||
fi
|
||||
|
||||
./squashfs-root/AppRun $ADDITIONAL_SETCONFIG_FLAGS --offline setconfig rpcuser $ELECTRUM_RPC_USER
|
||||
./squashfs-root/AppRun $ADDITIONAL_SETCONFIG_FLAGS --offline setconfig rpcpassword $ELECTRUM_RPC_PASSWORD
|
||||
./squashfs-root/AppRun $ADDITIONAL_SETCONFIG_FLAGS --offline setconfig rpchost 0.0.0.0
|
||||
./squashfs-root/AppRun $ADDITIONAL_SETCONFIG_FLAGS --offline setconfig rpcport 7000
|
||||
./squashfs-root/AppRun daemon $ADDITIONAL_DAEMON_FLAGS "$@"
|
||||
|
||||
|
||||
5
docker/monero-wallet-rpc/Dockerfile
Normal file
5
docker/monero-wallet-rpc/Dockerfile
Normal file
@@ -0,0 +1,5 @@
|
||||
FROM ghcr.io/sethforprivacy/simple-monero-wallet-rpc:latest
|
||||
|
||||
COPY docker-entrypoint.sh /usr/local/bin/
|
||||
|
||||
ENTRYPOINT ["docker-entrypoint.sh"]
|
||||
14
docker/monero-wallet-rpc/docker-entrypoint.sh
Executable file
14
docker/monero-wallet-rpc/docker-entrypoint.sh
Executable file
@@ -0,0 +1,14 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
ADDITIONAL_FLAGS=""
|
||||
|
||||
if [ "$TESTNET" = "1" ]; then
|
||||
ADDITIONAL_FLAGS="--testnet"
|
||||
fi
|
||||
|
||||
set -- "monero-wallet-rpc" "--non-interactive" "--rpc-bind-ip=0.0.0.0" "--confirm-external-bind" $ADDITIONAL_FLAGS "$@"
|
||||
|
||||
# Start the daemon using fixuid
|
||||
# to adjust permissions if needed
|
||||
exec fixuid -q "$@"
|
||||
@@ -1,8 +1,7 @@
|
||||
from typing import Literal, cast
|
||||
import math
|
||||
import traceback
|
||||
import random
|
||||
import time
|
||||
|
||||
import util
|
||||
import env
|
||||
|
||||
@@ -86,28 +85,32 @@ def attempt_sell(asset, settlement_currency, settlement_kraken_ticker):
|
||||
else:
|
||||
print(f'No balance for {asset}; skipping')
|
||||
|
||||
if __name__ == '__main__':
|
||||
if env.TESTNET == '1':
|
||||
print('Testnet mode enabled. Autoconvert will now sleep forever.')
|
||||
time.sleep(999999999)
|
||||
|
||||
while 1:
|
||||
# First, get the settlement currency formatted correctly
|
||||
settlement_currency = env.SETTLEMENT_CURRENCY
|
||||
# https://support.kraken.com/hc/en-us/articles/360001206766-Bitcoin-currency-code-XBT-vs-BTC
|
||||
if settlement_currency in ['AUD', 'CAD', 'EUR', 'GBP', 'JPY', 'USD']:
|
||||
settlement_kraken_ticker = 'Z' + settlement_currency
|
||||
elif settlement_currency in ['ETC', 'ETH', 'LTC', 'MLN', 'REP', 'XBT', 'XDG', 'XLM', 'XMR', 'XRP', 'ZEC']:
|
||||
settlement_kraken_ticker = 'X' + settlement_currency
|
||||
else:
|
||||
settlement_kraken_ticker = settlement_currency
|
||||
while 1:
|
||||
# First, get the settlement currency formatted correctly
|
||||
settlement_currency = env.SETTLEMENT_CURRENCY
|
||||
# https://support.kraken.com/hc/en-us/articles/360001206766-Bitcoin-currency-code-XBT-vs-BTC
|
||||
if settlement_currency in ['AUD', 'CAD', 'EUR', 'GBP', 'JPY', 'USD']:
|
||||
settlement_kraken_ticker = 'Z' + settlement_currency
|
||||
elif settlement_currency in ['ETC', 'ETH', 'LTC', 'MLN', 'REP', 'XBT', 'XDG', 'XLM', 'XMR', 'XRP', 'ZEC']:
|
||||
settlement_kraken_ticker = 'X' + settlement_currency
|
||||
else:
|
||||
settlement_kraken_ticker = settlement_currency
|
||||
|
||||
# Then, initiate the selling process for each supported asset unless it's already the settlement currency
|
||||
for asset in ['XBT', 'LTC', 'XMR']:
|
||||
try:
|
||||
if settlement_currency != asset:
|
||||
attempt_sell(asset, settlement_currency, settlement_kraken_ticker)
|
||||
else:
|
||||
print(f'Not converting {asset} since it is already in the settlement currency')
|
||||
except Exception as e:
|
||||
print(util.get_time(), f'Error attempting to sell {asset}:')
|
||||
print(traceback.format_exc())
|
||||
# Then, initiate the selling process for each supported asset unless it's already the settlement currency
|
||||
for asset in ['XBT', 'LTC', 'XMR']:
|
||||
try:
|
||||
if settlement_currency != asset:
|
||||
attempt_sell(asset, settlement_currency, settlement_kraken_ticker)
|
||||
else:
|
||||
print(f'Not converting {asset} since it is already in the settlement currency')
|
||||
except Exception as e:
|
||||
print(util.get_time(), f'Error attempting to sell {asset}:')
|
||||
print(traceback.format_exc())
|
||||
|
||||
delay = random.randint(30, 90)
|
||||
time.sleep(delay)
|
||||
delay = random.randint(30, 90)
|
||||
time.sleep(delay)
|
||||
|
||||
@@ -5,7 +5,7 @@ import traceback
|
||||
import requests
|
||||
import json
|
||||
|
||||
from constants import MIN_BITCOIN_SEND_AMOUNT, MIN_LITECOIN_SEND_AMOUNT, MIN_MONERO_SEND_AMOUNT
|
||||
from constants import MIN_BITCOIN_SEND_AMOUNT_MAINNET, MIN_BITCOIN_SEND_AMOUNT_TESTNET, MIN_LITECOIN_SEND_AMOUNT_MAINNET, MIN_LITECOIN_SEND_AMOUNT_TESTNET, MIN_MONERO_SEND_AMOUNT_MAINNET, MIN_MONERO_SEND_AMOUNT_TESTNET
|
||||
import util
|
||||
import env
|
||||
|
||||
@@ -29,20 +29,25 @@ def get_fee_rate(coin: ElectrumCoin) -> int:
|
||||
|
||||
return requests.get(source).json()[rate]
|
||||
|
||||
def set_electrum_fee_rate(coin: ElectrumCoin, rate: int, dynamic: bool):
|
||||
def set_electrum_fee_rate(coin: ElectrumCoin, rate: int = None, dynamic: bool = None):
|
||||
if dynamic: # Fall back to the Electrum rate if there is an issue
|
||||
util.request_electrum_rpc(coin, 'setconfig', ['dynamic_fees', True])
|
||||
util.request_electrum_rpc(coin, 'setconfig', ['fee_policy.default', 'eta:3'])
|
||||
elif rate:
|
||||
util.request_electrum_rpc(coin, 'setconfig', ['fee_policy.default', f'feerate:{rate * 1000}'])
|
||||
else:
|
||||
util.request_electrum_rpc(coin, 'setconfig', ['dynamic_fees', False])
|
||||
util.request_electrum_rpc(coin, 'setconfig', ['fee_per_kb', rate * 1000])
|
||||
raise Exception('Either rate or dynamic=True must be provided')
|
||||
|
||||
def get_electrum_balance(coin: ElectrumCoin) -> float:
|
||||
return float(util.request_electrum_rpc(coin, 'getbalance')['confirmed'])
|
||||
|
||||
def create_psbt(coin: ElectrumCoin, destination_address: str, unsigned = True) -> str:
|
||||
def get_electrum_unused_address(coin: ElectrumCoin) -> str:
|
||||
return util.request_electrum_rpc(coin, 'getunusedaddress')
|
||||
|
||||
def create_psbt(coin: ElectrumCoin, destination_address: str, fee_rate: int = None, unsigned = True) -> str:
|
||||
params = {
|
||||
'destination': destination_address,
|
||||
'amount': '!',
|
||||
'feerate': fee_rate,
|
||||
'unsigned': unsigned # This way we can get the input amounts
|
||||
}
|
||||
|
||||
@@ -51,14 +56,25 @@ def create_psbt(coin: ElectrumCoin, destination_address: str, unsigned = True) -
|
||||
def get_psbt_data(coin: ElectrumCoin, psbt: str) -> dict:
|
||||
return util.request_electrum_rpc(coin, 'deserialize', [psbt])
|
||||
|
||||
def get_total_psbt_fee(psbt_data: dict) -> float:
|
||||
def get_tx_output_amount(coin: ElectrumCoin, tx_id: str, output_index: int) -> int:
|
||||
serialized_tx = util.request_electrum_rpc(coin, 'gettransaction', [tx_id])
|
||||
tx = util.request_electrum_rpc(coin, 'deserialize', [serialized_tx])
|
||||
|
||||
for i, output in enumerate(tx['outputs']):
|
||||
if i == output_index:
|
||||
return output['value_sats']
|
||||
|
||||
raise Exception(f'Output {output_index} not found in {coin} transaction {tx_id}')
|
||||
|
||||
def get_total_psbt_fee(coin: ElectrumCoin, tx: dict) -> float:
|
||||
inputs_sum_sats = 0
|
||||
outputs_sum_sats = 0
|
||||
|
||||
for _input in psbt_data['inputs']:
|
||||
inputs_sum_sats += cast(int, _input['value_sats'])
|
||||
for _input in tx['inputs']:
|
||||
input_amount = get_tx_output_amount(coin, _input['prevout_hash'], _input['prevout_n'])
|
||||
inputs_sum_sats += input_amount
|
||||
|
||||
for _output in psbt_data['outputs']:
|
||||
for _output in tx['outputs']:
|
||||
outputs_sum_sats += cast(int, _output['value_sats'])
|
||||
|
||||
total_fee_sats = inputs_sum_sats - outputs_sum_sats
|
||||
@@ -83,6 +99,9 @@ def get_monero_balance() -> float:
|
||||
params = {'account_index': 0}
|
||||
return util.request_monero_rpc('get_balance', params)['unlocked_balance'] / 1000000000000
|
||||
|
||||
def get_monero_unused_address() -> str:
|
||||
return util.request_monero_rpc('get_address', {'account_index': 0})
|
||||
|
||||
def sweep_all_monero(address: str) -> None:
|
||||
params = {
|
||||
'account_index': 0,
|
||||
@@ -122,25 +141,34 @@ def attempt_electrum_autoforward(coin: ElectrumCoin):
|
||||
coin_upper = coin.upper()
|
||||
balance = get_electrum_balance(coin)
|
||||
|
||||
coin_to_min_send: dict[ElectrumCoin, float] = {
|
||||
'btc': MIN_BITCOIN_SEND_AMOUNT,
|
||||
'ltc': MIN_LITECOIN_SEND_AMOUNT,
|
||||
'ltc-mweb': MIN_LITECOIN_SEND_AMOUNT
|
||||
coin_to_min_send_mainnet: dict[ElectrumCoin, float] = {
|
||||
'btc': MIN_BITCOIN_SEND_AMOUNT_MAINNET,
|
||||
'ltc': MIN_LITECOIN_SEND_AMOUNT_MAINNET,
|
||||
'ltc-mweb': MIN_LITECOIN_SEND_AMOUNT_MAINNET
|
||||
}
|
||||
|
||||
min_send = coin_to_min_send[coin]
|
||||
coin_to_min_send_testnet: dict[ElectrumCoin, float] = {
|
||||
'btc': MIN_BITCOIN_SEND_AMOUNT_TESTNET,
|
||||
'ltc': MIN_LITECOIN_SEND_AMOUNT_TESTNET,
|
||||
'ltc-mweb': MIN_LITECOIN_SEND_AMOUNT_TESTNET
|
||||
}
|
||||
|
||||
if balance < MIN_BITCOIN_SEND_AMOUNT:
|
||||
min_send = coin_to_min_send_testnet[coin] if env.TESTNET == '1' else coin_to_min_send_mainnet[coin]
|
||||
|
||||
if balance < min_send:
|
||||
print(util.get_time(), f'Not enough {coin_upper} balance to autoforward. (Balance: {balance}, Min. send: {min_send})')
|
||||
return
|
||||
|
||||
try:
|
||||
fee_rate = get_fee_rate(coin)
|
||||
set_electrum_fee_rate(coin, fee_rate, dynamic=False)
|
||||
set_electrum_fee_rate(coin, fee_rate)
|
||||
except:
|
||||
set_electrum_fee_rate(coin, rate=0, dynamic=True)
|
||||
set_electrum_fee_rate(coin, dynamic=True)
|
||||
|
||||
address = get_new_kraken_address(coin if coin != 'ltc-mweb' else 'ltc')
|
||||
if env.TESTNET == '1':
|
||||
address = get_electrum_unused_address(coin)
|
||||
else:
|
||||
address = get_new_kraken_address(coin if coin != 'ltc-mweb' else 'ltc')
|
||||
|
||||
# Electrum-ltc doesn't support deserializing mweb transactions, so we can't check total fee
|
||||
if coin != 'ltc-mweb':
|
||||
@@ -155,8 +183,8 @@ def attempt_electrum_autoforward(coin: ElectrumCoin):
|
||||
|
||||
raise http_error
|
||||
|
||||
psbt_data = get_psbt_data(coin, psbt)
|
||||
total_fee = get_total_psbt_fee(psbt_data)
|
||||
tx = get_psbt_data(coin, psbt)
|
||||
total_fee = get_total_psbt_fee(coin, tx)
|
||||
amount = balance
|
||||
|
||||
if total_fee / amount * 100 > env.MAX_NETWORK_FEE_PERCENT:
|
||||
@@ -185,11 +213,17 @@ def attempt_electrum_autoforward(coin: ElectrumCoin):
|
||||
def attempt_monero_autoforward():
|
||||
balance = get_monero_balance()
|
||||
|
||||
if balance < MIN_MONERO_SEND_AMOUNT:
|
||||
print(util.get_time(), f'Not enough XMR balance to autoforward. (Balance: {balance}, Min Send: {MIN_MONERO_SEND_AMOUNT})')
|
||||
min_send = MIN_MONERO_SEND_AMOUNT_TESTNET if env.TESTNET == '1' else MIN_MONERO_SEND_AMOUNT_MAINNET
|
||||
|
||||
if balance < min_send:
|
||||
print(util.get_time(), f'Not enough XMR balance to autoforward. (Balance: {balance}, Min Send: {min_send})')
|
||||
return
|
||||
|
||||
address = get_new_kraken_address('xmr')
|
||||
if env.TESTNET == '1':
|
||||
address = get_monero_unused_address()
|
||||
else:
|
||||
address = get_new_kraken_address('xmr')
|
||||
|
||||
sweep_all_monero(address)
|
||||
print(util.get_time(), f'Autoforwarded {balance} XMR to {address}!')
|
||||
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
MIN_BITCOIN_SEND_AMOUNT = 0.0001
|
||||
MIN_LITECOIN_SEND_AMOUNT = 0.05
|
||||
MIN_MONERO_SEND_AMOUNT = 0.03
|
||||
MIN_BITCOIN_SEND_AMOUNT_MAINNET = 0.0001
|
||||
MIN_LITECOIN_SEND_AMOUNT_MAINNET = 0.05
|
||||
MIN_MONERO_SEND_AMOUNT_MAINNET = 0.03
|
||||
|
||||
MIN_BITCOIN_SEND_AMOUNT_TESTNET = 0.00001
|
||||
MIN_LITECOIN_SEND_AMOUNT_TESTNET = 0.00001
|
||||
MIN_MONERO_SEND_AMOUNT_TESTNET = 0.00001
|
||||
@@ -1,5 +1,7 @@
|
||||
import os
|
||||
|
||||
TESTNET = os.getenv('TESTNET', '0')
|
||||
|
||||
BITCOIN_ELECTRUM_RPC_URL = os.getenv('BITCOIN_ELECTRUM_RPC_URL', '')
|
||||
LITECOIN_ELECTRUM_RPC_URL = os.getenv('LITECOIN_ELECTRUM_RPC_URL', '')
|
||||
LITECOIN_MWEB_ELECTRUM_RPC_URL = os.getenv('LITECOIN_MWEB_ELECTRUM_RPC_URL', '')
|
||||
|
||||
@@ -1,24 +1,37 @@
|
||||
from typing import Literal
|
||||
from bip_utils import Bip39SeedGenerator, Bip84, Bip84Coins
|
||||
import traceback
|
||||
|
||||
import util
|
||||
import env
|
||||
|
||||
def get_xprv_from_mnemonic(coin: Literal['btc', 'ltc',], mnemonic: str ) -> str:
|
||||
coin_type = Bip84Coins.BITCOIN if coin == 'btc' else Bip84Coins.LITECOIN
|
||||
if env.TESTNET == '1':
|
||||
coin_type = Bip84Coins.BITCOIN_TESTNET if coin == 'btc' else Bip84Coins.LITECOIN_TESTNET
|
||||
else:
|
||||
coin_type = Bip84Coins.BITCOIN if coin == 'btc' else Bip84Coins.LITECOIN
|
||||
|
||||
seed_bytes = Bip39SeedGenerator(mnemonic).Generate()
|
||||
bip84_master_key = Bip84.FromSeed(seed_bytes, coin_type)
|
||||
zprv = bip84_master_key.Purpose().Coin().Account(0).PrivateKey().ToExtended()
|
||||
return zprv
|
||||
|
||||
def import_bitcoin_seed():
|
||||
xprv = get_xprv_from_mnemonic('btc', env.BITCOIN_WALLET_SEED)
|
||||
util.request_electrum_rpc('btc', 'restore', [xprv])
|
||||
# Only support electrum format for testnet
|
||||
if env.TESTNET == '1':
|
||||
key = env.BITCOIN_WALLET_SEED
|
||||
else:
|
||||
key = get_xprv_from_mnemonic('btc', env.BITCOIN_WALLET_SEED)
|
||||
|
||||
util.request_electrum_rpc('btc', 'restore', [key])
|
||||
|
||||
def import_litecoin_seed():
|
||||
xprv = get_xprv_from_mnemonic('ltc', env.LITECOIN_WALLET_SEED)
|
||||
util.request_electrum_rpc('ltc', 'restore', [xprv])
|
||||
# Only support electrum format for testnet
|
||||
if env.TESTNET == '1':
|
||||
key = env.LITECOIN_WALLET_SEED
|
||||
else:
|
||||
key = get_xprv_from_mnemonic('ltc', env.LITECOIN_WALLET_SEED)
|
||||
|
||||
util.request_electrum_rpc('ltc', 'restore', [key])
|
||||
|
||||
def import_litecoin_mweb_seed():
|
||||
util.request_electrum_rpc('ltc-mweb', 'restore', [env.LITECOIN_MWEB_WALLET_SEED])
|
||||
@@ -43,8 +56,12 @@ try:
|
||||
util.request_electrum_rpc('btc', 'changegaplimit', [1000, 'iknowhatimdoing'])
|
||||
print('Bitcoin seed has successfully been imported!')
|
||||
except Exception as e:
|
||||
print(util.get_time(), 'Error importing bitcoin seed:')
|
||||
print(traceback.format_exc())
|
||||
if 'Remove the existing wallet first!' in e.__str__():
|
||||
print('Bitcoin wallet already exists. Skipping...')
|
||||
else:
|
||||
print(util.get_time(), 'Error importing bitcoin seed:')
|
||||
print(traceback.format_exc())
|
||||
|
||||
|
||||
try:
|
||||
import_litecoin_seed()
|
||||
@@ -52,8 +69,11 @@ try:
|
||||
util.request_electrum_rpc('ltc', 'changegaplimit', [1000, 'iknowhatimdoing'])
|
||||
print('Litecoin seed has successfully been imported!')
|
||||
except Exception as e:
|
||||
print(util.get_time(), 'Error importing litecoin seed:')
|
||||
print(traceback.format_exc())
|
||||
if 'Remove the existing wallet first!' in e.__str__():
|
||||
print('Bitcoin wallet already exists. Skipping...')
|
||||
else:
|
||||
print(util.get_time(), 'Error importing litecoin seed:')
|
||||
print(traceback.format_exc())
|
||||
|
||||
try:
|
||||
import_litecoin_mweb_seed()
|
||||
@@ -61,12 +81,18 @@ try:
|
||||
util.request_electrum_rpc('ltc-mweb', 'changegaplimit', [1000, 'iknowhatimdoing'])
|
||||
print('Litecoin mimblewimble seed has successfully been imported!')
|
||||
except Exception as e:
|
||||
print(util.get_time(), 'Error importing litecoin mimblewimble seed:')
|
||||
print(traceback.format_exc())
|
||||
if 'Remove the existing wallet first!' in e.__str__():
|
||||
print('Litecoin mimblewimble wallet already exists. Skipping...')
|
||||
else:
|
||||
print(util.get_time(), 'Error importing litecoin mimblewimble seed:')
|
||||
print(traceback.format_exc())
|
||||
|
||||
try:
|
||||
import_monero_seed()
|
||||
print('Monero seed has successfully been imported!')
|
||||
except Exception as e:
|
||||
print(util.get_time(), 'Error importing monero seed:')
|
||||
print(traceback.format_exc())
|
||||
if 'Remove the existing wallet first!' in e.__str__():
|
||||
print('Bitcoin wallet already exists. Skipping...')
|
||||
else:
|
||||
print(util.get_time(), 'Error importing monero seed:')
|
||||
print(traceback.format_exc())
|
||||
|
||||
10
src/util.py
10
src/util.py
@@ -9,7 +9,6 @@ import base64
|
||||
import hmac
|
||||
import json
|
||||
import time
|
||||
|
||||
import env
|
||||
|
||||
def get_time() -> str:
|
||||
@@ -109,6 +108,15 @@ def wait_for_rpc():
|
||||
request_electrum_rpc('ltc', 'getinfo')
|
||||
break
|
||||
except:
|
||||
|
||||
time.sleep(10)
|
||||
|
||||
while 1:
|
||||
try:
|
||||
request_electrum_rpc('ltc-mweb', 'getinfo')
|
||||
break
|
||||
except:
|
||||
|
||||
time.sleep(10)
|
||||
|
||||
print('Waiting for Monero RPC...')
|
||||
|
||||
Reference in New Issue
Block a user