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'/0'/0' is "sat" (default)
|
||||||
# m/0'/1'/0' is "msat"
|
# m/0'/1'/0' is "msat"
|
||||||
# m/0'/2'/0' is "usd"
|
# 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
|
# 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_DERIVATION_PATH_LIST=["m/0'/0'/0'", "m/0'/0'/1'", "m/0'/1'/0'", "m/0'/2'/0'"]
|
||||||
|
|
||||||
MINT_DATABASE=data/mint
|
MINT_DATABASE=data/mint
|
||||||
|
|
||||||
# Funding source backends
|
# Funding source backends
|
||||||
# Supported: FakeWallet, LndRestWallet, CoreLightningRestWallet, BlinkWallet, LNbitsWallet, StrikeUSDWallet
|
# Supported: FakeWallet, LndRestWallet, CoreLightningRestWallet, BlinkWallet, LNbitsWallet, StrikeWallet
|
||||||
MINT_BACKEND_BOLT11_SAT=FakeWallet
|
MINT_BACKEND_BOLT11_SAT=FakeWallet
|
||||||
# Only works if a usd derivation path is set
|
# Only works if a usd derivation path is set
|
||||||
# MINT_BACKEND_BOLT11_SAT=FakeWallet
|
# MINT_BACKEND_BOLT11_SAT=FakeWallet
|
||||||
@@ -78,7 +79,7 @@ MINT_CORELIGHTNING_REST_CERT="./clightning-2-rest/certificate.pem"
|
|||||||
# Use with BlinkWallet
|
# Use with BlinkWallet
|
||||||
MINT_BLINK_KEY=blink_abcdefgh
|
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
|
MINT_STRIKE_KEY=ABC123
|
||||||
|
|
||||||
# fee to reserve in percent of the amount
|
# fee to reserve in percent of the amount
|
||||||
|
|||||||
@@ -436,6 +436,8 @@ class Unit(Enum):
|
|||||||
sat = 0
|
sat = 0
|
||||||
msat = 1
|
msat = 1
|
||||||
usd = 2
|
usd = 2
|
||||||
|
eur = 3
|
||||||
|
btc = 4
|
||||||
|
|
||||||
def str(self, amount: int) -> str:
|
def str(self, amount: int) -> str:
|
||||||
if self == Unit.sat:
|
if self == Unit.sat:
|
||||||
@@ -444,6 +446,10 @@ class Unit(Enum):
|
|||||||
return f"{amount} msat"
|
return f"{amount} msat"
|
||||||
elif self == Unit.usd:
|
elif self == Unit.usd:
|
||||||
return f"${amount/100:.2f} 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:
|
else:
|
||||||
raise Exception("Invalid unit")
|
raise Exception("Invalid unit")
|
||||||
|
|
||||||
@@ -478,6 +484,33 @@ class Amount:
|
|||||||
else:
|
else:
|
||||||
return self
|
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:
|
def str(self) -> str:
|
||||||
return self.unit.str(self.amount)
|
return self.unit.str(self.amount)
|
||||||
|
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ class MintBackends(MintSettings):
|
|||||||
mint_lightning_backend: str = Field(default="") # deprecated
|
mint_lightning_backend: str = Field(default="") # deprecated
|
||||||
mint_backend_bolt11_sat: str = Field(default="")
|
mint_backend_bolt11_sat: str = Field(default="")
|
||||||
mint_backend_bolt11_usd: 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_endpoint: str = Field(default=None)
|
||||||
mint_lnbits_key: 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 .fake import FakeWallet # noqa: F401
|
||||||
from .lnbits import LNbitsWallet # noqa: F401
|
from .lnbits import LNbitsWallet # noqa: F401
|
||||||
from .lndrest import LndRestWallet # 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:
|
backend_settings = [
|
||||||
raise Exception("MINT_BACKEND_BOLT11_SAT or MINT_BACKEND_BOLT11_USD not set")
|
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,
|
32,
|
||||||
).hex()
|
).hex()
|
||||||
|
|
||||||
supported_units = set([Unit.sat, Unit.msat, Unit.usd])
|
supported_units = set([Unit.sat, Unit.msat, Unit.usd, Unit.eur])
|
||||||
unit = Unit.sat
|
unit = Unit.sat
|
||||||
|
|
||||||
supports_incoming_payment_stream: bool = True
|
supports_incoming_payment_stream: bool = True
|
||||||
@@ -113,7 +113,7 @@ class FakeWallet(LightningBackend):
|
|||||||
amount_msat = 0
|
amount_msat = 0
|
||||||
if self.unit == Unit.sat:
|
if self.unit == Unit.sat:
|
||||||
amount_msat = MilliSatoshi(amount.to(Unit.msat, round="up").amount)
|
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(
|
amount_msat = MilliSatoshi(
|
||||||
math.ceil(amount.amount / self.fake_btc_price * 1e9)
|
math.ceil(amount.amount / self.fake_btc_price * 1e9)
|
||||||
)
|
)
|
||||||
@@ -194,10 +194,10 @@ class FakeWallet(LightningBackend):
|
|||||||
fees_msat = fee_reserve(amount_msat)
|
fees_msat = fee_reserve(amount_msat)
|
||||||
fees = Amount(unit=Unit.msat, amount=fees_msat)
|
fees = Amount(unit=Unit.msat, amount=fees_msat)
|
||||||
amount = Amount(unit=Unit.msat, amount=amount_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_usd = math.ceil(invoice_obj.amount_msat / 1e9 * self.fake_btc_price)
|
||||||
amount = Amount(unit=Unit.usd, amount=amount_usd)
|
amount = Amount(unit=self.unit, amount=amount_usd)
|
||||||
fees = Amount(unit=Unit.usd, amount=2)
|
fees = Amount(unit=self.unit, amount=2)
|
||||||
else:
|
else:
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
|||||||
@@ -17,15 +17,17 @@ from .base import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class StrikeUSDWallet(LightningBackend):
|
class StrikeWallet(LightningBackend):
|
||||||
"""https://github.com/lnbits/lnbits"""
|
"""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.assert_unit_supported(unit)
|
||||||
self.unit = unit
|
self.unit = unit
|
||||||
self.endpoint = "https://api.strike.me"
|
self.endpoint = "https://api.strike.me"
|
||||||
|
self.currency = self.currency_map[self.unit]
|
||||||
|
|
||||||
# bearer auth with settings.mint_strike_key
|
# bearer auth with settings.mint_strike_key
|
||||||
bearer_auth = {
|
bearer_auth = {
|
||||||
@@ -57,8 +59,13 @@ class StrikeUSDWallet(LightningBackend):
|
|||||||
)
|
)
|
||||||
|
|
||||||
for balance in data:
|
for balance in data:
|
||||||
if balance["currency"] == "USD":
|
if balance["currency"] == self.currency:
|
||||||
return StatusResponse(error_message=None, balance=balance["total"])
|
return StatusResponse(
|
||||||
|
error_message=None,
|
||||||
|
balance=Amount.from_float(
|
||||||
|
float(balance["total"]), self.unit
|
||||||
|
).amount,
|
||||||
|
)
|
||||||
|
|
||||||
async def create_invoice(
|
async def create_invoice(
|
||||||
self,
|
self,
|
||||||
@@ -79,7 +86,7 @@ class StrikeUSDWallet(LightningBackend):
|
|||||||
payload = {
|
payload = {
|
||||||
"correlationId": secrets.token_hex(16),
|
"correlationId": secrets.token_hex(16),
|
||||||
"description": "Invoice for order 123",
|
"description": "Invoice for order 123",
|
||||||
"amount": {"amount": str(amount.amount / 100), "currency": "USD"},
|
"amount": {"amount": amount.to_float_string(), "currency": self.currency},
|
||||||
}
|
}
|
||||||
try:
|
try:
|
||||||
r = await self.client.post(url=f"{self.endpoint}/v1/invoices", json=payload)
|
r = await self.client.post(url=f"{self.endpoint}/v1/invoices", json=payload)
|
||||||
@@ -126,7 +133,7 @@ class StrikeUSDWallet(LightningBackend):
|
|||||||
try:
|
try:
|
||||||
r = await self.client.post(
|
r = await self.client.post(
|
||||||
url=f"{self.endpoint}/v1/payment-quotes/lightning",
|
url=f"{self.endpoint}/v1/payment-quotes/lightning",
|
||||||
json={"sourceCurrency": "USD", "lnInvoice": bolt11},
|
json={"sourceCurrency": self.currency, "lnInvoice": bolt11},
|
||||||
timeout=None,
|
timeout=None,
|
||||||
)
|
)
|
||||||
r.raise_for_status()
|
r.raise_for_status()
|
||||||
@@ -135,11 +142,11 @@ class StrikeUSDWallet(LightningBackend):
|
|||||||
raise Exception(error_message)
|
raise Exception(error_message)
|
||||||
data = r.json()
|
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(
|
quote = PaymentQuoteResponse(
|
||||||
amount=Amount(Unit.usd, amount=amount_cent),
|
amount=amount,
|
||||||
checking_id=data.get("paymentQuoteId"),
|
checking_id=data.get("paymentQuoteId"),
|
||||||
fee=Amount(Unit.usd, 0),
|
fee=Amount(self.unit, 0),
|
||||||
)
|
)
|
||||||
return quote
|
return quote
|
||||||
|
|
||||||
|
|||||||
@@ -55,6 +55,11 @@ if settings.mint_backend_bolt11_usd:
|
|||||||
unit=Unit.usd
|
unit=Unit.usd
|
||||||
)
|
)
|
||||||
backends.setdefault(Method.bolt11, {})[Unit.usd] = backend_bolt11_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:
|
if not backends:
|
||||||
raise Exception("No backends are set.")
|
raise Exception("No backends are set.")
|
||||||
|
|
||||||
|
|||||||
@@ -26,8 +26,15 @@ class LedgerTasks(SupportsDb, SupportsBackends, SupportsEvents):
|
|||||||
asyncio.create_task(self.invoice_listener(backend))
|
asyncio.create_task(self.invoice_listener(backend))
|
||||||
|
|
||||||
async def invoice_listener(self, backend: LightningBackend) -> None:
|
async def invoice_listener(self, backend: LightningBackend) -> None:
|
||||||
async for checking_id in backend.paid_invoices_stream():
|
if backend.supports_incoming_payment_stream:
|
||||||
await self.invoice_callback_dispatcher(checking_id)
|
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:
|
async def invoice_callback_dispatcher(self, checking_id: str) -> None:
|
||||||
logger.debug(f"Invoice callback dispatcher: {checking_id}")
|
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"]
|
wallet: Wallet = ctx.obj["WALLET"]
|
||||||
await wallet.load_mint()
|
await wallet.load_mint()
|
||||||
await print_balance(ctx)
|
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)}.")
|
print(f"Requesting invoice for {wallet.unit.str(amount)}.")
|
||||||
# in case the user wants a specific split, we create a list of amounts
|
# in case the user wants a specific split, we create a list of amounts
|
||||||
optional_split = None
|
optional_split = None
|
||||||
@@ -546,7 +546,7 @@ async def send_command(
|
|||||||
include_fees: bool,
|
include_fees: bool,
|
||||||
):
|
):
|
||||||
wallet: Wallet = ctx.obj["WALLET"]
|
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:
|
if not nostr and not nopt:
|
||||||
await send(
|
await send(
|
||||||
wallet,
|
wallet,
|
||||||
|
|||||||
Reference in New Issue
Block a user