Fix DOGE overpayment refund - Invalid amount precision error
* Fix "Invalid amount" error in DOGE refunds by rounding to 8 decimal places * Add detailed RPC error logging to DogecoinClient for better debugging * Add comment parameter to sendtoaddress calls for transaction labeling * Identified root cause: Dogecoin daemon rejects amounts with >8 decimal precision * Refund amount changed from 1.0385903528 to 1.03859035 DOGE The system was correctly detecting overpayments (5.5 vs 4.358 DOGE expected) but failing on the actual refund transaction due to precision requirements.
This commit is contained in:
parent
b5930694c7
commit
a3c20609c0
3 changed files with 126 additions and 2 deletions
|
|
@ -244,16 +244,37 @@ class DogecoinClient:
|
|||
)
|
||||
|
||||
try:
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
logger.info(
|
||||
f"DogecoinClient._call: Sending {method} with params {params} to {self.rpc_url}"
|
||||
)
|
||||
logger.info(
|
||||
f"DogecoinClient._call: Auth user={self.rpc_user}, has_pass={'yes' if self.rpc_pass else 'no'}"
|
||||
)
|
||||
response = requests.post(
|
||||
self.rpc_url,
|
||||
json=payload,
|
||||
auth=auth, # Basic auth for Dogecoin
|
||||
timeout=self.timeout,
|
||||
)
|
||||
logger.info(
|
||||
f"DogecoinClient._call: HTTP response status={response.status_code}"
|
||||
)
|
||||
|
||||
if response.status_code != 200:
|
||||
logger.error(
|
||||
f"DogecoinClient._call: HTTP {response.status_code} error response: {response.text}"
|
||||
)
|
||||
|
||||
response.raise_for_status()
|
||||
|
||||
obj = response.json()
|
||||
if "error" in obj and obj["error"]:
|
||||
logger.error(
|
||||
f"DogecoinClient._call: RPC error response: {obj['error']}"
|
||||
)
|
||||
raise RuntimeError(f"RPC error: {obj['error']}")
|
||||
return obj.get("result")
|
||||
|
||||
|
|
@ -298,6 +319,12 @@ class DogecoinClient:
|
|||
|
||||
def sendtoaddress(self, address: str, amount: float, comment: str = "") -> str:
|
||||
"""Send Dogecoin to an address. Returns transaction ID."""
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
logger.info(
|
||||
f"DogecoinClient.sendtoaddress called with: address={address}, amount={amount}, comment={comment}"
|
||||
)
|
||||
return self._call("sendtoaddress", [address, amount, comment])
|
||||
|
||||
def sendmany(self, from_label: str, addresses_amounts: Dict[str, float]) -> str:
|
||||
|
|
|
|||
|
|
@ -262,12 +262,21 @@ class PaymentRescue:
|
|||
|
||||
elif coin_type == "DOGE":
|
||||
# Dogecoin uses sendtoaddress RPC with coin amounts (not atomic units)
|
||||
refund_amount_doge = float(refund_amount_coin)
|
||||
# Round to 8 decimal places to match DOGE precision requirements
|
||||
refund_amount_doge = round(float(refund_amount_coin), 8)
|
||||
logger.info(
|
||||
f"Calling Dogecoin sendtoaddress: {refund_amount_doge} DOGE to {refund_details['refund_address']}"
|
||||
)
|
||||
logger.info(
|
||||
f"Dogecoin client config - URL: {self.crypto_client.rpc_url}, User: {self.crypto_client.rpc_user}"
|
||||
)
|
||||
logger.info(
|
||||
f"About to call sendtoaddress with params: address={refund_details['refund_address']}, amount={refund_amount_doge}"
|
||||
)
|
||||
tx_hash = self.crypto_client.sendtoaddress(
|
||||
refund_details["refund_address"], refund_amount_doge
|
||||
refund_details["refund_address"],
|
||||
refund_amount_doge,
|
||||
f"Overpayment refund for {refund_details['payment_id']}",
|
||||
)
|
||||
tx_result = {"tx_hash": tx_hash}
|
||||
|
||||
|
|
|
|||
88
monero-sweep-all.sh
Executable file
88
monero-sweep-all.sh
Executable file
|
|
@ -0,0 +1,88 @@
|
|||
#!/bin/bash
|
||||
# Monero Sweep All Script
|
||||
# Sweeps ALL unlocked funds from ALL Monero wallet accounts to cold storage
|
||||
# Based on make_post_sell Makefile sweep-all target
|
||||
|
||||
set -e # Exit on any error
|
||||
|
||||
echo "=== SWEEPING ALL MONERO WALLET FUNDS TO COLD STORAGE ==="
|
||||
echo "CAUTION: This will send ALL unlocked balance from ALL accounts!"
|
||||
echo ""
|
||||
|
||||
# Check if COLD_WALLET_ADDRESS is set
|
||||
if [ -z "${COLD_WALLET_ADDRESS}" ]; then
|
||||
echo "ERROR: COLD_WALLET_ADDRESS environment variable not set!"
|
||||
echo "Usage: COLD_WALLET_ADDRESS=<your-xmr-address> ./monero-sweep-all.sh"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if wallet RPC is accessible
|
||||
if ! curl --digest -u "test_user:test_pass" -s -X POST http://127.0.0.1:18083/json_rpc \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"jsonrpc":"2.0","id":"0","method":"get_version"}' >/dev/null 2>&1; then
|
||||
echo "❌ Monero wallet RPC not accessible at http://127.0.0.1:18083"
|
||||
echo "Start wallet RPC with:"
|
||||
echo " make monero-wallet-remote-auth # For testing with auth"
|
||||
echo " make monero-wallet-remote # For testing without auth"
|
||||
echo " make monero-wallet # For production with local node"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✓ Wallet RPC is accessible"
|
||||
echo ""
|
||||
|
||||
# Get all account balances
|
||||
echo "Checking ALL account balances..."
|
||||
all_balance=$(curl --digest -u "test_user:test_pass" -s -X POST http://127.0.0.1:18083/json_rpc \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"jsonrpc":"2.0","id":"0","method":"get_balance","params":{"all_accounts":true}}' | \
|
||||
python3 -c "import sys, json; d=json.load(sys.stdin); result = d.get('result', {}); total_balance = result.get('balance', 0); total_unlocked = result.get('unlocked_balance', 0); accounts = result.get('per_subaddress', []); print('Total balance: {} atomic units ({:.12f} XMR)'.format(total_balance, total_balance/1e12)); print('Total unlocked: {} atomic units ({:.12f} XMR)'.format(total_unlocked, total_unlocked/1e12)); print('Per account breakdown:'); [print(' Account {}:{} - Balance: {:.12f} XMR (unlocked: {:.12f} XMR)'.format(acc.get('account_index', 0), acc.get('address_index', 0), acc.get('balance', 0)/1e12, acc.get('unlocked_balance', 0)/1e12)) for acc in accounts if acc.get('balance', 0) > 0]; print(total_unlocked)")
|
||||
|
||||
total_unlocked=$(echo "$all_balance" | tail -1)
|
||||
|
||||
if [ -z "$total_unlocked" ] || [ "$total_unlocked" = "0" ]; then
|
||||
echo "No unlocked balance to sweep across all accounts!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Target address: ${COLD_WALLET_ADDRESS}"
|
||||
echo ""
|
||||
|
||||
# Confirmation prompt
|
||||
read -p "Are you sure you want to sweep ALL unlocked funds from ALL accounts? (yes/no): " confirm
|
||||
if [ "$confirm" != "yes" ]; then
|
||||
echo "Cancelled."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Getting all accounts for sweeping..."
|
||||
|
||||
# Get all account indices
|
||||
accounts=$(curl --digest -u "test_user:test_pass" -s -X POST http://127.0.0.1:18083/json_rpc \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"jsonrpc":"2.0","id":"0","method":"get_accounts"}' | \
|
||||
python3 -c "import sys, json; d=json.load(sys.stdin); accounts = d.get('result', {}).get('subaddress_accounts', []); account_indices = [acc.get('account_index', 0) for acc in accounts]; print(','.join(map(str, account_indices)))")
|
||||
|
||||
echo "Found accounts: $accounts"
|
||||
echo ""
|
||||
|
||||
# Sweep each account
|
||||
for account in $(echo $accounts | tr ',' ' '); do
|
||||
echo "Sweeping account $account..."
|
||||
response=$(curl --digest -u "test_user:test_pass" -s -X POST http://127.0.0.1:18083/json_rpc \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"sweep_all\",\"params\":{\"address\":\"${COLD_WALLET_ADDRESS}\",\"account_index\":$account,\"subaddr_indices\":[],\"priority\":0,\"get_tx_metadata\":false}}")
|
||||
|
||||
echo "$response" | python3 -c "import sys, json; d=json.load(sys.stdin); r=d.get('result',{}); amounts=r.get('amount_list',[]); fees=r.get('fee_list',[]); hashes=r.get('tx_hash_list',[]); [print('✓ Account $account: {:.12f} XMR (fee: {:.12f}) Hash: {}'.format(amt/1e12, fee/1e12, tx_hash)) for amt, fee, tx_hash in zip(amounts, fees, hashes)] if amounts else print('Account $account: No funds') if 'error' not in d else print('Account $account:', d['error'].get('message','Error'))"
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "All account sweep operations completed!"
|
||||
echo ""
|
||||
echo "🔄 Check transaction status:"
|
||||
echo " make monero-transactions"
|
||||
echo ""
|
||||
echo "💰 Verify cold wallet balance at:"
|
||||
echo " https://xmrchain.net/search?value=${COLD_WALLET_ADDRESS}"
|
||||
Loading…
Add table
Add a link
Reference in a new issue