mirror of
https://github.com/aljazceru/nutshell.git
synced 2025-12-23 19:54:18 +01:00
[Mint] Add support for BTC and EUR in StrikeWallet backend, add EUR to FakeWallet (#561)
* strike for btc and eur * strike works with eur * backend check
This commit is contained in:
@@ -50,13 +50,14 @@ MINT_DERIVATION_PATH="m/0'/0'/0'"
|
||||
# m/0'/0'/0' is "sat" (default)
|
||||
# m/0'/1'/0' is "msat"
|
||||
# m/0'/2'/0' is "usd"
|
||||
# m/0'/3'/0' is "eur"
|
||||
# In this example, we have 2 keysets for sat, 1 for msat and 1 for usd
|
||||
# MINT_DERIVATION_PATH_LIST=["m/0'/0'/0'", "m/0'/0'/1'", "m/0'/1'/0'", "m/0'/2'/0'"]
|
||||
|
||||
MINT_DATABASE=data/mint
|
||||
|
||||
# Funding source backends
|
||||
# Supported: FakeWallet, LndRestWallet, CoreLightningRestWallet, BlinkWallet, LNbitsWallet, StrikeUSDWallet
|
||||
# Supported: FakeWallet, LndRestWallet, CoreLightningRestWallet, BlinkWallet, LNbitsWallet, StrikeWallet
|
||||
MINT_BACKEND_BOLT11_SAT=FakeWallet
|
||||
# Only works if a usd derivation path is set
|
||||
# MINT_BACKEND_BOLT11_SAT=FakeWallet
|
||||
@@ -78,7 +79,7 @@ MINT_CORELIGHTNING_REST_CERT="./clightning-2-rest/certificate.pem"
|
||||
# Use with BlinkWallet
|
||||
MINT_BLINK_KEY=blink_abcdefgh
|
||||
|
||||
# Use with StrikeUSDWallet (usd only, does not currently support sats/msats)
|
||||
# Use with StrikeWallet for BTC, USD, and EUR
|
||||
MINT_STRIKE_KEY=ABC123
|
||||
|
||||
# fee to reserve in percent of the amount
|
||||
|
||||
@@ -436,6 +436,8 @@ class Unit(Enum):
|
||||
sat = 0
|
||||
msat = 1
|
||||
usd = 2
|
||||
eur = 3
|
||||
btc = 4
|
||||
|
||||
def str(self, amount: int) -> str:
|
||||
if self == Unit.sat:
|
||||
@@ -444,6 +446,10 @@ class Unit(Enum):
|
||||
return f"{amount} msat"
|
||||
elif self == Unit.usd:
|
||||
return f"${amount/100:.2f} USD"
|
||||
elif self == Unit.eur:
|
||||
return f"{amount/100:.2f} EUR"
|
||||
elif self == Unit.btc:
|
||||
return f"{amount/1e8:.8f} BTC"
|
||||
else:
|
||||
raise Exception("Invalid unit")
|
||||
|
||||
@@ -478,6 +484,33 @@ class Amount:
|
||||
else:
|
||||
return self
|
||||
|
||||
def to_float_string(self) -> str:
|
||||
if self.unit == Unit.usd or self.unit == Unit.eur:
|
||||
return self.cents_to_usd()
|
||||
elif self.unit == Unit.sat:
|
||||
return self.sat_to_btc()
|
||||
else:
|
||||
raise Exception("Amount must be in satoshis or cents")
|
||||
|
||||
@classmethod
|
||||
def from_float(cls, amount: float, unit: Unit) -> "Amount":
|
||||
if unit == Unit.usd or unit == Unit.eur:
|
||||
return cls(unit, int(amount * 100))
|
||||
elif unit == Unit.sat:
|
||||
return cls(unit, int(amount * 1e8))
|
||||
else:
|
||||
raise Exception("Amount must be in satoshis or cents")
|
||||
|
||||
def sat_to_btc(self) -> str:
|
||||
if self.unit != Unit.sat:
|
||||
raise Exception("Amount must be in satoshis")
|
||||
return f"{self.amount/1e8:.8f}"
|
||||
|
||||
def cents_to_usd(self) -> str:
|
||||
if self.unit != Unit.usd and self.unit != Unit.eur:
|
||||
raise Exception("Amount must be in cents")
|
||||
return f"{self.amount/100:.2f}"
|
||||
|
||||
def str(self) -> str:
|
||||
return self.unit.str(self.amount)
|
||||
|
||||
|
||||
@@ -67,6 +67,7 @@ class MintBackends(MintSettings):
|
||||
mint_lightning_backend: str = Field(default="") # deprecated
|
||||
mint_backend_bolt11_sat: str = Field(default="")
|
||||
mint_backend_bolt11_usd: str = Field(default="")
|
||||
mint_backend_bolt11_eur: str = Field(default="")
|
||||
|
||||
mint_lnbits_endpoint: str = Field(default=None)
|
||||
mint_lnbits_key: str = Field(default=None)
|
||||
|
||||
@@ -5,7 +5,14 @@ from .corelightningrest import CoreLightningRestWallet # noqa: F401
|
||||
from .fake import FakeWallet # noqa: F401
|
||||
from .lnbits import LNbitsWallet # noqa: F401
|
||||
from .lndrest import LndRestWallet # noqa: F401
|
||||
from .strike import StrikeUSDWallet # noqa: F401
|
||||
from .strike import StrikeWallet # noqa: F401
|
||||
|
||||
if settings.mint_backend_bolt11_sat is None or settings.mint_backend_bolt11_usd is None:
|
||||
raise Exception("MINT_BACKEND_BOLT11_SAT or MINT_BACKEND_BOLT11_USD not set")
|
||||
backend_settings = [
|
||||
settings.mint_backend_bolt11_sat,
|
||||
settings.mint_backend_bolt11_usd,
|
||||
settings.mint_backend_bolt11_eur,
|
||||
]
|
||||
if all([s is None for s in backend_settings]):
|
||||
raise Exception(
|
||||
"MINT_BACKEND_BOLT11_SAT or MINT_BACKEND_BOLT11_USD or MINT_BACKEND_BOLT11_EUR not set"
|
||||
)
|
||||
|
||||
@@ -45,7 +45,7 @@ class FakeWallet(LightningBackend):
|
||||
32,
|
||||
).hex()
|
||||
|
||||
supported_units = set([Unit.sat, Unit.msat, Unit.usd])
|
||||
supported_units = set([Unit.sat, Unit.msat, Unit.usd, Unit.eur])
|
||||
unit = Unit.sat
|
||||
|
||||
supports_incoming_payment_stream: bool = True
|
||||
@@ -113,7 +113,7 @@ class FakeWallet(LightningBackend):
|
||||
amount_msat = 0
|
||||
if self.unit == Unit.sat:
|
||||
amount_msat = MilliSatoshi(amount.to(Unit.msat, round="up").amount)
|
||||
elif self.unit == Unit.usd:
|
||||
elif self.unit == Unit.usd or self.unit == Unit.eur:
|
||||
amount_msat = MilliSatoshi(
|
||||
math.ceil(amount.amount / self.fake_btc_price * 1e9)
|
||||
)
|
||||
@@ -194,10 +194,10 @@ class FakeWallet(LightningBackend):
|
||||
fees_msat = fee_reserve(amount_msat)
|
||||
fees = Amount(unit=Unit.msat, amount=fees_msat)
|
||||
amount = Amount(unit=Unit.msat, amount=amount_msat)
|
||||
elif self.unit == Unit.usd:
|
||||
elif self.unit == Unit.usd or self.unit == Unit.eur:
|
||||
amount_usd = math.ceil(invoice_obj.amount_msat / 1e9 * self.fake_btc_price)
|
||||
amount = Amount(unit=Unit.usd, amount=amount_usd)
|
||||
fees = Amount(unit=Unit.usd, amount=2)
|
||||
amount = Amount(unit=self.unit, amount=amount_usd)
|
||||
fees = Amount(unit=self.unit, amount=2)
|
||||
else:
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
@@ -17,15 +17,17 @@ from .base import (
|
||||
)
|
||||
|
||||
|
||||
class StrikeUSDWallet(LightningBackend):
|
||||
"""https://github.com/lnbits/lnbits"""
|
||||
class StrikeWallet(LightningBackend):
|
||||
"""https://docs.strike.me/api/"""
|
||||
|
||||
supported_units = [Unit.usd]
|
||||
supported_units = [Unit.sat, Unit.usd, Unit.eur]
|
||||
currency_map = {Unit.sat: "BTC", Unit.usd: "USD", Unit.eur: "EUR"}
|
||||
|
||||
def __init__(self, unit: Unit = Unit.usd, **kwargs):
|
||||
def __init__(self, unit: Unit, **kwargs):
|
||||
self.assert_unit_supported(unit)
|
||||
self.unit = unit
|
||||
self.endpoint = "https://api.strike.me"
|
||||
self.currency = self.currency_map[self.unit]
|
||||
|
||||
# bearer auth with settings.mint_strike_key
|
||||
bearer_auth = {
|
||||
@@ -57,8 +59,13 @@ class StrikeUSDWallet(LightningBackend):
|
||||
)
|
||||
|
||||
for balance in data:
|
||||
if balance["currency"] == "USD":
|
||||
return StatusResponse(error_message=None, balance=balance["total"])
|
||||
if balance["currency"] == self.currency:
|
||||
return StatusResponse(
|
||||
error_message=None,
|
||||
balance=Amount.from_float(
|
||||
float(balance["total"]), self.unit
|
||||
).amount,
|
||||
)
|
||||
|
||||
async def create_invoice(
|
||||
self,
|
||||
@@ -79,7 +86,7 @@ class StrikeUSDWallet(LightningBackend):
|
||||
payload = {
|
||||
"correlationId": secrets.token_hex(16),
|
||||
"description": "Invoice for order 123",
|
||||
"amount": {"amount": str(amount.amount / 100), "currency": "USD"},
|
||||
"amount": {"amount": amount.to_float_string(), "currency": self.currency},
|
||||
}
|
||||
try:
|
||||
r = await self.client.post(url=f"{self.endpoint}/v1/invoices", json=payload)
|
||||
@@ -126,7 +133,7 @@ class StrikeUSDWallet(LightningBackend):
|
||||
try:
|
||||
r = await self.client.post(
|
||||
url=f"{self.endpoint}/v1/payment-quotes/lightning",
|
||||
json={"sourceCurrency": "USD", "lnInvoice": bolt11},
|
||||
json={"sourceCurrency": self.currency, "lnInvoice": bolt11},
|
||||
timeout=None,
|
||||
)
|
||||
r.raise_for_status()
|
||||
@@ -135,11 +142,11 @@ class StrikeUSDWallet(LightningBackend):
|
||||
raise Exception(error_message)
|
||||
data = r.json()
|
||||
|
||||
amount_cent = int(float(data.get("amount").get("amount")) * 100)
|
||||
amount = Amount.from_float(float(data.get("amount").get("amount")), self.unit)
|
||||
quote = PaymentQuoteResponse(
|
||||
amount=Amount(Unit.usd, amount=amount_cent),
|
||||
amount=amount,
|
||||
checking_id=data.get("paymentQuoteId"),
|
||||
fee=Amount(Unit.usd, 0),
|
||||
fee=Amount(self.unit, 0),
|
||||
)
|
||||
return quote
|
||||
|
||||
|
||||
@@ -55,6 +55,11 @@ if settings.mint_backend_bolt11_usd:
|
||||
unit=Unit.usd
|
||||
)
|
||||
backends.setdefault(Method.bolt11, {})[Unit.usd] = backend_bolt11_usd
|
||||
if settings.mint_backend_bolt11_eur:
|
||||
backend_bolt11_eur = getattr(wallets_module, settings.mint_backend_bolt11_eur)(
|
||||
unit=Unit.eur
|
||||
)
|
||||
backends.setdefault(Method.bolt11, {})[Unit.eur] = backend_bolt11_eur
|
||||
if not backends:
|
||||
raise Exception("No backends are set.")
|
||||
|
||||
|
||||
@@ -26,8 +26,15 @@ class LedgerTasks(SupportsDb, SupportsBackends, SupportsEvents):
|
||||
asyncio.create_task(self.invoice_listener(backend))
|
||||
|
||||
async def invoice_listener(self, backend: LightningBackend) -> None:
|
||||
async for checking_id in backend.paid_invoices_stream():
|
||||
await self.invoice_callback_dispatcher(checking_id)
|
||||
if backend.supports_incoming_payment_stream:
|
||||
while True:
|
||||
try:
|
||||
async for checking_id in backend.paid_invoices_stream():
|
||||
await self.invoice_callback_dispatcher(checking_id)
|
||||
except Exception as e:
|
||||
logger.error(f"Error in invoice listener: {e}")
|
||||
logger.info("Restarting invoice listener...")
|
||||
await asyncio.sleep(1)
|
||||
|
||||
async def invoice_callback_dispatcher(self, checking_id: str) -> None:
|
||||
logger.debug(f"Invoice callback dispatcher: {checking_id}")
|
||||
|
||||
@@ -262,7 +262,7 @@ async def invoice(ctx: Context, amount: float, id: str, split: int, no_check: bo
|
||||
wallet: Wallet = ctx.obj["WALLET"]
|
||||
await wallet.load_mint()
|
||||
await print_balance(ctx)
|
||||
amount = int(amount * 100) if wallet.unit == Unit.usd else int(amount)
|
||||
amount = int(amount * 100) if wallet.unit in [Unit.usd, Unit.eur] else int(amount)
|
||||
print(f"Requesting invoice for {wallet.unit.str(amount)}.")
|
||||
# in case the user wants a specific split, we create a list of amounts
|
||||
optional_split = None
|
||||
@@ -546,7 +546,7 @@ async def send_command(
|
||||
include_fees: bool,
|
||||
):
|
||||
wallet: Wallet = ctx.obj["WALLET"]
|
||||
amount = int(amount * 100) if wallet.unit == Unit.usd else int(amount)
|
||||
amount = int(amount * 100) if wallet.unit in [Unit.usd, Unit.eur] else int(amount)
|
||||
if not nostr and not nopt:
|
||||
await send(
|
||||
wallet,
|
||||
|
||||
Reference in New Issue
Block a user