mirror of
https://github.com/MAGICGrants/autoforward-autoconvert.git
synced 2026-01-06 20:23:52 -05:00
More fixes and improvements
This commit is contained in:
@@ -2,12 +2,14 @@ BITCOIN_ELECTRUM_RPC_URL="http://btc-electrum:7000"
|
||||
BITCOIN_ELECTRUM_RPC_USERNAME="user"
|
||||
BITCOIN_ELECTRUM_RPC_PASSWORD=""
|
||||
LITECOIN_ELECTRUM_RPC_URL="http://ltc-electrum:7000"
|
||||
LITECOIN_MWEB_ELECTRUM_RPC_URL="http://ltc-mweb-electrum:7000"
|
||||
LITECOIN_ELECTRUM_RPC_USERNAME="user"
|
||||
LITECOIN_ELECTRUM_RPC_PASSWORD=""
|
||||
BITCOIN_ELECTRUM_SERVER_ADDRESS=""
|
||||
LITECOIN_ELECTRUM_SERVER_ADDRESS=""
|
||||
BITCOIN_WALLET_SEED=""
|
||||
LITECOIN_WALLET_SEED=""
|
||||
LITECOIN_MWEB_WALLET_SEED=""
|
||||
|
||||
MONERO_DAEMON_ADDRESS=""
|
||||
MONERO_RPC_URL=""
|
||||
@@ -17,10 +19,6 @@ MONERO_WALLET_SEED=""
|
||||
MONERO_WALLET_PASSWORD=""
|
||||
MONERO_WALLET_HEIGHT=""
|
||||
|
||||
NBXPLORER_URL=""
|
||||
NBXPLORER_USERNAME=""
|
||||
NBXPLORER_PASSWORD=""
|
||||
|
||||
KRAKEN_API_KEY=""
|
||||
KRAKEN_API_SECRET=""
|
||||
|
||||
|
||||
39
.github/workflows/deploy.yml
vendored
39
.github/workflows/deploy.yml
vendored
@@ -25,16 +25,33 @@ jobs:
|
||||
git pull
|
||||
echo "Starting..."
|
||||
|
||||
ELECTRUM_RPC_USERNAME="user" \
|
||||
ELECTRUM_RPC_PASSWORD="${{ secrets.ELECTRUM_RPC_PASSWORD }}" \
|
||||
BITCOIN_WALLET_SEED="${{ secrets.BITCOIN_WALLET_SEED }}" \
|
||||
MONERO_DAEMON_ADDRESS="${{ secrets.MONERO_DAEMON_ADDRESS }}" \
|
||||
MONERO_RPC_USERNAME="user" \
|
||||
MONERO_RPC_PASSWORD="${{ secrets.MONERO_RPC_PASSWORD }}" \
|
||||
MONERO_WALLET_SEED="${{ secrets.MONERO_WALLET_SEED }}" \
|
||||
MONERO_WALLET_PASSWORD="${{ secrets.MONERO_WALLET_PASSWORD }}" \
|
||||
MONERO_WALLET_HEIGHT="${{ secrets.MONERO_WALLET_HEIGHT }}" \
|
||||
KRAKEN_API_KEY="${{ secrets.KRAKEN_API_KEY }}" \
|
||||
KRAKEN_API_SECRET="${{ secrets.KRAKEN_API_SECRET }}" \
|
||||
BITCOIN_ELECTRUM_RPC_USERNAME=user \
|
||||
BITCOIN_ELECTRUM_RPC_PASSWORD=${{ secrets.BITCOIN_ELECTRUM_RPC_PASSWORD }} \
|
||||
LITECOIN_ELECTRUM_RPC_USERNAME=user \
|
||||
LITECOIN_ELECTRUM_RPC_PASSWORD=${{ secrets.LITECOIN_ELECTRUM_RPC_PASSWORD }} \
|
||||
BITCOIN_ELECTRUM_SERVER_ADDRESS=${{ secrets.BITCOIN_ELECTRUM_SERVER_ADDRESS }} \
|
||||
LITECOIN_ELECTRUM_SERVER_ADDRESS=${{ secrets.LITECOIN_ELECTRUM_SERVER_ADDRESS }} \
|
||||
BITCOIN_WALLET_SEED=${{ secrets.BITCOIN_WALLET_SEED }} \
|
||||
LITECOIN_WALLET_SEED=${{ secrets.LITECOIN_WALLET_SEED }} \
|
||||
LITECOIN_MWEB_WALLET_SEED=${{ secrets.LITECOIN_MWEB_WALLET_SEED }} \
|
||||
|
||||
MONERO_DAEMON_ADDRESS=${{ secrets.MONERO_DAEMON_ADDRESS }} \
|
||||
MONERO_RPC_URL=${{ secrets.MONERO_RPC_URL }} \
|
||||
MONERO_RPC_USERNAME=user \
|
||||
MONERO_RPC_PASSWORD=${{ secrets.MONERO_RPC_PASSWORD }} \
|
||||
MONERO_WALLET_SEED=${{ secrets.MONERO_WALLET_SEED }} \
|
||||
MONERO_WALLET_PASSWORD=${{ secrets.MONERO_WALLET_PASSWORD }} \
|
||||
MONERO_WALLET_HEIGHT=${{ secrets.MONERO_WALLET_HEIGHT }} \
|
||||
|
||||
KRAKEN_API_KEY=${{ secrets.KRAKEN_API_KEY }} \
|
||||
KRAKEN_API_SECRET=${{ secrets.KRAKEN_API_SECRET }} \
|
||||
|
||||
MAX_NETWORK_FEE_PERCENT=${{ secrets.MAX_NETWORK_FEE_PERCENT }} \
|
||||
MAX_SLIPPAGE_PERCENT=${{ secrets.MAX_SLIPPAGE_PERCENT }} \
|
||||
|
||||
BITCOIN_FEE_SOURCE=${{ secrets.BITCOIN_FEE_SOURCE }} \
|
||||
BITCOIN_FEE_RATE=${{ secrets.BITCOIN_FEE_RATE }} \
|
||||
LITECOIN_FEE_SOURCE=${{ secrets.LITECOIN_FEE_SOURCE }} \
|
||||
LITECOIN_FEE_RATE=${{ secrets.LITECOIN_FEE_RATE }} \
|
||||
docker compose up -d --build
|
||||
EOF
|
||||
|
||||
@@ -24,8 +24,8 @@ Create a `.env` file as a copy of `.env.example` and set the values for the empt
|
||||
| `KRAKEN_API_KEY` | Yes | - | Your API key from Kraken. |
|
||||
| `KRAKEN_API_SECRET` | Yes | - | Your API secret from Kraken. |
|
||||
| `MONERO_DAEMON_ADDRESS` | Yes | - | The address of a Monero daemon you own or trust. |
|
||||
| `BTC_ELECTRUM_SERVER_ADDRESS` | **No** | - | The address of a Bitcoin Electrum server you own or trust. E.g.: `localhost:50001:t` (no SSL) or `my.electrum.server:50001:s` (SSL). By leaving this blank you're letting Electrum select a random server for you, which may be a privacy concern. |
|
||||
| `LTC_ELECTRUM_SERVER_ADDRESS` | **No** | - | The address of a Litecoin Electrum server you own or trust. E.g.: `localhost:50001:t` (no SSL) or `my.electrum.server:50001:s` (SSL). By leaving this blank you're letting Electrum select a random server for you, which may be a privacy concern. |
|
||||
| `BITCOIN_ELECTRUM_SERVER_ADDRESS` | **No** | - | The address of a Bitcoin Electrum server you own or trust. E.g.: `localhost:50001:t` (no SSL) or `my.electrum.server:50001:s` (SSL). By leaving this blank you're letting Electrum select a random server for you, which may be a privacy concern. |
|
||||
| `LITECOIN_ELECTRUM_SERVER_ADDRESS` | **No** | - | The address of a Litecoin Electrum server you own or trust. E.g.: `localhost:50001:t` (no SSL) or `my.electrum.server:50001:s` (SSL). By leaving this blank you're letting Electrum select a random server for you, which may be a privacy concern. |
|
||||
| `MAX_NETWORK_FEE_PERCENT` | **No** | `5` | The maximum accepted miner fee percent when auto-forwarding. Not applied to XMR. |
|
||||
| `MAX_SLIPPAGE_PERCENT` | **No** | `0.5` | The maximum accepted slippage percent when auto-converting. |
|
||||
| `BITCOIN_FEE_SOURCE` | **No** | `https://mempool.space/api/v1/fees/recommended` | The fee API source to use for Bitcoin transactions. |
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
services:
|
||||
btc-electrum:
|
||||
build:
|
||||
context: ./btc-electrum
|
||||
context: ./docker/btc-electrum
|
||||
args:
|
||||
VERSION: "4.5.5"
|
||||
CHECKSUM_SHA512: "3bdfce2187466fff20fd67736bdf257bf95d3517de47043be411ccda558a442b8fd81d6a8da094a39a1db39a7339dcd4282e73a7f00cf6bbd70473d7ce456b0b"
|
||||
container_name: btc-electrum
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- btc-data:/home/electrum/.electrum
|
||||
environment:
|
||||
- ELECTRUM_RPC_USER=${BITCOIN_ELECTRUM_RPC_USERNAME}
|
||||
- ELECTRUM_RPC_PASSWORD=${BITCOIN_ELECTRUM_RPC_PASSWORD}
|
||||
@@ -14,19 +16,44 @@ services:
|
||||
|
||||
ltc-electrum:
|
||||
build:
|
||||
context: ./ltc-electrum
|
||||
context: ./docker/ltc-electrum
|
||||
args:
|
||||
VERSION: "release-9"
|
||||
CHECKSUM_SHA512: "bdf9edef38a34b087e4c70bb663ac111545d92b48aa4e2292438ebc93948ccc6f2c1b7e1b6384097ce13a8298919316e4b8863f0186764c0712245567aa4d73f"
|
||||
CHECKSUM_SHA512: "62248d5eba9b7d67facb767ff35706ef3e3dcd69c6b6fb8fb67b09bc07e52193ecd59f122388d401e854385b2e2b31fd802a9f5d56464472d893f5bc1bd394af"
|
||||
container_name: ltc-electrum
|
||||
restart: unless-stopped
|
||||
cap_add:
|
||||
- SYS_ADMIN
|
||||
devices:
|
||||
- /dev/fuse:/dev/fuse
|
||||
volumes:
|
||||
- ltc-data:/home/electrum-ltc/.electrum
|
||||
environment:
|
||||
- ELECTRUM_RPC_USER=${LITECOIN_ELECTRUM_RPC_USERNAME}
|
||||
- ELECTRUM_RPC_PASSWORD=${LITECOIN_ELECTRUM_RPC_PASSWORD}
|
||||
- ELECTRUM_SERVER_ADDRESS=${LITECOIN_ELECTRUM_SERVER_ADDRESS}
|
||||
|
||||
# ltc-mweb-electrum:
|
||||
# build:
|
||||
# context: ./docker/ltc-electrum
|
||||
# args:
|
||||
# VERSION: "release-9"
|
||||
# CHECKSUM_SHA512: "62248d5eba9b7d67facb767ff35706ef3e3dcd69c6b6fb8fb67b09bc07e52193ecd59f122388d401e854385b2e2b31fd802a9f5d56464472d893f5bc1bd394af"
|
||||
# container_name: ltc-mweb-electrum
|
||||
# restart: unless-stopped
|
||||
# cap_add:
|
||||
# - SYS_ADMIN
|
||||
# devices:
|
||||
# - "/dev/fuse"
|
||||
# volumes:
|
||||
# - ltc-mweb-data:/home/electrum-ltc/.electrum
|
||||
# environment:
|
||||
# - ELECTRUM_RPC_USER=${LITECOIN_ELECTRUM_RPC_USERNAME}
|
||||
# - ELECTRUM_RPC_PASSWORD=${LITECOIN_ELECTRUM_RPC_PASSWORD}
|
||||
# - ELECTRUM_SERVER_ADDRESS=${LITECOIN_ELECTRUM_SERVER_ADDRESS}
|
||||
|
||||
monero-wallet-rpc:
|
||||
image: ghcr.io/sethforprivacy/simple-monero-wallet-rpc:latest
|
||||
image: sethsimmons/simple-monero-wallet-rpc:latest
|
||||
restart: unless-stopped
|
||||
container_name: monero-wallet-rpc
|
||||
volumes:
|
||||
@@ -51,10 +78,12 @@ services:
|
||||
- LITECOIN_ELECTRUM_RPC_URL=${LITECOIN_ELECTRUM_RPC_URL}
|
||||
- LITECOIN_ELECTRUM_RPC_USERNAME=${LITECOIN_ELECTRUM_RPC_USERNAME}
|
||||
- LITECOIN_ELECTRUM_RPC_PASSWORD=${LITECOIN_ELECTRUM_RPC_PASSWORD}
|
||||
- LITECOIN_MWEB_ELECTRUM_RPC_URL=${LITECOIN_MWEB_ELECTRUM_RPC_URL}
|
||||
- BITCOIN_ELECTRUM_SERVER_ADDRESS=${BITCOIN_ELECTRUM_SERVER_ADDRESS}
|
||||
- LITECOIN_ELECTRUM_SERVER_ADDRESS=${LITECOIN_ELECTRUM_SERVER_ADDRESS}
|
||||
- BITCOIN_WALLET_SEED=${BITCOIN_WALLET_SEED}
|
||||
- LITECOIN_WALLET_SEED=${LITECOIN_WALLET_SEED}
|
||||
- LITECOIN_MWEB_WALLET_SEED=${LITECOIN_MWEB_WALLET_SEED}
|
||||
- MONERO_RPC_URL=http://monero-wallet-rpc:18082/json_rpc
|
||||
- MONERO_RPC_USERNAME=${MONERO_RPC_USERNAME}
|
||||
- MONERO_RPC_PASSWORD=${MONERO_RPC_PASSWORD}
|
||||
@@ -62,6 +91,8 @@ services:
|
||||
- MONERO_WALLET_PASSWORD=${MONERO_WALLET_PASSWORD}
|
||||
- MONERO_WALLET_HEIGHT=${MONERO_WALLET_HEIGHT}
|
||||
command: python ./src/seed-importer.py
|
||||
extra_hosts:
|
||||
- "host.docker.internal:host-gateway"
|
||||
depends_on:
|
||||
- btc-electrum
|
||||
- ltc-electrum
|
||||
@@ -78,21 +109,17 @@ services:
|
||||
- BITCOIN_ELECTRUM_RPC_USERNAME=${BITCOIN_ELECTRUM_RPC_USERNAME}
|
||||
- BITCOIN_ELECTRUM_RPC_PASSWORD=${BITCOIN_ELECTRUM_RPC_PASSWORD}
|
||||
- LITECOIN_ELECTRUM_RPC_URL=${LITECOIN_ELECTRUM_RPC_URL}
|
||||
- LITECOIN_MWEB_ELECTRUM_RPC_URL=${LITECOIN_MWEB_ELECTRUM_RPC_URL}
|
||||
- LITECOIN_ELECTRUM_RPC_USERNAME=${LITECOIN_ELECTRUM_RPC_USERNAME}
|
||||
- LITECOIN_ELECTRUM_RPC_PASSWORD=${LITECOIN_ELECTRUM_RPC_PASSWORD}
|
||||
- BITCOIN_ELECTRUM_SERVER_ADDRESS=${BITCOIN_ELECTRUM_SERVER_ADDRESS}
|
||||
- LITECOIN_ELECTRUM_SERVER_ADDRESS=${LITECOIN_ELECTRUM_SERVER_ADDRESS}
|
||||
- BITCOIN_WALLET_SEED=${BITCOIN_WALLET_SEED}
|
||||
- LITECOIN_WALLET_SEED=${LITECOIN_WALLET_SEED}
|
||||
- MONERO_RPC_URL=http://monero-wallet-rpc:18082/json_rpc
|
||||
- MONERO_RPC_USERNAME=${MONERO_RPC_USERNAME}
|
||||
- MONERO_RPC_PASSWORD=${MONERO_RPC_PASSWORD}
|
||||
- MONERO_WALLET_SEED=${MONERO_WALLET_SEED}
|
||||
- MONERO_WALLET_PASSWORD=${MONERO_WALLET_PASSWORD}
|
||||
- MONERO_WALLET_HEIGHT=${MONERO_WALLET_HEIGHT}
|
||||
- KRAKEN_API_KEY=${KRAKEN_API_KEY}
|
||||
- KRAKEN_API_SECRET=${KRAKEN_API_SECRET}
|
||||
command: python ./src/autoforward.py
|
||||
extra_hosts:
|
||||
- "host.docker.internal:host-gateway"
|
||||
depends_on:
|
||||
- seed-importer
|
||||
|
||||
@@ -110,4 +137,7 @@ services:
|
||||
- seed-importer
|
||||
|
||||
volumes:
|
||||
btc-data:
|
||||
ltc-data:
|
||||
ltc-mweb-data:
|
||||
monero-wallet-rpc-data:
|
||||
|
||||
@@ -6,7 +6,6 @@ ARG CHECKSUM_SHA512
|
||||
ENV ELECTRUM_VERSION=$VERSION
|
||||
ENV ELECTRUM_USER=electrum
|
||||
ENV ELECTRUM_HOME=/home/$ELECTRUM_USER
|
||||
ENV ELECTRUM_NETWORK=mainnet
|
||||
|
||||
RUN adduser -D $ELECTRUM_USER
|
||||
|
||||
@@ -35,8 +34,8 @@ RUN mkdir -p /data \
|
||||
COPY docker-entrypoint.sh /usr/local/bin/
|
||||
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
|
||||
|
||||
USER $BTC_ELECTRUM_USER
|
||||
WORKDIR $BTC_ELECTRUM_HOME
|
||||
USER $BITCOIN_ELECTRUM_USER
|
||||
WORKDIR $BITCOIN_ELECTRUM_HOME
|
||||
VOLUME /data
|
||||
EXPOSE 7000
|
||||
|
||||
@@ -3,7 +3,7 @@ set -ex
|
||||
|
||||
trap 'pkill -TERM -P1; electrum stop; exit 0' SIGTERM
|
||||
|
||||
rm -f .electrum/daemon
|
||||
rm -f .electrum/daemon .electrum/daemon_rpc_socket
|
||||
electrum --offline setconfig rpcuser ${ELECTRUM_RPC_USER}
|
||||
electrum --offline setconfig rpcpassword ${ELECTRUM_RPC_PASSWORD}
|
||||
electrum --offline setconfig rpchost 0.0.0.0
|
||||
25
docker/ltc-electrum/Dockerfile
Normal file
25
docker/ltc-electrum/Dockerfile
Normal file
@@ -0,0 +1,25 @@
|
||||
FROM ubuntu:latest
|
||||
|
||||
ARG VERSION
|
||||
ARG CHECKSUM_SHA512
|
||||
|
||||
RUN apt update && \
|
||||
apt install -y wget libfuse2 fuse3
|
||||
|
||||
ENV ELECTRUM_VERSION=$VERSION
|
||||
ENV ELECTRUM_USER=electrum
|
||||
ENV ELECTRUM_HOME=/home/$ELECTRUM_USER
|
||||
ENV ELECTRUM_CHECKSUM_SHA512 $CHECKSUM_SHA512
|
||||
|
||||
RUN useradd -m $ELECTRUM_USER
|
||||
USER $ELECTRUM_USER
|
||||
WORKDIR $ELECTRUM_HOME
|
||||
|
||||
RUN wget https://github.com/ltcmweb/electrum-ltc/releases/download/${ELECTRUM_VERSION}/electrum-ltc-${ELECTRUM_VERSION}-x86_64.AppImage -O electrum-ltc.appimage
|
||||
RUN [ "${ELECTRUM_CHECKSUM_SHA512} electrum-ltc.appimage" = "$(sha512sum electrum-ltc.appimage)" ]
|
||||
RUN echo -e "**************************\n SHA 512 Checksum OK\n**************************"
|
||||
COPY --chown=$ELECTRUM_USER:$ELECTRUM_USER ./docker-entrypoint.sh docker-entrypoint.sh
|
||||
RUN chmod +x ./electrum-ltc.appimage ./docker-entrypoint.sh
|
||||
|
||||
EXPOSE 7000
|
||||
ENTRYPOINT [ "./docker-entrypoint.sh" ]
|
||||
18
docker/ltc-electrum/docker-entrypoint.sh
Normal file
18
docker/ltc-electrum/docker-entrypoint.sh
Normal file
@@ -0,0 +1,18 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
trap 'pkill -TERM -P1; electrum-ltc.appimage stop; exit 0' SIGTERM
|
||||
|
||||
rm -f $HOME/.electrum-ltc/daemon
|
||||
./electrum-ltc.appimage --offline setconfig rpcuser ${ELECTRUM_RPC_USER}
|
||||
./electrum-ltc.appimage --offline setconfig rpcpassword ${ELECTRUM_RPC_PASSWORD}
|
||||
./electrum-ltc.appimage --offline setconfig rpchost 0.0.0.0
|
||||
./electrum-ltc.appimage --offline setconfig rpcport 7000
|
||||
|
||||
if [ -n "${ELECTRUM_SERVER_ADDRESS}" ]; then
|
||||
./electrum-ltc.appimage daemon -1 -s "${ELECTRUM_SERVER_ADDRESS}" "$@"
|
||||
else
|
||||
./electrum-ltc.appimage daemon "$@"
|
||||
fi
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
FROM python:3.12-alpine
|
||||
|
||||
ARG VERSION
|
||||
ARG CHECKSUM_SHA512
|
||||
|
||||
ENV ELECTRUM_VERSION=$VERSION
|
||||
ENV ELECTRUM_USER=electrum
|
||||
ENV ELECTRUM_HOME=/home/$ELECTRUM_USER
|
||||
ENV ELECTRUM_NETWORK=mainnet
|
||||
|
||||
RUN adduser -D $ELECTRUM_USER
|
||||
|
||||
RUN mkdir -p /data ${ELECTRUM_HOME} && \
|
||||
ln -sf /data ${ELECTRUM_HOME}/.electrum && \
|
||||
chown ${ELECTRUM_USER} ${ELECTRUM_HOME}/.electrum /data
|
||||
|
||||
# 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 wget https://github.com/ltcmweb/electrum-ltc/archive/refs/tags/${ELECTRUM_VERSION}.tar.gz
|
||||
RUN [ "${ELECTRUM_CHECKSUM_SHA512} ${ELECTRUM_VERSION}.tar.gz" = "$(sha512sum ${ELECTRUM_VERSION}.tar.gz)" ]
|
||||
RUN echo -e "**************************\n SHA 512 Checksum OK\n**************************"
|
||||
RUN tar -xzf ${ELECTRUM_VERSION}.tar.gz
|
||||
RUN pip3 install cryptography google protobuf grpcio scrypt ${ELECTRUM_VERSION}.tar.gz
|
||||
RUN rm ${ELECTRUM_VERSION}.tar.gz
|
||||
RUN ln -s /usr/lib/libsecp256k1.so /usr/local/lib/python3.12/site-packages/electrum_ltc/libsecp256k1.so.0
|
||||
|
||||
RUN mkdir -p /data \
|
||||
${ELECTRUM_HOME}/.electrum/wallets/ \
|
||||
${ELECTRUM_HOME}/.electrum/testnet/wallets/ \
|
||||
${ELECTRUM_HOME}/.electrum/regtest/wallets/ \
|
||||
${ELECTRUM_HOME}/.electrum/simnet/wallets/ && \
|
||||
ln -sf ${ELECTRUM_HOME}/.electrum/ /data && \
|
||||
chown -R ${ELECTRUM_USER} ${ELECTRUM_HOME}/.electrum /data
|
||||
|
||||
COPY docker-entrypoint.sh /usr/local/bin/
|
||||
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
|
||||
|
||||
USER $ELECTRUM_USER
|
||||
WORKDIR $ELECTRUM_HOME
|
||||
VOLUME /data
|
||||
EXPOSE 7000
|
||||
|
||||
ENTRYPOINT ["docker-entrypoint.sh"]
|
||||
@@ -1,17 +0,0 @@
|
||||
#!/usr/bin/env sh
|
||||
set -ex
|
||||
|
||||
trap 'pkill -TERM -P1; electrum-ltc stop; exit 0' SIGTERM
|
||||
|
||||
rm -f .electrum/daemon
|
||||
electrum-ltc --offline setconfig rpcuser ${ELECTRUM_RPC_USER}
|
||||
electrum-ltc --offline setconfig rpcpassword ${ELECTRUM_RPC_PASSWORD}
|
||||
electrum-ltc --offline setconfig rpchost 0.0.0.0
|
||||
electrum-ltc --offline setconfig rpcport 7000
|
||||
|
||||
if [ -n "${LITECOIN_ELECTRUM_SERVER_ADDRESS}" ]; then
|
||||
electrum-ltc daemon -1 -s "${LITECOIN_ELECTRUM_SERVER_ADDRESS}" "$@"
|
||||
else
|
||||
electrum-ltc daemon "$@"
|
||||
fi
|
||||
|
||||
26
poetry.lock
generated
26
poetry.lock
generated
@@ -1,4 +1,4 @@
|
||||
# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand.
|
||||
# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand.
|
||||
|
||||
[[package]]
|
||||
name = "asn1crypto"
|
||||
@@ -6,6 +6,7 @@ version = "1.5.1"
|
||||
description = "Fast ASN.1 parser and serializer with definitions for private keys, public keys, certificates, CRL, OCSP, CMS, PKCS#3, PKCS#7, PKCS#8, PKCS#12, PKCS#5, X.509 and TSP"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "asn1crypto-1.5.1-py2.py3-none-any.whl", hash = "sha256:db4e40728b728508912cbb3d44f19ce188f218e9eba635821bb4b68564f8fd67"},
|
||||
{file = "asn1crypto-1.5.1.tar.gz", hash = "sha256:13ae38502be632115abf8a24cbe5f4da52e3b5231990aff31123c805306ccb9c"},
|
||||
@@ -17,6 +18,7 @@ version = "2.9.3"
|
||||
description = "Generation of mnemonics, seeds, private/public keys and addresses for different types of cryptocurrencies"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "bip_utils-2.9.3-py3-none-any.whl", hash = "sha256:ee26b8417a576c7f89b847da37316db01a5cece1994c1609d37fbeefb91ad45e"},
|
||||
{file = "bip_utils-2.9.3.tar.gz", hash = "sha256:72a8c95484b57e92311b0b2a3d5195b0ce4395c19a0b157d4a289e8b1300f48a"},
|
||||
@@ -41,6 +43,7 @@ version = "5.6.4"
|
||||
description = "CBOR (de)serializer with extensive tag support"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "cbor2-5.6.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c40c68779a363f47a11ded7b189ba16767391d5eae27fac289e7f62b730ae1fc"},
|
||||
{file = "cbor2-5.6.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c0625c8d3c487e509458459de99bf052f62eb5d773cc9fc141c6a6ea9367726d"},
|
||||
@@ -83,7 +86,7 @@ files = [
|
||||
|
||||
[package.extras]
|
||||
benchmarks = ["pytest-benchmark (==4.0.0)"]
|
||||
doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme (>=1.3.0)", "typing-extensions"]
|
||||
doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme (>=1.3.0)", "typing-extensions ; python_version < \"3.12\""]
|
||||
test = ["coverage (>=7)", "hypothesis", "pytest"]
|
||||
|
||||
[[package]]
|
||||
@@ -92,6 +95,7 @@ version = "2024.8.30"
|
||||
description = "Python package for providing Mozilla's CA Bundle."
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8"},
|
||||
{file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"},
|
||||
@@ -103,6 +107,7 @@ version = "1.17.1"
|
||||
description = "Foreign Function Interface for Python calling C code."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"},
|
||||
{file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"},
|
||||
@@ -182,6 +187,7 @@ version = "3.3.2"
|
||||
description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
|
||||
optional = false
|
||||
python-versions = ">=3.7.0"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"},
|
||||
{file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"},
|
||||
@@ -281,6 +287,7 @@ version = "20.0.0"
|
||||
description = "Cross-platform Python CFFI bindings for libsecp256k1"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "coincurve-20.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d559b22828638390118cae9372a1bb6f6594f5584c311deb1de6a83163a0919b"},
|
||||
{file = "coincurve-20.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:33d7f6ebd90fcc550f819f7f2cce2af525c342aac07f0ccda46ad8956ad9d99b"},
|
||||
@@ -347,6 +354,7 @@ version = "1.7"
|
||||
description = "CRC Generator"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "crcmod-1.7.tar.gz", hash = "sha256:dc7051a0db5f2bd48665a990d3ec1cc305a466a77358ca4492826f41f283601e"},
|
||||
]
|
||||
@@ -357,6 +365,7 @@ version = "0.19.0"
|
||||
description = "ECDSA cryptographic signature library (pure python)"
|
||||
optional = false
|
||||
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.6"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "ecdsa-0.19.0-py2.py3-none-any.whl", hash = "sha256:2cea9b88407fdac7bbeca0833b189e4c9c53f2ef1e1eaa29f6224dbc809b707a"},
|
||||
{file = "ecdsa-0.19.0.tar.gz", hash = "sha256:60eaad1199659900dd0af521ed462b793bbdf867432b3948e87416ae4caf6bf8"},
|
||||
@@ -375,6 +384,7 @@ version = "1.4.1"
|
||||
description = "Ed25519 public-key signatures (BLAKE2b fork)"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "ed25519-blake2b-1.4.1.tar.gz", hash = "sha256:731e9f93cd1ac1a64649575f3519a99ffe0bb1e4cf7bf5f5f0be513a39df7363"},
|
||||
]
|
||||
@@ -385,6 +395,7 @@ version = "3.8"
|
||||
description = "Internationalized Domain Names in Applications (IDNA)"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "idna-3.8-py3-none-any.whl", hash = "sha256:050b4e5baadcd44d760cedbd2b8e639f2ff89bbc7a5730fcc662954303377aac"},
|
||||
{file = "idna-3.8.tar.gz", hash = "sha256:d838c2c0ed6fced7693d5e8ab8e734d5f8fda53a039c0164afb0b82e771e3603"},
|
||||
@@ -396,6 +407,7 @@ version = "0.2.0"
|
||||
description = "Python bindings for sr25519 library"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "py_sr25519_bindings-0.2.0-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:86cc1a571852a4f2ade827ebf211e066b23ab805d3e864cbe213a3d8cd53f7d5"},
|
||||
{file = "py_sr25519_bindings-0.2.0-cp310-cp310-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:453c9088e39dd04b07bf3ada6c473a5349c4dfd965009a35124b2c807117eda8"},
|
||||
@@ -477,6 +489,7 @@ version = "2.22"
|
||||
description = "C parser in Python"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"},
|
||||
{file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"},
|
||||
@@ -488,6 +501,7 @@ version = "3.20.0"
|
||||
description = "Cryptographic library for Python"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "pycryptodome-3.20.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:f0e6d631bae3f231d3634f91ae4da7a960f7ff87f2865b2d2b831af1dfb04e9a"},
|
||||
{file = "pycryptodome-3.20.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:baee115a9ba6c5d2709a1e88ffe62b73ecc044852a925dcb67713a288c4ec70f"},
|
||||
@@ -529,6 +543,7 @@ version = "1.5.0"
|
||||
description = "Python binding to the Networking and Cryptography (NaCl) library"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "PyNaCl-1.5.0-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:401002a4aaa07c9414132aaed7f6836ff98f59277a234704ff66878c2ee4a0d1"},
|
||||
{file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:52cb72a79269189d4e0dc537556f4740f7f0a9ec41c1322598799b0bdad4ef92"},
|
||||
@@ -555,6 +570,7 @@ version = "2.32.3"
|
||||
description = "Python HTTP for Humans."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"},
|
||||
{file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"},
|
||||
@@ -576,6 +592,7 @@ version = "1.16.0"
|
||||
description = "Python 2 and 3 compatibility utilities"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
|
||||
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
|
||||
@@ -587,18 +604,19 @@ version = "2.2.3"
|
||||
description = "HTTP library with thread-safe connection pooling, file post, and more."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "urllib3-2.2.3-py3-none-any.whl", hash = "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac"},
|
||||
{file = "urllib3-2.2.3.tar.gz", hash = "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"]
|
||||
brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""]
|
||||
h2 = ["h2 (>=4,<5)"]
|
||||
socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"]
|
||||
zstd = ["zstandard (>=0.18.0)"]
|
||||
|
||||
[metadata]
|
||||
lock-version = "2.0"
|
||||
lock-version = "2.1"
|
||||
python-versions = "^3.12"
|
||||
content-hash = "312bfc0d67e89feee1abbdbabc31649ff525b844d43eeaa06d57b9e9d26fbf06"
|
||||
|
||||
@@ -11,7 +11,6 @@ python = "^3.12"
|
||||
requests = "^2.32.3"
|
||||
bip-utils = "^2.9.3"
|
||||
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
|
||||
@@ -8,29 +8,46 @@ from constants import MIN_BITCOIN_SEND_AMOUNT, MIN_LITECOIN_SEND_AMOUNT, MIN_MON
|
||||
import util
|
||||
import env
|
||||
|
||||
def get_fee_rate(source: str, rate: str) -> int:
|
||||
ElectrumCoin = Literal['btc', 'ltc', 'ltc-mweb']
|
||||
|
||||
def get_fee_rate(coin: ElectrumCoin) -> int:
|
||||
coin_to_source: dict[ElectrumCoin, str] = {
|
||||
'btc': env.BITCOIN_FEE_SOURCE,
|
||||
'ltc': env.LITECOIN_FEE_SOURCE,
|
||||
'ltc-mweb': env.LITECOIN_FEE_SOURCE
|
||||
}
|
||||
|
||||
coin_to_rate: dict[ElectrumCoin, str] = {
|
||||
'btc': env.BITCOIN_FEE_RATE,
|
||||
'ltc': env.LITECOIN_FEE_RATE,
|
||||
'ltc-mweb': env.LITECOIN_FEE_RATE
|
||||
}
|
||||
|
||||
source = coin_to_source[coin]
|
||||
rate = coin_to_rate[coin]
|
||||
|
||||
return requests.get(source).json()[rate]
|
||||
|
||||
def set_electrum_fee_rate(coin: Literal['btc', 'ltc'], rate: int, dynamic: bool):
|
||||
def set_electrum_fee_rate(coin: ElectrumCoin, rate: int, dynamic: bool):
|
||||
if dynamic: # Fall back to the Electrum rate if there is an issue
|
||||
util.request_electrum_rpc(coin, 'setconfig', ['dynamic_fees', True])
|
||||
else:
|
||||
util.request_electrum_rpc(coin, 'setconfig', ['dynamic_fees', False])
|
||||
util.request_electrum_rpc(coin, 'setconfig', ['fee_per_kb', rate * 1000])
|
||||
|
||||
def get_electrum_balance(coin: Literal['btc', 'ltc']) -> float:
|
||||
def get_electrum_balance(coin: ElectrumCoin) -> float:
|
||||
return float(util.request_electrum_rpc(coin, 'getbalance')['confirmed'])
|
||||
|
||||
def create_psbt(coin: Literal['btc', 'ltc'], destination_address: str) -> str:
|
||||
def create_psbt(coin: ElectrumCoin, destination_address: str, unsigned = True) -> str:
|
||||
params = {
|
||||
'destination': destination_address,
|
||||
'amount': '!',
|
||||
'unsigned': True # This way we can get the input amounts
|
||||
'unsigned': unsigned # This way we can get the input amounts
|
||||
}
|
||||
|
||||
return util.request_electrum_rpc(coin, 'payto', params)
|
||||
|
||||
def get_psbt_data(coin: Literal['btc', 'ltc'], psbt: str) -> dict:
|
||||
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:
|
||||
@@ -47,10 +64,10 @@ def get_total_psbt_fee(psbt_data: dict) -> float:
|
||||
total_fee = total_fee_sats / 100000000
|
||||
return total_fee
|
||||
|
||||
def sign_psbt(coin: Literal['btc', 'ltc'], psbt: str) -> str:
|
||||
def sign_psbt(coin: ElectrumCoin, psbt: str) -> str:
|
||||
return cast(str, util.request_electrum_rpc(coin, 'signtransaction', [psbt]))
|
||||
|
||||
def broadcast_electrum_tx(coin: Literal['btc', 'ltc'], signed_tx: str):
|
||||
def broadcast_electrum_tx(coin: ElectrumCoin, signed_tx: str):
|
||||
util.request_electrum_rpc(coin, 'broadcast', [signed_tx])
|
||||
|
||||
def get_monero_balance() -> float:
|
||||
@@ -91,87 +108,62 @@ def get_new_kraken_address(asset: Literal['btc', 'ltc', 'xmr']) -> str:
|
||||
|
||||
raise Exception(f'Kraken did not return a new address: {json.dumps(result, indent=2)}')
|
||||
|
||||
def attempt_bitcoin_autoforward():
|
||||
balance = get_electrum_balance('btc')
|
||||
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
|
||||
}
|
||||
|
||||
min_send = coin_to_min_send[coin]
|
||||
|
||||
if balance < MIN_BITCOIN_SEND_AMOUNT:
|
||||
print(util.get_time(), f'Not enough Bitcoin balance to autoforward. (Balance: {balance}, Min Send: {MIN_BITCOIN_SEND_AMOUNT})')
|
||||
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(env.BITCOIN_FEE_SOURCE, env.BITCOIN_FEE_RATE)
|
||||
set_electrum_fee_rate('btc', fee_rate, dynamic=False)
|
||||
fee_rate = get_fee_rate(coin)
|
||||
set_electrum_fee_rate(coin, fee_rate, dynamic=False)
|
||||
except:
|
||||
set_electrum_fee_rate('btc', rate=0, dynamic=True)
|
||||
address = get_new_kraken_address('btc')
|
||||
set_electrum_fee_rate(coin, rate=0, dynamic=True)
|
||||
address = get_new_kraken_address(coin if coin != 'ltc-mweb' else 'ltc')
|
||||
|
||||
try:
|
||||
psbt = create_psbt('btc', address)
|
||||
except requests.exceptions.HTTPError as http_error:
|
||||
response_json = cast(dict, http_error.response.json())
|
||||
# Electrum-ltc doesn't support deserializing mweb transactions, so we can't check total fee
|
||||
if coin != 'ltc-mweb':
|
||||
try:
|
||||
psbt = create_psbt(coin, address)
|
||||
except requests.exceptions.HTTPError as http_error:
|
||||
response_json = cast(dict, http_error.response.json())
|
||||
|
||||
if response_json.get('error', {}).get('data', {}).get('exception', '') == 'NotEnoughFunds()':
|
||||
print(util.get_time(), f'Not autoforwarding due to high transaction fee.')
|
||||
return
|
||||
if response_json.get('error', {}).get('data', {}).get('exception', '') == 'NotEnoughFunds()':
|
||||
print(util.get_time(), f'Not autoforwarding due to high transaction fee.')
|
||||
return
|
||||
|
||||
raise http_error
|
||||
raise http_error
|
||||
|
||||
psbt_data = get_psbt_data('btc', psbt)
|
||||
total_fee = get_total_psbt_fee(psbt_data)
|
||||
amount = balance
|
||||
psbt_data = get_psbt_data(coin, psbt)
|
||||
total_fee = get_total_psbt_fee(psbt_data)
|
||||
amount = balance
|
||||
|
||||
if total_fee / amount * 100 > env.MAX_NETWORK_FEE_PERCENT:
|
||||
print(util.get_time(), f'Not autoforwarding due to high transaction fee {total_fee} BTC.')
|
||||
return
|
||||
if total_fee / amount * 100 > env.MAX_NETWORK_FEE_PERCENT:
|
||||
print(util.get_time(), f'Not autoforwarding due to high transaction fee ({total_fee} {coin_upper}).')
|
||||
return
|
||||
|
||||
signed_tx = sign_psbt('btc', psbt)
|
||||
broadcast_electrum_tx('btc', signed_tx)
|
||||
signed_tx = sign_psbt(coin, psbt)
|
||||
else:
|
||||
signed_tx = create_psbt(coin, address, unsigned=False)
|
||||
|
||||
print(util.get_time(), f'Autoforwarded {amount} BTC to {address}!')
|
||||
|
||||
def attempt_litecoin_autoforward():
|
||||
balance = get_electrum_balance('ltc')
|
||||
|
||||
if balance < MIN_LITECOIN_SEND_AMOUNT:
|
||||
print(util.get_time(), f'Not enough Litecoin balance to autoforward. (Balance: {balance}, Min Send: {MIN_LITECOIN_SEND_AMOUNT})')
|
||||
return
|
||||
|
||||
try:
|
||||
fee_rate = get_fee_rate(env.LITECOIN_FEE_SOURCE, env.LITECOIN_FEE_RATE)
|
||||
set_electrum_fee_rate('ltc', fee_rate, dynamic=False)
|
||||
except:
|
||||
set_electrum_fee_rate('ltc', rate=0, dynamic=True)
|
||||
address = get_new_kraken_address('ltc')
|
||||
|
||||
try:
|
||||
psbt = create_psbt('ltc', address)
|
||||
except requests.exceptions.HTTPError as http_error:
|
||||
response_json = cast(dict, http_error.response.json())
|
||||
|
||||
if response_json.get('error', {}).get('data', {}).get('exception', '') == 'NotEnoughFunds()':
|
||||
print(util.get_time(), f'Not autoforwarding due to high transaction fee.')
|
||||
return
|
||||
|
||||
raise http_error
|
||||
|
||||
psbt_data = get_psbt_data('ltc', psbt)
|
||||
total_fee = get_total_psbt_fee(psbt_data)
|
||||
amount = balance
|
||||
|
||||
if total_fee / amount * 100 > env.MAX_NETWORK_FEE_PERCENT:
|
||||
print(util.get_time(), f'Not autoforwarding due to high transaction fee {total_fee} LTC.')
|
||||
return
|
||||
|
||||
signed_tx = sign_psbt('ltc', psbt)
|
||||
broadcast_electrum_tx('ltc', signed_tx)
|
||||
|
||||
print(util.get_time(), f'Autoforwarded {amount} LTC to {address}!')
|
||||
broadcast_electrum_tx(coin, signed_tx)
|
||||
print(util.get_time(), f'Autoforwarded {amount} {coin_upper} to {address}!')
|
||||
|
||||
def attempt_monero_autoforward():
|
||||
balance = get_monero_balance()
|
||||
|
||||
if balance < MIN_MONERO_SEND_AMOUNT:
|
||||
print(util.get_time(), f'Not enough Monero balance to autoforward. (Balance: {balance}, Min Send: {MIN_MONERO_SEND_AMOUNT})')
|
||||
print(util.get_time(), f'Not enough XMR balance to autoforward. (Balance: {balance}, Min Send: {MIN_MONERO_SEND_AMOUNT})')
|
||||
return
|
||||
|
||||
address = get_new_kraken_address('xmr')
|
||||
@@ -183,21 +175,27 @@ util.wait_for_wallets()
|
||||
|
||||
while 1:
|
||||
try:
|
||||
attempt_bitcoin_autoforward()
|
||||
attempt_electrum_autoforward('btc')
|
||||
except Exception as e:
|
||||
print(util.get_time(), 'Error autoforwarding bitcoin:')
|
||||
print(util.get_time(), 'Error autoforwarding Bitcoin:')
|
||||
print(traceback.format_exc())
|
||||
|
||||
try:
|
||||
attempt_litecoin_autoforward()
|
||||
attempt_electrum_autoforward('ltc')
|
||||
except Exception as e:
|
||||
print(util.get_time(), 'Error autoforwarding litecoin:')
|
||||
print(traceback.format_exc())
|
||||
|
||||
# try:
|
||||
# attempt_electrum_autoforward('ltc-mweb')
|
||||
# except Exception as e:
|
||||
# print(util.get_time(), 'Error autoforwarding Litecoin MWEB:')
|
||||
# print(traceback.format_exc())
|
||||
|
||||
try:
|
||||
attempt_monero_autoforward()
|
||||
except Exception as e:
|
||||
print(util.get_time(), 'Error autoforwarding monero:')
|
||||
print(util.get_time(), 'Error autoforwarding Monero:')
|
||||
print(traceback.format_exc())
|
||||
|
||||
sleep(60 * 5)
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
MIN_BITCOIN_SEND_AMOUNT = 0.0001
|
||||
MIN_LITECOIN_SEND_AMOUNT = 0.05
|
||||
MIN_MONERO_SEND_AMOUNT = 0.03
|
||||
MIN_MONERO_SEND_AMOUNT = 0.03
|
||||
|
||||
@@ -6,10 +6,12 @@ BITCOIN_ELECTRUM_RPC_USERNAME = os.getenv('BITCOIN_ELECTRUM_RPC_USERNAME', '')
|
||||
BITCOIN_ELECTRUM_RPC_PASSWORD = os.getenv('BITCOIN_ELECTRUM_RPC_PASSWORD', '')
|
||||
LITECOIN_ELECTRUM_RPC_USERNAME = os.getenv('LITECOIN_ELECTRUM_RPC_USERNAME', '')
|
||||
LITECOIN_ELECTRUM_RPC_PASSWORD = os.getenv('LITECOIN_ELECTRUM_RPC_PASSWORD', '')
|
||||
LITECOIN_MWEB_ELECTRUM_RPC_URL = os.getenv('LITECOIN_MWEB_ELECTRUM_RPC_URL', '')
|
||||
BITCOIN_ELECTRUM_SERVER_ADDRESS = os.getenv('BITCOIN_ELECTRUM_SERVER_ADDRESS', '')
|
||||
LITECOIN_ELECTRUM_SERVER_ADDRESS = os.getenv('LITECOIN_ELECTRUM_SERVER_ADDRESS', '')
|
||||
BITCOIN_WALLET_SEED = os.getenv('BITCOIN_WALLET_SEED', '')
|
||||
LITECOIN_WALLET_SEED = os.getenv('LITECOIN_WALLET_SEED', '')
|
||||
LITECOIN_MWEB_WALLET_SEED = os.getenv('LITECOIN_MWEB_WALLET_SEED', '')
|
||||
|
||||
MONERO_RPC_URL = os.getenv('MONERO_RPC_URL', 'http://monero-wallet-rpc:18082/json_rpc')
|
||||
MONERO_RPC_USERNAME = os.getenv('MONERO_RPC_USERNAME', '')
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
from typing import Literal
|
||||
from bip_utils import Bip39SeedGenerator, Bip84, Bip84Coins
|
||||
import traceback
|
||||
|
||||
import util
|
||||
import env
|
||||
|
||||
def get_zprv_from_seed(coin: Literal['btc', 'ltc'], mnemonic: str ) -> str:
|
||||
def get_xprv_from_mnemonic(coin: Literal['btc', 'ltc',], mnemonic: str ) -> str:
|
||||
coin_type = Bip84Coins.BITCOIN if coin == 'btc' else Bip84Coins.LITECOIN
|
||||
seed_bytes = Bip39SeedGenerator(mnemonic).Generate()
|
||||
bip84_master_key = Bip84.FromSeed(seed_bytes, coin_type)
|
||||
@@ -12,12 +13,15 @@ def get_zprv_from_seed(coin: Literal['btc', 'ltc'], mnemonic: str ) -> str:
|
||||
return zprv
|
||||
|
||||
def import_bitcoin_seed():
|
||||
zprv = get_zprv_from_seed('btc', env.BITCOIN_WALLET_SEED)
|
||||
util.request_electrum_rpc('btc', 'restore', [zprv])
|
||||
xprv = get_xprv_from_mnemonic('btc', env.BITCOIN_WALLET_SEED)
|
||||
util.request_electrum_rpc('btc', 'restore', [xprv])
|
||||
|
||||
def import_litecoin_seed():
|
||||
zprv = get_zprv_from_seed('ltc', env.LITECOIN_WALLET_SEED)
|
||||
util.request_electrum_rpc('ltc', 'restore', [zprv])
|
||||
xprv = get_xprv_from_mnemonic('ltc', env.LITECOIN_WALLET_SEED)
|
||||
util.request_electrum_rpc('ltc', 'restore', [xprv])
|
||||
|
||||
def import_litecoin_mweb_seed():
|
||||
util.request_electrum_rpc('ltc-mweb', 'restore', [env.LITECOIN_MWEB_WALLET_SEED])
|
||||
|
||||
def import_monero_seed():
|
||||
params = {
|
||||
@@ -51,6 +55,15 @@ except Exception as e:
|
||||
print(util.get_time(), 'Error importing litecoin seed:')
|
||||
print(traceback.format_exc())
|
||||
|
||||
# try:
|
||||
# import_litecoin_mweb_seed()
|
||||
# util.request_electrum_rpc('ltc-mweb', 'load_wallet')
|
||||
# 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())
|
||||
|
||||
try:
|
||||
import_monero_seed()
|
||||
print('Monero seed has successfully been imported!')
|
||||
|
||||
33
src/util.py
33
src/util.py
@@ -15,13 +15,20 @@ import env
|
||||
def get_time() -> str:
|
||||
return f'[{datetime.now().strftime("%Y-%m-%d %H:%M:%S")}]'
|
||||
|
||||
def request_electrum_rpc(coin: Literal['btc', 'ltc'], method: str, params: list | dict = []):
|
||||
def request_electrum_rpc(coin: Literal['btc', 'ltc', 'ltc-mweb'], method: str, params: list | dict = []):
|
||||
headers = {'content-type': 'application/json'}
|
||||
|
||||
if coin == 'btc':
|
||||
auth = (env.BITCOIN_ELECTRUM_RPC_USERNAME, env.BITCOIN_ELECTRUM_RPC_PASSWORD)
|
||||
else:
|
||||
auth = (env.LITECOIN_ELECTRUM_RPC_USERNAME, env.LITECOIN_ELECTRUM_RPC_PASSWORD)
|
||||
coin_to_auth = {
|
||||
'btc': (env.BITCOIN_ELECTRUM_RPC_USERNAME, env.BITCOIN_ELECTRUM_RPC_PASSWORD),
|
||||
'ltc': (env.LITECOIN_ELECTRUM_RPC_USERNAME, env.LITECOIN_ELECTRUM_RPC_PASSWORD),
|
||||
'ltc-mweb': (env.LITECOIN_ELECTRUM_RPC_USERNAME, env.LITECOIN_ELECTRUM_RPC_PASSWORD)
|
||||
}
|
||||
|
||||
coin_to_url = {
|
||||
'btc': env.BITCOIN_ELECTRUM_RPC_URL,
|
||||
'ltc': env.LITECOIN_ELECTRUM_RPC_URL,
|
||||
'ltc-mweb': env.LITECOIN_MWEB_ELECTRUM_RPC_URL
|
||||
}
|
||||
|
||||
data = {
|
||||
'jsonrpc': '2.0',
|
||||
@@ -31,10 +38,10 @@ def request_electrum_rpc(coin: Literal['btc', 'ltc'], method: str, params: list
|
||||
}
|
||||
|
||||
response = requests.post(
|
||||
env.BITCOIN_ELECTRUM_RPC_URL if coin == 'btc' else env.LITECOIN_ELECTRUM_RPC_URL,
|
||||
coin_to_url[coin],
|
||||
headers=headers,
|
||||
data=json.dumps(data),
|
||||
auth=auth
|
||||
auth=coin_to_auth[coin]
|
||||
)
|
||||
|
||||
response_json = response.json()
|
||||
@@ -78,6 +85,9 @@ def open_bitcoin_wallet():
|
||||
def open_litecoin_wallet():
|
||||
request_electrum_rpc('ltc', 'load_wallet')
|
||||
|
||||
def open_litecoin_mweb_wallet():
|
||||
request_electrum_rpc('ltc-mweb', 'load_wallet')
|
||||
|
||||
def open_monero_wallet() -> None:
|
||||
params = {'filename': 'foo', 'password': env.MONERO_WALLET_PASSWORD}
|
||||
request_monero_rpc('open_wallet', params)
|
||||
@@ -130,6 +140,15 @@ def wait_for_wallets():
|
||||
break
|
||||
except:
|
||||
time.sleep(10)
|
||||
|
||||
print('Waiting for Litecoin MWEB wallet...')
|
||||
|
||||
while 1:
|
||||
try:
|
||||
open_litecoin_mweb_wallet()
|
||||
break
|
||||
except:
|
||||
time.sleep(10)
|
||||
|
||||
print('Waiting for Monero wallet...')
|
||||
|
||||
|
||||
Reference in New Issue
Block a user