Skip to content

Instantly share code, notes, and snippets.

@ethgr0wth
Last active December 13, 2025 22:19
Show Gist options
  • Select an option

  • Save ethgr0wth/5b21aef2bc3bdfa6791aa7cd2a6dcd18 to your computer and use it in GitHub Desktop.

Select an option

Save ethgr0wth/5b21aef2bc3bdfa6791aa7cd2a6dcd18 to your computer and use it in GitHub Desktop.
PSBT: Below is a minimal, production-safe version that does the following: python3 itc_cli.py <destination_address> <amount> No CSV, no shares, no batching, no previews — just: validate address create PSBT sign finalize broadcast Uses walletcreatefundedpsbt so fees come from change (exact send).
#!/usr/bin/env python3
import os
import sys
import argparse
from decimal import Decimal, ROUND_DOWN, getcontext
import requests
from requests.auth import HTTPBasicAuth
getcontext().prec = 28
SAT8 = Decimal("0.00000001")
# ---------- Helpers ----------
def quant8(x: Decimal) -> Decimal:
return x.quantize(SAT8, rounding=ROUND_DOWN)
def rpc_call(method, params, args):
url = f"http://{args.rpc_host}:{args.rpc_port}/wallet/{args.wallet}"
payload = {
"jsonrpc": "1.0",
"id": "send",
"method": method,
"params": params
}
auth = HTTPBasicAuth(args.rpc_user, args.rpc_pass)
r = requests.post(url, json=payload, auth=auth, timeout=30)
r.raise_for_status()
data = r.json()
if data.get("error"):
raise SystemExit(f"RPC error: {data['error']}")
return data["result"]
# ---------- Main ----------
def main():
ap = argparse.ArgumentParser(description="Send ITC via PSBT")
ap.add_argument("address", help="Destination ITC address")
ap.add_argument("amount", type=Decimal, help="Amount to send")
ap.add_argument("--wallet", default=os.getenv("RPC_WALLET", "default"))
ap.add_argument("--rpc-host", default="127.0.0.1")
ap.add_argument("--rpc-port", default="8332")
ap.add_argument("--rpc-user", default=os.getenv("RPC_USER"))
ap.add_argument("--rpc-pass", default=os.getenv("RPC_PASS"))
ap.add_argument("--rbf", action="store_true", help="Enable RBF")
ap.add_argument("--conf-target", type=int, default=6)
ap.add_argument("--fee-rate", type=Decimal, help="Optional fee rate (per kB)")
args = ap.parse_args()
amount = quant8(args.amount)
if amount <= 0:
raise SystemExit("Amount must be positive")
# Validate address
val = rpc_call("validateaddress", [args.address], args)
if not val.get("isvalid"):
raise SystemExit("Invalid destination address")
outputs = {args.address: float(amount)}
opts = {
"replaceable": args.rbf,
"change_type": "bech32"
}
if args.fee_rate:
opts["feeRate"] = float(args.fee_rate)
else:
opts["conf_target"] = args.conf_target
# Create PSBT
psbt = rpc_call(
"walletcreatefundedpsbt",
[[], outputs, 0, opts, True],
args
)
# Sign
processed = rpc_call(
"walletprocesspsbt",
[psbt["psbt"], True, "ALL", True],
args
)
# Finalize
final = rpc_call("finalizepsbt", [processed["psbt"]], args)
if not final.get("complete"):
raise SystemExit("PSBT not complete")
# Broadcast
txid = rpc_call("sendrawtransaction", [final["hex"]], args)
print("✅ Transaction sent")
print("TXID:", txid)
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment