mirror of
https://github.com/believethehype/nostrdvm.git
synced 2025-06-29 23:10:36 +02:00
move cashu functions to cashu_utils, add test functions
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@ -166,3 +166,4 @@ outputs
|
|||||||
app_deploy.py
|
app_deploy.py
|
||||||
app.py
|
app.py
|
||||||
app_deploy.py
|
app_deploy.py
|
||||||
|
db/Cashu/wallet.sqlite3
|
||||||
|
@ -101,10 +101,7 @@ def check_nova_server_status(jobID, address) -> str | pd.DataFrame:
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
elif content_type == 'text/plain; charset=utf-8':
|
elif content_type == 'text/plain; charset=utf-8':
|
||||||
result = response.content.decode('utf-8')
|
return response.content.decode('utf-8')
|
||||||
# TODO: This should not be necessary?
|
|
||||||
result = result.replace(" ", "#").replace(" ", "").replace("#", " ")
|
|
||||||
return result
|
|
||||||
elif content_type == "application/x-zip-compressed":
|
elif content_type == "application/x-zip-compressed":
|
||||||
zf = zipfile.ZipFile(io.BytesIO(response.content), "r")
|
zf = zipfile.ZipFile(io.BytesIO(response.content), "r")
|
||||||
|
|
||||||
|
@ -13,7 +13,8 @@ from utils.definitions import EventDefinitions
|
|||||||
from utils.nip89_utils import nip89_fetch_events_pubkey, NIP89Config
|
from utils.nip89_utils import nip89_fetch_events_pubkey, NIP89Config
|
||||||
from utils.nostr_utils import send_event
|
from utils.nostr_utils import send_event
|
||||||
from utils.output_utils import PostProcessFunctionType, post_process_list_to_users, post_process_list_to_events
|
from utils.output_utils import PostProcessFunctionType, post_process_list_to_users, post_process_list_to_events
|
||||||
from utils.zap_utils import parse_zap_event_tags, pay_bolt11_ln_bits, zap, redeem_cashu
|
from utils.zap_utils import parse_zap_event_tags, pay_bolt11_ln_bits, zap
|
||||||
|
from utils.cashu_utils import redeem_cashu
|
||||||
|
|
||||||
|
|
||||||
class Bot:
|
class Bot:
|
||||||
|
@ -14,8 +14,9 @@ from utils.backend_utils import get_amount_per_task, check_task_is_supported, ge
|
|||||||
from utils.database_utils import create_sql_table, get_or_add_user, update_user_balance, update_sql_table
|
from utils.database_utils import create_sql_table, get_or_add_user, update_user_balance, update_sql_table
|
||||||
from utils.mediasource_utils import input_data_file_duration
|
from utils.mediasource_utils import input_data_file_duration
|
||||||
from utils.nostr_utils import get_event_by_id, get_referenced_event_by_id, send_event, check_and_decrypt_tags
|
from utils.nostr_utils import get_event_by_id, get_referenced_event_by_id, send_event, check_and_decrypt_tags
|
||||||
from utils.output_utils import post_process_result, build_status_reaction
|
from utils.output_utils import build_status_reaction
|
||||||
from utils.zap_utils import check_bolt11_ln_bits_is_paid, create_bolt11_ln_bits, parse_zap_event_tags, redeem_cashu
|
from utils.zap_utils import check_bolt11_ln_bits_is_paid, create_bolt11_ln_bits, parse_zap_event_tags
|
||||||
|
from utils.cashu_utils import redeem_cashu
|
||||||
|
|
||||||
use_logger = False
|
use_logger = False
|
||||||
if use_logger:
|
if use_logger:
|
||||||
|
@ -52,3 +52,6 @@ typing_extensions==4.8.0
|
|||||||
tzdata==2023.3
|
tzdata==2023.3
|
||||||
urllib3==2.1.0
|
urllib3==2.1.0
|
||||||
wcwidth==0.2.10
|
wcwidth==0.2.10
|
||||||
|
--use-pep517
|
||||||
|
secp256k1
|
||||||
|
cashu
|
||||||
|
177
utils/cashu_utils.py
Normal file
177
utils/cashu_utils.py
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
import asyncio
|
||||||
|
import base64
|
||||||
|
import json
|
||||||
|
import requests
|
||||||
|
from utils.database_utils import get_or_add_user
|
||||||
|
from utils.zap_utils import create_bolt11_ln_bits, create_bolt11_lud16
|
||||||
|
|
||||||
|
|
||||||
|
async def get_cashu_balance(url):
|
||||||
|
from cashu.wallet.wallet import Wallet
|
||||||
|
from cashu.core.settings import settings
|
||||||
|
|
||||||
|
|
||||||
|
settings.tor = False
|
||||||
|
wallet = await Wallet.with_db(
|
||||||
|
url=url,
|
||||||
|
db="db/Cashu",
|
||||||
|
)
|
||||||
|
await wallet.load_mint()
|
||||||
|
await wallet.load_proofs()
|
||||||
|
print("Cashu Wallet balance " + str(wallet.available_balance) + " sats")
|
||||||
|
mint_balances = await wallet.balance_per_minturl()
|
||||||
|
print(mint_balances)
|
||||||
|
|
||||||
|
|
||||||
|
async def mint_cashu_test(url, amount):
|
||||||
|
from cashu.wallet.wallet import Wallet
|
||||||
|
from cashu.core.settings import settings
|
||||||
|
|
||||||
|
|
||||||
|
settings.tor = False
|
||||||
|
wallet = await Wallet.with_db(
|
||||||
|
url=url,
|
||||||
|
db="db/Cashu",
|
||||||
|
)
|
||||||
|
await wallet.load_mint()
|
||||||
|
await wallet.load_proofs()
|
||||||
|
print("Wallet balance " + str(wallet.available_balance) + " sats")
|
||||||
|
mint_balances = await wallet.balance_per_minturl()
|
||||||
|
print(mint_balances)
|
||||||
|
# mint tokens into wallet, skip if wallet already has funds
|
||||||
|
|
||||||
|
#if wallet.available_balance <= 10:
|
||||||
|
# invoice = await wallet.request_mint(amount)
|
||||||
|
# input(f"Pay this invoice and press any button: {invoice.bolt11}\n")
|
||||||
|
# await wallet.mint(amount, id=invoice.id)
|
||||||
|
|
||||||
|
# create 10 sat token
|
||||||
|
proofs_to_send, _ = await wallet.split_to_send(wallet.proofs, amount, set_reserved=True)
|
||||||
|
token_str = await wallet.serialize_proofs(proofs_to_send)
|
||||||
|
print(token_str)
|
||||||
|
return token_str
|
||||||
|
|
||||||
|
async def receive_cashu_test(token_str):
|
||||||
|
from cashu.wallet.wallet import Wallet
|
||||||
|
from cashu.core.settings import settings
|
||||||
|
from cashu.core.base import TokenV3
|
||||||
|
|
||||||
|
token = TokenV3.deserialize(token_str)
|
||||||
|
print(token.token[0])
|
||||||
|
|
||||||
|
settings.tor = False
|
||||||
|
wallet = await Wallet.with_db(
|
||||||
|
url=token.token[0].mint,
|
||||||
|
db="db/Cashu",
|
||||||
|
)
|
||||||
|
|
||||||
|
await wallet.load_mint()
|
||||||
|
await wallet.load_proofs()
|
||||||
|
|
||||||
|
print(f"Wallet balance: {wallet.available_balance} sats")
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
await wallet.redeem(token.token[0].proofs)
|
||||||
|
print(f"Wallet balance: {wallet.available_balance} sats")
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def parse_cashu(cashu_token: str):
|
||||||
|
try:
|
||||||
|
prefix = "cashuA"
|
||||||
|
assert cashu_token.startswith(prefix), Exception(
|
||||||
|
f"Token prefix not valid. Expected {prefix}."
|
||||||
|
)
|
||||||
|
if not cashu_token.endswith("="):
|
||||||
|
cashu_token = str(cashu_token) + "=="
|
||||||
|
print(cashu_token)
|
||||||
|
token_base64 = cashu_token[len(prefix):].encode("utf-8")
|
||||||
|
cashu = json.loads(base64.urlsafe_b64decode(token_base64))
|
||||||
|
token = cashu["token"][0]
|
||||||
|
proofs = token["proofs"]
|
||||||
|
mint = token["mint"]
|
||||||
|
total_amount = 0
|
||||||
|
for proof in proofs:
|
||||||
|
total_amount += proof["amount"]
|
||||||
|
|
||||||
|
|
||||||
|
return proofs, mint, total_amount, None
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
return None, None, None, "Cashu Parser: " + str(e)
|
||||||
|
|
||||||
|
|
||||||
|
def redeem_cashu(cashu, config, client, required_amount=0, update_self=False) -> (bool, str, int, int):
|
||||||
|
proofs, mint, total_amount, message = parse_cashu(cashu)
|
||||||
|
if message is not None:
|
||||||
|
return False, message, 0, 0
|
||||||
|
|
||||||
|
estimated_fees = max(int(total_amount * 0.02), 3)
|
||||||
|
estimated_redeem_invoice_amount = total_amount - estimated_fees
|
||||||
|
|
||||||
|
# Not sure if this the best way to go, we first create an invoice that we send to the mint, we catch the fees
|
||||||
|
# for that invoice, and create another invoice with the amount without fees to melt.
|
||||||
|
if config.LNBITS_INVOICE_KEY != "":
|
||||||
|
invoice, paymenthash = create_bolt11_ln_bits(estimated_redeem_invoice_amount, config)
|
||||||
|
else:
|
||||||
|
|
||||||
|
user = get_or_add_user(db=config.DB, npub=config.PUBLIC_KEY,
|
||||||
|
client=client, config=config, update=update_self)
|
||||||
|
invoice = create_bolt11_lud16(user.lud16, estimated_redeem_invoice_amount)
|
||||||
|
print(invoice)
|
||||||
|
if invoice is None:
|
||||||
|
return False, "couldn't create invoice", 0, 0
|
||||||
|
|
||||||
|
url = mint + "/checkfees" # Melt cashu tokens at Mint
|
||||||
|
json_object = {"pr": invoice}
|
||||||
|
headers = {"Content-Type": "application/json; charset=utf-8"}
|
||||||
|
request_body = json.dumps(json_object).encode('utf-8')
|
||||||
|
request = requests.post(url, data=request_body, headers=headers)
|
||||||
|
tree = json.loads(request.text)
|
||||||
|
fees = tree["fee"]
|
||||||
|
print("Fees on this mint are " + str(fees) + " Sats")
|
||||||
|
redeem_invoice_amount = total_amount -fees
|
||||||
|
if redeem_invoice_amount < required_amount:
|
||||||
|
err = ("Token value (Payment: " + str(total_amount) + " Sats. Fees: " +
|
||||||
|
str(fees) + " Sats) below required amount of " + str(required_amount)
|
||||||
|
+ " Sats. Cashu token has not been claimed.")
|
||||||
|
print("[" + config.NIP89.NAME + "] " + err)
|
||||||
|
return False, err, 0, 0
|
||||||
|
|
||||||
|
if config.LNBITS_INVOICE_KEY != "":
|
||||||
|
invoice, paymenthash = create_bolt11_ln_bits(redeem_invoice_amount, config)
|
||||||
|
else:
|
||||||
|
|
||||||
|
user = get_or_add_user(db=config.DB, npub=config.PUBLIC_KEY,
|
||||||
|
client=client, config=config, update=update_self)
|
||||||
|
invoice = create_bolt11_lud16(user.lud16, redeem_invoice_amount)
|
||||||
|
print(invoice)
|
||||||
|
|
||||||
|
try:
|
||||||
|
url = mint + "/melt" # Melt cashu tokens at Mint
|
||||||
|
json_object = {"proofs": proofs, "pr": invoice}
|
||||||
|
headers = {"Content-Type": "application/json; charset=utf-8"}
|
||||||
|
request_body = json.dumps(json_object).encode('utf-8')
|
||||||
|
request = requests.post(url, data=request_body, headers=headers)
|
||||||
|
tree = json.loads(request.text)
|
||||||
|
print(request.text)
|
||||||
|
is_paid = tree["paid"] if tree.get("paid") else False
|
||||||
|
print(is_paid)
|
||||||
|
if is_paid:
|
||||||
|
print("cashu token redeemed")
|
||||||
|
return True, "success", redeem_invoice_amount, fees
|
||||||
|
else:
|
||||||
|
msg = tree.get("detail").split('.')[0].strip() if tree.get("detail") else None
|
||||||
|
print(msg)
|
||||||
|
return False, msg
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
|
||||||
|
return False, "", redeem_invoice_amount, fees
|
@ -8,8 +8,6 @@ from Crypto.Cipher import AES
|
|||||||
from Crypto.Util.Padding import pad
|
from Crypto.Util.Padding import pad
|
||||||
from bech32 import bech32_decode, convertbits, bech32_encode
|
from bech32 import bech32_decode, convertbits, bech32_encode
|
||||||
from nostr_sdk import nostr_sdk, PublicKey, SecretKey, Event, EventBuilder, Tag, Keys
|
from nostr_sdk import nostr_sdk, PublicKey, SecretKey, Event, EventBuilder, Tag, Keys
|
||||||
|
|
||||||
from utils.database_utils import get_or_add_user
|
|
||||||
from utils.dvmconfig import DVMConfig
|
from utils.dvmconfig import DVMConfig
|
||||||
from utils.nostr_utils import get_event_by_id, check_and_decrypt_own_tags
|
from utils.nostr_utils import get_event_by_id, check_and_decrypt_own_tags
|
||||||
import lnurl
|
import lnurl
|
||||||
@ -249,96 +247,6 @@ def zap(lud16: str, amount: int, content, zapped_event: Event, keys, dvm_config,
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def parse_cashu(cashu_token: str):
|
|
||||||
try:
|
|
||||||
prefix = "cashuA"
|
|
||||||
assert cashu_token.startswith(prefix), Exception(
|
|
||||||
f"Token prefix not valid. Expected {prefix}."
|
|
||||||
)
|
|
||||||
if not cashu_token.endswith("="):
|
|
||||||
cashu_token = str(cashu_token) + "=="
|
|
||||||
print(cashu_token)
|
|
||||||
token_base64 = cashu_token[len(prefix):].encode("utf-8")
|
|
||||||
cashu = json.loads(base64.urlsafe_b64decode(token_base64))
|
|
||||||
token = cashu["token"][0]
|
|
||||||
proofs = token["proofs"]
|
|
||||||
mint = token["mint"]
|
|
||||||
total_amount = 0
|
|
||||||
for proof in proofs:
|
|
||||||
total_amount += proof["amount"]
|
|
||||||
|
|
||||||
|
|
||||||
return proofs, mint, total_amount, None
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(e)
|
|
||||||
return None, None, None, "Cashu Parser: " + str(e)
|
|
||||||
|
|
||||||
|
|
||||||
def redeem_cashu(cashu, config, client, required_amount=0, update_self=False) -> (bool, str, int, int):
|
|
||||||
proofs, mint, total_amount, message = parse_cashu(cashu)
|
|
||||||
if message is not None:
|
|
||||||
return False, message, 0, 0
|
|
||||||
|
|
||||||
# Not sure if this the best way to go, we first create an invoice that we send to the mint, we catch the fees
|
|
||||||
# for that invoice, and create another invoice with the amount without fees to melt.
|
|
||||||
if config.LNBITS_INVOICE_KEY != "":
|
|
||||||
invoice, paymenthash = create_bolt11_ln_bits(total_amount, config)
|
|
||||||
else:
|
|
||||||
|
|
||||||
user = get_or_add_user(db=config.DB, npub=config.PUBLIC_KEY,
|
|
||||||
client=client, config=config, update=update_self)
|
|
||||||
invoice = create_bolt11_lud16(user.lud16, total_amount)
|
|
||||||
print(invoice)
|
|
||||||
if invoice is None:
|
|
||||||
return False, "couldn't create invoice", 0, 0
|
|
||||||
|
|
||||||
url = mint + "/checkfees" # Melt cashu tokens at Mint
|
|
||||||
json_object = {"pr": invoice}
|
|
||||||
headers = {"Content-Type": "application/json; charset=utf-8"}
|
|
||||||
request_body = json.dumps(json_object).encode('utf-8')
|
|
||||||
request = requests.post(url, data=request_body, headers=headers)
|
|
||||||
tree = json.loads(request.text)
|
|
||||||
fees = tree["fee"]
|
|
||||||
print("Fees on this mint are " + str(fees) + " Sats")
|
|
||||||
redeem_invoice_amount = total_amount -fees
|
|
||||||
if redeem_invoice_amount < required_amount:
|
|
||||||
err = ("Token value (Payment: " + str(total_amount) + " Sats. Fees: " +
|
|
||||||
str(fees) + " Sats) below required amount of " + str(required_amount)
|
|
||||||
+ " Sats. Cashu token has not been claimed.")
|
|
||||||
print("[" + config.NIP89.NAME + "] " + err)
|
|
||||||
return False, err, 0, 0
|
|
||||||
|
|
||||||
if config.LNBITS_INVOICE_KEY != "":
|
|
||||||
invoice, paymenthash = create_bolt11_ln_bits(redeem_invoice_amount, config)
|
|
||||||
else:
|
|
||||||
|
|
||||||
user = get_or_add_user(db=config.DB, npub=config.PUBLIC_KEY,
|
|
||||||
client=client, config=config, update=update_self)
|
|
||||||
invoice = create_bolt11_lud16(user.lud16, redeem_invoice_amount)
|
|
||||||
print(invoice)
|
|
||||||
|
|
||||||
try:
|
|
||||||
url = mint + "/melt" # Melt cashu tokens at Mint
|
|
||||||
json_object = {"proofs": proofs, "pr": invoice}
|
|
||||||
headers = {"Content-Type": "application/json; charset=utf-8"}
|
|
||||||
request_body = json.dumps(json_object).encode('utf-8')
|
|
||||||
request = requests.post(url, data=request_body, headers=headers)
|
|
||||||
tree = json.loads(request.text)
|
|
||||||
print(request.text)
|
|
||||||
is_paid = tree["paid"] if tree.get("paid") else False
|
|
||||||
print(is_paid)
|
|
||||||
if is_paid:
|
|
||||||
print("cashu token redeemed")
|
|
||||||
return True, "success", redeem_invoice_amount, fees
|
|
||||||
else:
|
|
||||||
msg = tree.get("detail").split('.')[0].strip() if tree.get("detail") else None
|
|
||||||
print(msg)
|
|
||||||
return False, msg
|
|
||||||
except Exception as e:
|
|
||||||
print(e)
|
|
||||||
|
|
||||||
return False, "", redeem_invoice_amount, fees
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user