We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Tip Jar extension broken
Tip Jars are broken on v0.9.1

Setup:
- venv python based setup (python 3.9.13)
- LNBits v0.9.1
- Backend: PostgreSQL
- LND-Rest connection

Steps to reproduce: 
- add extension "Tip Jar"
- choose wallet
- do not choose onchain
- test tip jar, fill out donator and amount, hit submit
- error occurrs: `'NoneType' object has no attribute 'mempool_endpoint'`

debug log:
```
"POST /tipjar/api/v1/tips HTTP/1.1" 200
ERROR | lnbits.app:basic_error:182 | handled error
ERROR | lnbits.app:basic_error:183 | ERROR:
Traceback (most recent call last):
  File "/home/lnbits/lnbits-legend/venv/lib/python3.9/site-packages/starlette/middleware/errors.py", line 162, in __call__
    await self.app(scope, receive, _send)
  File "/home/lnbits/lnbits-legend/venv/lib/python3.9/site-packages/starlette/middleware/gzip.py", line 24, in __call__
    await responder(scope, receive, send)
  File "/home/lnbits/lnbits-legend/venv/lib/python3.9/site-packages/starlette/middleware/gzip.py", line 43, in __call__
    await self.app(scope, receive, self.send_with_gzip)
  File "/home/lnbits/lnbits-legend/venv/lib/python3.9/site-packages/starlette/middleware/cors.py", line 84, in __call__
    await self.app(scope, receive, send)
  File "/home/lnbits/lnbits-legend/venv/lib/python3.9/site-packages/starlette/exceptions.py", line 93, in __call__
    raise exc
  File "/home/lnbits/lnbits-legend/venv/lib/python3.9/site-packages/starlette/exceptions.py", line 82, in __call__
    await self.app(scope, receive, sender)
  File "/home/lnbits/lnbits-legend/venv/lib/python3.9/site-packages/fastapi/middleware/asyncexitstack.py", line 21, in __call__
    raise e
  File "/home/lnbits/lnbits-legend/venv/lib/python3.9/site-packages/fastapi/middleware/asyncexitstack.py", line 18, in __call__
    await self.app(scope, receive, send)
  File "/home/lnbits/lnbits-legend/venv/lib/python3.9/site-packages/starlette/routing.py", line 670, in __call__
    await route.handle(scope, receive, send)
  File "/home/lnbits/lnbits-legend/venv/lib/python3.9/site-packages/starlette/routing.py", line 266, in handle
    await self.app(scope, receive, send)
  File "/home/lnbits/lnbits-legend/venv/lib/python3.9/site-packages/starlette/routing.py", line 65, in app
    response = await func(request)
  File "/home/lnbits/lnbits-legend/venv/lib/python3.9/site-packages/fastapi/routing.py", line 227, in app
    raw_response = await run_endpoint_function(
  File "/home/lnbits/lnbits-legend/venv/lib/python3.9/site-packages/fastapi/routing.py", line 160, in run_endpoint_function
    return await dependant.call(**values)
  File "/home/lnbits/lnbits-legend/./lnbits/extensions/satspay/views.py", line 43, in display
    "mempool_endpoint": onchainwallet_config.mempool_endpoint,
AttributeError: 'NoneType' object has no attribute 'mempool_endpoint'
```

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `lnbits/extensions/tipjar/crud.py`
Content:
```
1 from typing import Optional
2 
3 from lnbits.db import SQLITE
4 
5 from ..satspay.crud import delete_charge  # type: ignore
6 from . import db
7 from .models import Tip, TipJar, createTipJar
8 
9 
10 async def create_tip(
11     id: int, wallet: str, message: str, name: str, sats: int, tipjar: str
12 ) -> Tip:
13     """Create a new Tip"""
14     await db.execute(
15         """
16         INSERT INTO tipjar.Tips (
17             id,
18             wallet,
19             name,
20             message,
21             sats,
22             tipjar
23         )
24         VALUES (?, ?, ?, ?, ?, ?)
25         """,
26         (id, wallet, name, message, sats, tipjar),
27     )
28 
29     tip = await get_tip(id)
30     assert tip, "Newly created tip couldn't be retrieved"
31     return tip
32 
33 
34 async def create_tipjar(data: createTipJar) -> TipJar:
35     """Create a new TipJar"""
36 
37     returning = "" if db.type == SQLITE else "RETURNING ID"
38     method = db.execute if db.type == SQLITE else db.fetchone
39 
40     result = await (method)(
41         f"""
42         INSERT INTO tipjar.TipJars (
43             name,
44             wallet,
45             webhook,
46             onchain
47         )
48         VALUES (?, ?, ?, ?)
49         {returning}
50         """,
51         (data.name, data.wallet, data.webhook, data.onchain),
52     )
53     if db.type == SQLITE:
54         tipjar_id = result._result_proxy.lastrowid
55     else:
56         tipjar_id = result[0]
57 
58     tipjar = await get_tipjar(tipjar_id)
59     assert tipjar
60     return tipjar
61 
62 
63 async def get_tipjar(tipjar_id: int) -> Optional[TipJar]:
64     """Return a tipjar by ID"""
65     row = await db.fetchone("SELECT * FROM tipjar.TipJars WHERE id = ?", (tipjar_id,))
66     return TipJar(**row) if row else None
67 
68 
69 async def get_tipjars(wallet_id: str) -> Optional[list]:
70     """Return all TipJars belonging assigned to the wallet_id"""
71     rows = await db.fetchall(
72         "SELECT * FROM tipjar.TipJars WHERE wallet = ?", (wallet_id,)
73     )
74     return [TipJar(**row) for row in rows] if rows else None
75 
76 
77 async def delete_tipjar(tipjar_id: int) -> None:
78     """Delete a TipJar and all corresponding Tips"""
79     await db.execute("DELETE FROM tipjar.TipJars WHERE id = ?", (tipjar_id,))
80     rows = await db.fetchall("SELECT * FROM tipjar.Tips WHERE tipjar = ?", (tipjar_id,))
81     for row in rows:
82         await delete_tip(row["id"])
83 
84 
85 async def get_tip(tip_id: str) -> Optional[Tip]:
86     """Return a Tip"""
87     row = await db.fetchone("SELECT * FROM tipjar.Tips WHERE id = ?", (tip_id,))
88     return Tip(**row) if row else None
89 
90 
91 async def get_tips(wallet_id: str) -> Optional[list]:
92     """Return all Tips assigned to wallet_id"""
93     rows = await db.fetchall("SELECT * FROM tipjar.Tips WHERE wallet = ?", (wallet_id,))
94     return [Tip(**row) for row in rows] if rows else None
95 
96 
97 async def delete_tip(tip_id: str) -> None:
98     """Delete a Tip and its corresponding statspay charge"""
99     await db.execute("DELETE FROM tipjar.Tips WHERE id = ?", (tip_id,))
100     await delete_charge(tip_id)
101 
102 
103 async def update_tip(tip_id: str, **kwargs) -> Tip:
104     """Update a Tip"""
105     q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()])
106     await db.execute(
107         f"UPDATE tipjar.Tips SET {q} WHERE id = ?", (*kwargs.values(), tip_id)
108     )
109     row = await db.fetchone("SELECT * FROM tipjar.Tips WHERE id = ?", (tip_id,))
110     assert row, "Newly updated tip couldn't be retrieved"
111     return Tip(**row)
112 
113 
114 async def update_tipjar(tipjar_id: str, **kwargs) -> TipJar:
115     """Update a tipjar"""
116     q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()])
117     await db.execute(
118         f"UPDATE tipjar.TipJars SET {q} WHERE id = ?", (*kwargs.values(), tipjar_id)
119     )
120     row = await db.fetchone("SELECT * FROM tipjar.TipJars WHERE id = ?", (tipjar_id,))
121     assert row, "Newly updated tipjar couldn't be retrieved"
122     return TipJar(**row)
123
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/lnbits/extensions/tipjar/crud.py b/lnbits/extensions/tipjar/crud.py
--- a/lnbits/extensions/tipjar/crud.py
+++ b/lnbits/extensions/tipjar/crud.py
@@ -76,10 +76,10 @@
 
 async def delete_tipjar(tipjar_id: int) -> None:
     """Delete a TipJar and all corresponding Tips"""
-    await db.execute("DELETE FROM tipjar.TipJars WHERE id = ?", (tipjar_id,))
     rows = await db.fetchall("SELECT * FROM tipjar.Tips WHERE tipjar = ?", (tipjar_id,))
     for row in rows:
         await delete_tip(row["id"])
+    await db.execute("DELETE FROM tipjar.TipJars WHERE id = ?", (tipjar_id,))
 
 
 async def get_tip(tip_id: str) -> Optional[Tip]:

{"golden_diff": "diff --git a/lnbits/extensions/tipjar/crud.py b/lnbits/extensions/tipjar/crud.py\n--- a/lnbits/extensions/tipjar/crud.py\n+++ b/lnbits/extensions/tipjar/crud.py\n@@ -76,10 +76,10 @@\n \n async def delete_tipjar(tipjar_id: int) -> None:\n     \"\"\"Delete a TipJar and all corresponding Tips\"\"\"\n-    await db.execute(\"DELETE FROM tipjar.TipJars WHERE id = ?\", (tipjar_id,))\n     rows = await db.fetchall(\"SELECT * FROM tipjar.Tips WHERE tipjar = ?\", (tipjar_id,))\n     for row in rows:\n         await delete_tip(row[\"id\"])\n+    await db.execute(\"DELETE FROM tipjar.TipJars WHERE id = ?\", (tipjar_id,))\n \n \n async def get_tip(tip_id: str) -> Optional[Tip]:\n", "issue": "Tip Jar extension broken\nTip Jars are broken on v0.9.1\r\n\r\nSetup:\r\n- venv python based setup (python 3.9.13)\r\n- LNBits v0.9.1\r\n- Backend: PostgreSQL\r\n- LND-Rest connection\r\n\r\nSteps to reproduce: \r\n- add extension \"Tip Jar\"\r\n- choose wallet\r\n- do not choose onchain\r\n- test tip jar, fill out donator and amount, hit submit\r\n- error occurrs: `'NoneType' object has no attribute 'mempool_endpoint'`\r\n\r\ndebug log:\r\n```\r\n\"POST /tipjar/api/v1/tips HTTP/1.1\" 200\r\nERROR | lnbits.app:basic_error:182 | handled error\r\nERROR | lnbits.app:basic_error:183 | ERROR:\r\nTraceback (most recent call last):\r\n  File \"/home/lnbits/lnbits-legend/venv/lib/python3.9/site-packages/starlette/middleware/errors.py\", line 162, in __call__\r\n    await self.app(scope, receive, _send)\r\n  File \"/home/lnbits/lnbits-legend/venv/lib/python3.9/site-packages/starlette/middleware/gzip.py\", line 24, in __call__\r\n    await responder(scope, receive, send)\r\n  File \"/home/lnbits/lnbits-legend/venv/lib/python3.9/site-packages/starlette/middleware/gzip.py\", line 43, in __call__\r\n    await self.app(scope, receive, self.send_with_gzip)\r\n  File \"/home/lnbits/lnbits-legend/venv/lib/python3.9/site-packages/starlette/middleware/cors.py\", line 84, in __call__\r\n    await self.app(scope, receive, send)\r\n  File \"/home/lnbits/lnbits-legend/venv/lib/python3.9/site-packages/starlette/exceptions.py\", line 93, in __call__\r\n    raise exc\r\n  File \"/home/lnbits/lnbits-legend/venv/lib/python3.9/site-packages/starlette/exceptions.py\", line 82, in __call__\r\n    await self.app(scope, receive, sender)\r\n  File \"/home/lnbits/lnbits-legend/venv/lib/python3.9/site-packages/fastapi/middleware/asyncexitstack.py\", line 21, in __call__\r\n    raise e\r\n  File \"/home/lnbits/lnbits-legend/venv/lib/python3.9/site-packages/fastapi/middleware/asyncexitstack.py\", line 18, in __call__\r\n    await self.app(scope, receive, send)\r\n  File \"/home/lnbits/lnbits-legend/venv/lib/python3.9/site-packages/starlette/routing.py\", line 670, in __call__\r\n    await route.handle(scope, receive, send)\r\n  File \"/home/lnbits/lnbits-legend/venv/lib/python3.9/site-packages/starlette/routing.py\", line 266, in handle\r\n    await self.app(scope, receive, send)\r\n  File \"/home/lnbits/lnbits-legend/venv/lib/python3.9/site-packages/starlette/routing.py\", line 65, in app\r\n    response = await func(request)\r\n  File \"/home/lnbits/lnbits-legend/venv/lib/python3.9/site-packages/fastapi/routing.py\", line 227, in app\r\n    raw_response = await run_endpoint_function(\r\n  File \"/home/lnbits/lnbits-legend/venv/lib/python3.9/site-packages/fastapi/routing.py\", line 160, in run_endpoint_function\r\n    return await dependant.call(**values)\r\n  File \"/home/lnbits/lnbits-legend/./lnbits/extensions/satspay/views.py\", line 43, in display\r\n    \"mempool_endpoint\": onchainwallet_config.mempool_endpoint,\r\nAttributeError: 'NoneType' object has no attribute 'mempool_endpoint'\r\n```\n", "before_files": [{"content": "from typing import Optional\n\nfrom lnbits.db import SQLITE\n\nfrom ..satspay.crud import delete_charge  # type: ignore\nfrom . import db\nfrom .models import Tip, TipJar, createTipJar\n\n\nasync def create_tip(\n    id: int, wallet: str, message: str, name: str, sats: int, tipjar: str\n) -> Tip:\n    \"\"\"Create a new Tip\"\"\"\n    await db.execute(\n        \"\"\"\n        INSERT INTO tipjar.Tips (\n            id,\n            wallet,\n            name,\n            message,\n            sats,\n            tipjar\n        )\n        VALUES (?, ?, ?, ?, ?, ?)\n        \"\"\",\n        (id, wallet, name, message, sats, tipjar),\n    )\n\n    tip = await get_tip(id)\n    assert tip, \"Newly created tip couldn't be retrieved\"\n    return tip\n\n\nasync def create_tipjar(data: createTipJar) -> TipJar:\n    \"\"\"Create a new TipJar\"\"\"\n\n    returning = \"\" if db.type == SQLITE else \"RETURNING ID\"\n    method = db.execute if db.type == SQLITE else db.fetchone\n\n    result = await (method)(\n        f\"\"\"\n        INSERT INTO tipjar.TipJars (\n            name,\n            wallet,\n            webhook,\n            onchain\n        )\n        VALUES (?, ?, ?, ?)\n        {returning}\n        \"\"\",\n        (data.name, data.wallet, data.webhook, data.onchain),\n    )\n    if db.type == SQLITE:\n        tipjar_id = result._result_proxy.lastrowid\n    else:\n        tipjar_id = result[0]\n\n    tipjar = await get_tipjar(tipjar_id)\n    assert tipjar\n    return tipjar\n\n\nasync def get_tipjar(tipjar_id: int) -> Optional[TipJar]:\n    \"\"\"Return a tipjar by ID\"\"\"\n    row = await db.fetchone(\"SELECT * FROM tipjar.TipJars WHERE id = ?\", (tipjar_id,))\n    return TipJar(**row) if row else None\n\n\nasync def get_tipjars(wallet_id: str) -> Optional[list]:\n    \"\"\"Return all TipJars belonging assigned to the wallet_id\"\"\"\n    rows = await db.fetchall(\n        \"SELECT * FROM tipjar.TipJars WHERE wallet = ?\", (wallet_id,)\n    )\n    return [TipJar(**row) for row in rows] if rows else None\n\n\nasync def delete_tipjar(tipjar_id: int) -> None:\n    \"\"\"Delete a TipJar and all corresponding Tips\"\"\"\n    await db.execute(\"DELETE FROM tipjar.TipJars WHERE id = ?\", (tipjar_id,))\n    rows = await db.fetchall(\"SELECT * FROM tipjar.Tips WHERE tipjar = ?\", (tipjar_id,))\n    for row in rows:\n        await delete_tip(row[\"id\"])\n\n\nasync def get_tip(tip_id: str) -> Optional[Tip]:\n    \"\"\"Return a Tip\"\"\"\n    row = await db.fetchone(\"SELECT * FROM tipjar.Tips WHERE id = ?\", (tip_id,))\n    return Tip(**row) if row else None\n\n\nasync def get_tips(wallet_id: str) -> Optional[list]:\n    \"\"\"Return all Tips assigned to wallet_id\"\"\"\n    rows = await db.fetchall(\"SELECT * FROM tipjar.Tips WHERE wallet = ?\", (wallet_id,))\n    return [Tip(**row) for row in rows] if rows else None\n\n\nasync def delete_tip(tip_id: str) -> None:\n    \"\"\"Delete a Tip and its corresponding statspay charge\"\"\"\n    await db.execute(\"DELETE FROM tipjar.Tips WHERE id = ?\", (tip_id,))\n    await delete_charge(tip_id)\n\n\nasync def update_tip(tip_id: str, **kwargs) -> Tip:\n    \"\"\"Update a Tip\"\"\"\n    q = \", \".join([f\"{field[0]} = ?\" for field in kwargs.items()])\n    await db.execute(\n        f\"UPDATE tipjar.Tips SET {q} WHERE id = ?\", (*kwargs.values(), tip_id)\n    )\n    row = await db.fetchone(\"SELECT * FROM tipjar.Tips WHERE id = ?\", (tip_id,))\n    assert row, \"Newly updated tip couldn't be retrieved\"\n    return Tip(**row)\n\n\nasync def update_tipjar(tipjar_id: str, **kwargs) -> TipJar:\n    \"\"\"Update a tipjar\"\"\"\n    q = \", \".join([f\"{field[0]} = ?\" for field in kwargs.items()])\n    await db.execute(\n        f\"UPDATE tipjar.TipJars SET {q} WHERE id = ?\", (*kwargs.values(), tipjar_id)\n    )\n    row = await db.fetchone(\"SELECT * FROM tipjar.TipJars WHERE id = ?\", (tipjar_id,))\n    assert row, \"Newly updated tipjar couldn't be retrieved\"\n    return TipJar(**row)\n", "path": "lnbits/extensions/tipjar/crud.py"}], "after_files": [{"content": "from typing import Optional\n\nfrom lnbits.db import SQLITE\n\nfrom ..satspay.crud import delete_charge  # type: ignore\nfrom . import db\nfrom .models import Tip, TipJar, createTipJar\n\n\nasync def create_tip(\n    id: int, wallet: str, message: str, name: str, sats: int, tipjar: str\n) -> Tip:\n    \"\"\"Create a new Tip\"\"\"\n    await db.execute(\n        \"\"\"\n        INSERT INTO tipjar.Tips (\n            id,\n            wallet,\n            name,\n            message,\n            sats,\n            tipjar\n        )\n        VALUES (?, ?, ?, ?, ?, ?)\n        \"\"\",\n        (id, wallet, name, message, sats, tipjar),\n    )\n\n    tip = await get_tip(id)\n    assert tip, \"Newly created tip couldn't be retrieved\"\n    return tip\n\n\nasync def create_tipjar(data: createTipJar) -> TipJar:\n    \"\"\"Create a new TipJar\"\"\"\n\n    returning = \"\" if db.type == SQLITE else \"RETURNING ID\"\n    method = db.execute if db.type == SQLITE else db.fetchone\n\n    result = await (method)(\n        f\"\"\"\n        INSERT INTO tipjar.TipJars (\n            name,\n            wallet,\n            webhook,\n            onchain\n        )\n        VALUES (?, ?, ?, ?)\n        {returning}\n        \"\"\",\n        (data.name, data.wallet, data.webhook, data.onchain),\n    )\n    if db.type == SQLITE:\n        tipjar_id = result._result_proxy.lastrowid\n    else:\n        tipjar_id = result[0]\n\n    tipjar = await get_tipjar(tipjar_id)\n    assert tipjar\n    return tipjar\n\n\nasync def get_tipjar(tipjar_id: int) -> Optional[TipJar]:\n    \"\"\"Return a tipjar by ID\"\"\"\n    row = await db.fetchone(\"SELECT * FROM tipjar.TipJars WHERE id = ?\", (tipjar_id,))\n    return TipJar(**row) if row else None\n\n\nasync def get_tipjars(wallet_id: str) -> Optional[list]:\n    \"\"\"Return all TipJars belonging assigned to the wallet_id\"\"\"\n    rows = await db.fetchall(\n        \"SELECT * FROM tipjar.TipJars WHERE wallet = ?\", (wallet_id,)\n    )\n    return [TipJar(**row) for row in rows] if rows else None\n\n\nasync def delete_tipjar(tipjar_id: int) -> None:\n    \"\"\"Delete a TipJar and all corresponding Tips\"\"\"\n    rows = await db.fetchall(\"SELECT * FROM tipjar.Tips WHERE tipjar = ?\", (tipjar_id,))\n    for row in rows:\n        await delete_tip(row[\"id\"])\n    await db.execute(\"DELETE FROM tipjar.TipJars WHERE id = ?\", (tipjar_id,))\n\n\nasync def get_tip(tip_id: str) -> Optional[Tip]:\n    \"\"\"Return a Tip\"\"\"\n    row = await db.fetchone(\"SELECT * FROM tipjar.Tips WHERE id = ?\", (tip_id,))\n    return Tip(**row) if row else None\n\n\nasync def get_tips(wallet_id: str) -> Optional[list]:\n    \"\"\"Return all Tips assigned to wallet_id\"\"\"\n    rows = await db.fetchall(\"SELECT * FROM tipjar.Tips WHERE wallet = ?\", (wallet_id,))\n    return [Tip(**row) for row in rows] if rows else None\n\n\nasync def delete_tip(tip_id: str) -> None:\n    \"\"\"Delete a Tip and its corresponding statspay charge\"\"\"\n    await db.execute(\"DELETE FROM tipjar.Tips WHERE id = ?\", (tip_id,))\n    await delete_charge(tip_id)\n\n\nasync def update_tip(tip_id: str, **kwargs) -> Tip:\n    \"\"\"Update a Tip\"\"\"\n    q = \", \".join([f\"{field[0]} = ?\" for field in kwargs.items()])\n    await db.execute(\n        f\"UPDATE tipjar.Tips SET {q} WHERE id = ?\", (*kwargs.values(), tip_id)\n    )\n    row = await db.fetchone(\"SELECT * FROM tipjar.Tips WHERE id = ?\", (tip_id,))\n    assert row, \"Newly updated tip couldn't be retrieved\"\n    return Tip(**row)\n\n\nasync def update_tipjar(tipjar_id: str, **kwargs) -> TipJar:\n    \"\"\"Update a tipjar\"\"\"\n    q = \", \".join([f\"{field[0]} = ?\" for field in kwargs.items()])\n    await db.execute(\n        f\"UPDATE tipjar.TipJars SET {q} WHERE id = ?\", (*kwargs.values(), tipjar_id)\n    )\n    row = await db.fetchone(\"SELECT * FROM tipjar.TipJars WHERE id = ?\", (tipjar_id,))\n    assert row, \"Newly updated tipjar couldn't be retrieved\"\n    return TipJar(**row)\n", "path": "lnbits/extensions/tipjar/crud.py"}]}