add tutorials 8 and 9 - nutzaps

This commit is contained in:
Believethehype 2024-10-23 10:55:28 +02:00
parent cffeaae62f
commit 3dd7ecad9f
4 changed files with 148 additions and 8 deletions

View File

@ -563,9 +563,15 @@ class NutZapWallet:
break
# If that's not the case, lets look or mints we both trust, take the first one.
if not sufficent_budget:
mint_url = next(i for i in nut_wallet.mints if i in mints)
mint = self.get_mint(nut_wallet, mint_url)
if mint.available_balance() < amount:
try:
mint_url = next(i for i in nut_wallet.mints if i in mints)
if mint_url is not None:
mint = self.get_mint(nut_wallet, mint_url)
if mint.available_balance() < amount:
await self.handle_low_balance_on_mint(nut_wallet, mint_url, mint, amount, client, keys)
except StopIteration:
mint = self.get_mint(nut_wallet, mints[0])
mint_url = mint.mint_url
await self.handle_low_balance_on_mint(nut_wallet, mint_url, mint, amount, client, keys)
# If that's not the case, iterate over the recipents mints and try to mint there. This might be a bit dangerous as not all mints might give cashu, so loss of ln is possible

View File

@ -5,9 +5,6 @@
import asyncio
from pathlib import Path
from secp256k1 import PublicKey
from nostr_dvm.utils.dvmconfig import DVMConfig
from nostr_dvm.utils.print_utils import bcolors

View File

@ -21,6 +21,7 @@ from nostr_sdk import Kind, Keys
from nostr_dvm.utils.admin_utils import AdminConfig
from nostr_dvm.utils.dvmconfig import build_default_config, DVMConfig
from nostr_dvm.utils.nip89_utils import NIP89Config, check_and_set_d_tag
from nostr_dvm.utils.nut_wallet_utils import NutZapWallet
from nostr_dvm.utils.outbox_utils import AVOID_OUTBOX_RELAY_LIST
from nostr_dvm.utils.zap_utils import change_ln_address
@ -30,6 +31,7 @@ def run_dvm(identifier, announce):
dvm_config = build_default_config(identifier)
kind = Kind(5050)
dvm_config.KIND = kind
dvm_config.FIX_COST = 3
# Once you installed cashu, the rest is pretty staight forward:
@ -38,10 +40,14 @@ def run_dvm(identifier, announce):
# Define a relay to update your balance
dvm_config.NUTZAP_RELAYS = ["wss://relay.primal.net"]
# Define one or more mints you would like to receive on.
dvm_config.NUZAP_MINTS = ["https://mint.minibits.cash/Bitcoin", "https://mint.gwoq.com"]
dvm_config.NUZAP_MINTS = ["https://mint.gwoq.com"]
# If you want you can auto_melt cashu token to your lightning address once they pass a certain threshold.
dvm_config.ENABLE_AUTO_MELT = False
dvm_config.AUTO_MELT_AMOUNT = 1000
# If you update your mints in NUTZAP_MINTS make sure to reannounce these.
dvm_config.REANNOUNCE_MINTS = True
options = {
@ -67,7 +73,7 @@ def run_dvm(identifier, announce):
return result
dvm.process = process
dvm.run()
dvm.run(True)

View File

@ -0,0 +1,131 @@
# This is the complementary tutorial for tutorial 08. Sending nutzaps.
# Make sure tutorial 08 is running while testing this. Also make sure you lnbits config is set,
# or manually change the code to access a wallet to mint tokens.
# Check especially the code in line 76 and make sure to replace the npub with the one from
# the dvm in tutorial 8 in line 120.
from pathlib import Path
import dotenv
from nostr_sdk import PublicKey, Client, NostrSigner, EventBuilder, Kind, Tag, Keys, Timestamp, HandleNotification, \
Filter, Event
import asyncio
from nostr_dvm.utils.definitions import EventDefinitions
from nostr_dvm.utils.dvmconfig import DVMConfig
from nostr_dvm.utils.nostr_utils import send_event, check_and_set_private_key
from nostr_dvm.utils.nut_wallet_utils import NutZapWallet
from nostr_dvm.utils.print_utils import bcolors
# You already know this from our previous test client in tutorial 03: We simply send a kind 5050 request
# to return some text, just as before. Note: Our dvm in tutorial 08 requires a payment of 3 sats.
async def nostr_client_generic_test(ptag):
keys = Keys.parse(check_and_set_private_key("test_client"))
relay_list = ["wss://nostr.oxtr.dev", "wss://relay.primal.net"]
relaysTag = Tag.parse(["relays"] + relay_list)
alttag = Tag.parse(["alt", "This is a NIP90 DVM AI task"])
paramTag = Tag.parse(["param", "some_option", "#RUNDVM - With a Nutzap."])
pTag = Tag.parse(["p", PublicKey.parse(ptag).to_hex()])
tags = [relaysTag, alttag, pTag, paramTag]
event = EventBuilder(Kind(5050), "This is a test",
tags).to_event(keys)
signer = NostrSigner.keys(keys)
client = Client(signer)
for relay in relay_list:
await client.add_relay(relay)
await client.connect()
result = await send_event(event, client=client, dvm_config=DVMConfig())
print(result)
async def nostr_client(target_dvm_npub):
keys = Keys.parse(check_and_set_private_key("test_client"))
pk = keys.public_key()
print(f"Nostr Client public key: {pk.to_bech32()}, Hex: {pk.to_hex()} ")
signer = NostrSigner.keys(keys)
client = Client(signer)
dvmconfig = DVMConfig()
for relay in dvmconfig.RELAY_LIST:
await client.add_relay(relay)
await client.connect()
kinds = [EventDefinitions.KIND_NIP90_GENERIC]
for kind in range(6000, 7001):
if kind not in kinds:
kinds.append(Kind(kind))
dvm_filter = (Filter().kinds(kinds).since(Timestamp.now()).pubkey(pk))
await client.subscribe([dvm_filter], None)
# This will send a request to the DVM
await nostr_client_generic_test(target_dvm_npub)
# We listen to
class NotificationHandler(HandleNotification):
last_event_time = 0
async def handle(self, relay_url, subscription_id, event: Event):
if event.kind().as_u16() == 7000:
print(bcolors.YELLOW + "[Nostr Client]: " + event.content() + bcolors.ENDC)
amount_sats = 0
status = ""
for tag in event.tags():
if tag.as_vec()[0] == "amount":
amount_sats = int(int(tag.as_vec()[1]) / 1000) # millisats
if tag.as_vec()[0] == "status":
status = tag.as_vec()[1]
if status == "payment-required":
print("do a nutzap of " + str(amount_sats) +" sats here")
send_zap_amount = amount_sats
send_zap_message = "From my nutsack"
send_receiver = event.author().to_hex()
send_zapped_event = event.id().to_hex() # None # None, or zap an event like this: Nip19Event.from_nostr_uri("nostr:nevent1qqsxq59mhz8s6aj9jzltcmqmmv3eutsfcpkeny2x755vdu5dtq44ldqpz3mhxw309ucnydewxqhrqt338g6rsd3e9upzp75cf0tahv5z7plpdeaws7ex52nmnwgtwfr2g3m37r844evqrr6jqvzqqqqqqyqtxyr6").event_id().to_hex()
zapped_event_id_hex = send_zapped_event
nutzap_wallet = NutZapWallet()
nut_wallet = await nutzap_wallet.get_nut_wallet(client, keys)
if nut_wallet is None:
relays = DVMConfig().NUTZAP_RELAYS
mints = DVMConfig().NUZAP_MINTS
await nutzap_wallet.create_new_nut_wallet(mints, relays, client, keys, "Test", "My Nutsack")
nut_wallet = await nutzap_wallet.get_nut_wallet(client, keys)
if nut_wallet is not None:
await nutzap_wallet.announce_nutzap_info_event(nut_wallet, client, keys)
else:
print("Couldn't fetch wallet, please restart and see if it is there")
await nutzap_wallet.send_nut_zap(send_zap_amount, send_zap_message, nut_wallet, zapped_event_id_hex, send_receiver, client,
keys)
elif 6000 < event.kind().as_u16() < 6999:
print(bcolors.GREEN + "[Nostr Client]: " + event.content() + bcolors.ENDC)
async def handle_msg(self, relay_url, msg):
return
asyncio.create_task(client.handle_notifications(NotificationHandler()))
# await client.handle_notifications(NotificationHandler())
while True:
await asyncio.sleep(2)
if __name__ == '__main__':
env_path = Path('.env')
if env_path.is_file():
print(f'loading environment from {env_path.resolve()}')
dotenv.load_dotenv(env_path, verbose=True, override=True)
else:
raise FileNotFoundError(f'.env file not found at {env_path} ')
# Replace this key with the one from your DVM from part 8.
target_dvm_npub = "npub150laafgrw4dlj5pc7jk2qzhxkghtqn9pplsyg8u6jkpd9afaszzsu06p39"
asyncio.run(nostr_client(target_dvm_npub))