diff --git a/.github/workflows/mypy.yml b/.github/workflows/mypy.yml
index 4d6c6d4da..71713d88f 100644
--- a/.github/workflows/mypy.yml
+++ b/.github/workflows/mypy.yml
@@ -5,10 +5,9 @@ on: [push, pull_request]
jobs:
check:
runs-on: ubuntu-latest
- if: ${{ 'false' == 'true' }} # skip mypy for now
steps:
- uses: actions/checkout@v1
- uses: jpetrucciani/mypy-check@master
with:
mypy_flags: '--install-types --non-interactive'
- path: lnbits
+ path: 'lnbits'
diff --git a/.github/workflows/regtest.yml b/.github/workflows/regtest.yml
index f26e6c386..0d9afdac7 100644
--- a/.github/workflows/regtest.yml
+++ b/.github/workflows/regtest.yml
@@ -16,7 +16,6 @@ jobs:
python-version: ${{ matrix.python-version }}
- name: Setup Regtest
run: |
- docker build -t lnbits-legend .
git clone https://github.com/lnbits/legend-regtest-enviroment.git docker
cd docker
chmod +x ./tests
@@ -39,8 +38,8 @@ jobs:
LNBITS_DATA_FOLDER: ./data
LNBITS_BACKEND_WALLET_CLASS: LndRestWallet
LND_REST_ENDPOINT: https://localhost:8081/
- LND_REST_CERT: docker/data/lnd-1/tls.cert
- LND_REST_MACAROON: docker/data/lnd-1/data/chain/bitcoin/regtest/admin.macaroon
+ LND_REST_CERT: ./docker/data/lnd-1/tls.cert
+ LND_REST_MACAROON: ./docker/data/lnd-1/data/chain/bitcoin/regtest/admin.macaroon
run: |
sudo chmod -R a+rwx . && rm -rf ./data && mkdir -p ./data
make test-real-wallet
@@ -57,7 +56,6 @@ jobs:
python-version: ${{ matrix.python-version }}
- name: Setup Regtest
run: |
- docker build -t lnbits-legend .
git clone https://github.com/lnbits/legend-regtest-enviroment.git docker
cd docker
chmod +x ./tests
@@ -79,7 +77,7 @@ jobs:
PORT: 5123
LNBITS_DATA_FOLDER: ./data
LNBITS_BACKEND_WALLET_CLASS: CLightningWallet
- CLIGHTNING_RPC: docker/data/clightning-1/regtest/lightning-rpc
+ CLIGHTNING_RPC: ./docker/data/clightning-1/regtest/lightning-rpc
run: |
sudo chmod -R a+rwx . && rm -rf ./data && mkdir -p ./data
make test-real-wallet
diff --git a/.gitignore b/.gitignore
index 855e8737a..c2a305e7b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -35,3 +35,6 @@ coverage.xml
node_modules
lnbits/static/bundle.*
docker
+
+# Nix
+*result*
diff --git a/Pipfile b/Pipfile
index f8c42a9dd..a0cef805a 100644
--- a/Pipfile
+++ b/Pipfile
@@ -36,9 +36,11 @@ pycryptodomex = "*"
[dev-packages]
black = "==20.8b1"
-pytest = "*"
-pytest-cov = "*"
-mypy = "*"
-pytest-asyncio = "*"
-requests = "*"
mock = "*"
+mypy = "*"
+pytest = "*"
+pytest-asyncio = "*"
+pytest-cov = "*"
+requests = "*"
+types-mock = "*"
+types-protobuf = "*"
diff --git a/Pipfile.lock b/Pipfile.lock
index 42d471c6f..6a478e4c2 100644
--- a/Pipfile.lock
+++ b/Pipfile.lock
@@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
- "sha256": "503e9942306106e40621c59f37a3ab866b483f8c5f27b879c1c6783dca30949f"
+ "sha256": "08e53fc6f1fcc021b33f9b2c5ae3bd6dbde815d4b317e9341ab02cf5b625acbc"
},
"pipfile-spec": 6,
"requires": {
@@ -29,6 +29,7 @@
"sha256:413adf95f93886e442aea925f3ee43baa5a765a64a0f52c6081894f9992fdd0b",
"sha256:cb29b9c70620506a9a8f87a309591713446953302d7d995344d0d7c6c0c9a7be"
],
+ "markers": "python_full_version >= '3.6.2'",
"version": "==3.6.1"
},
"asyncio": {
@@ -46,6 +47,7 @@
"sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4",
"sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"
],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==21.4.0"
},
"bech32": {
@@ -53,6 +55,7 @@
"sha256:7d6db8214603bd7871fcfa6c0826ef68b85b0abd90fa21c285a9c5e21d2bd899",
"sha256:990dc8e5a5e4feabbdf55207b5315fdd9b73db40be294a19b3752cde9e79d981"
],
+ "markers": "python_version >= '3.5'",
"version": "==1.2.0"
},
"bitstring": {
@@ -76,6 +79,7 @@
"sha256:84c85a9078b11105f04f3036a9482ae10e4621616db313fe045dd24743a0820d",
"sha256:fe86415d55e84719d75f8b69414f6438ac3547d2078ab91b67e779ef69378412"
],
+ "markers": "python_version >= '3.6'",
"version": "==2022.6.15"
},
"cffi": {
@@ -139,6 +143,7 @@
"sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e",
"sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"
],
+ "markers": "python_version >= '3.7'",
"version": "==8.1.3"
},
"ecdsa": {
@@ -177,6 +182,7 @@
"sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6",
"sha256:47222cb6067e4a307d535814917cd98fd0a57b6788ce715755fa2b6c28b56042"
],
+ "markers": "python_version >= '3.6'",
"version": "==0.12.0"
},
"httpcore": {
@@ -184,6 +190,7 @@
"sha256:1105b8b73c025f23ff7c36468e4432226cbb959176eab66864b8e31c4ee27fa6",
"sha256:18b68ab86a3ccf3e7dc0f43598eaddcf472b602aba29f9aa6ab85fe2ada3980b"
],
+ "markers": "python_version >= '3.7'",
"version": "==0.15.0"
},
"httptools": {
@@ -238,6 +245,7 @@
"sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff",
"sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"
],
+ "markers": "python_version >= '3.5'",
"version": "==3.3"
},
"jinja2": {
@@ -307,6 +315,7 @@
"sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a",
"sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7"
],
+ "markers": "python_version >= '3.7'",
"version": "==2.1.1"
},
"marshmallow": {
@@ -314,6 +323,7 @@
"sha256:00040ab5ea0c608e8787137627a8efae97fabd60552a05dc889c888f814e75eb",
"sha256:635fb65a3285a31a30f276f30e958070f5214c7196202caa5c7ecf28f5274bc7"
],
+ "markers": "python_version >= '3.7'",
"version": "==3.17.0"
},
"outcome": {
@@ -321,6 +331,7 @@
"sha256:6f82bd3de45da303cf1f771ecafa1633750a358436a8bb60e06a1ceb745d2672",
"sha256:c4ab89a56575d6d38a05aa16daeaa333109c1f96167aba8901ab18b6b5e0f7f5"
],
+ "markers": "python_version >= '3.7'",
"version": "==1.2.0"
},
"packaging": {
@@ -328,6 +339,7 @@
"sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb",
"sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"
],
+ "markers": "python_version >= '3.6'",
"version": "==21.3"
},
"psycopg2-binary": {
@@ -473,6 +485,7 @@
"sha256:f0f047e11febe5c3198ed346b507e1d010330d56ad615a7e0a89fae604065a0e",
"sha256:fe4670cb32ea98ffbf5a1262f14c3e102cccd92b1869df3bb09538158ba90fe6"
],
+ "markers": "python_full_version >= '3.6.1'",
"version": "==1.9.1"
},
"pyngrok": {
@@ -487,6 +500,7 @@
"sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb",
"sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"
],
+ "markers": "python_full_version >= '3.6.8'",
"version": "==3.0.9"
},
"pypng": {
@@ -517,6 +531,7 @@
"sha256:b7e3b04a59693c42c36f9ab1cc2acc46fa5df8c78e178fc33a8d4cd05c8d498f",
"sha256:d92a187be61fe482e4fd675b6d52200e7be63a12b724abbf931a40ce4fa92938"
],
+ "markers": "python_version >= '3.5'",
"version": "==0.20.0"
},
"pyyaml": {
@@ -562,9 +577,13 @@
"sha256:026c0de2ee8385d1255b9c2426cd4f03fe9177ac94c09979bc601946c8493aa0",
"sha256:99142650756ef1998ce0661568f54a47dac8c638fb27e3816c02536575dbba8c"
],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.6.0.post0"
},
"rfc3986": {
+ "extras": [
+ "idna2008"
+ ],
"hashes": [
"sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835",
"sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97"
@@ -600,6 +619,14 @@
"index": "pypi",
"version": "==0.14.0"
},
+ "setuptools": {
+ "hashes": [
+ "sha256:0d33c374d41c7863419fc8f6c10bfe25b7b498aa34164d135c622e52580c6b16",
+ "sha256:c04b44a57a6265fe34a4a444e965884716d34bae963119a76353434d6f18e450"
+ ],
+ "markers": "python_version >= '3.7'",
+ "version": "==63.2.0"
+ },
"shortuuid": {
"hashes": [
"sha256:459f12fa1acc34ff213b1371467c0325169645a31ed989e268872339af7563d5",
@@ -613,6 +640,7 @@
"sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926",
"sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"
],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2'",
"version": "==1.16.0"
},
"sniffio": {
@@ -620,6 +648,7 @@
"sha256:471b71698eac1c2112a40ce2752bb2f4a4814c22a54a3eed3676bc0f5ca9f663",
"sha256:c4666eecec1d3f50960c6bdf61ab7bc350648da6c126e3cf6898d8cd4ddcd3de"
],
+ "markers": "python_version >= '3.5'",
"version": "==1.2.0"
},
"sqlalchemy": {
@@ -676,17 +705,18 @@
},
"sse-starlette": {
"hashes": [
- "sha256:840607fed361360cc76f8408a25f0eca887e7cab3c3ee44f9762f179428e2bd4",
- "sha256:ca2de945af80b83f1efda6144df9e13db83880b3b87c660044b64f199395e8b7"
+ "sha256:14608559d40e3e7c6385e8c5a7b88468f7fc40c2277673a1fe8d26568e8d7c65",
+ "sha256:72438ed39b1612d1ea6d89a7c0af8afee6de0389dcbe2e77539001e78b5aa89c"
],
"index": "pypi",
- "version": "==0.10.3"
+ "version": "==1.0.0"
},
"starlette": {
"hashes": [
"sha256:5a60c5c2d051f3a8eb546136aa0c9399773a689595e099e0877704d5888279bf",
"sha256:c6d21096774ecb9639acad41b86b7706e52ba3bf1dc13ea4ed9ad593d47e24c7"
],
+ "markers": "python_version >= '3.6'",
"version": "==0.19.1"
},
"typing-extensions": {
@@ -698,6 +728,9 @@
"version": "==4.3.0"
},
"uvicorn": {
+ "extras": [
+ "standard"
+ ],
"hashes": [
"sha256:c19a057deb1c5bb060946e2e5c262fc01590c6529c0af2c3d9ce941e89bc30e0",
"sha256:cade07c403c397f9fe275492a48c1b869efd175d5d8a692df649e6e7e2ed8f4e"
@@ -815,6 +848,7 @@
"sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4",
"sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"
],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==21.4.0"
},
"black": {
@@ -829,6 +863,7 @@
"sha256:84c85a9078b11105f04f3036a9482ae10e4621616db313fe045dd24743a0820d",
"sha256:fe86415d55e84719d75f8b69414f6438ac3547d2078ab91b67e779ef69378412"
],
+ "markers": "python_version >= '3.6'",
"version": "==2022.6.15"
},
"charset-normalizer": {
@@ -836,6 +871,7 @@
"sha256:5189b6f22b01957427f35b6a08d9a0bc45b46d3788ef5a92e978433c7a35f8a5",
"sha256:575e708016ff3a5e3681541cb9d79312c416835686d054a23accb873b254f413"
],
+ "markers": "python_version >= '3.6'",
"version": "==2.1.0"
},
"click": {
@@ -843,9 +879,13 @@
"sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e",
"sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"
],
+ "markers": "python_version >= '3.7'",
"version": "==8.1.3"
},
"coverage": {
+ "extras": [
+ "toml"
+ ],
"hashes": [
"sha256:0895ea6e6f7f9939166cc835df8fa4599e2d9b759b02d1521b574e13b859ac32",
"sha256:0f211df2cba951ffcae210ee00e54921ab42e2b64e0bf2c0befc977377fb09b7",
@@ -889,6 +929,7 @@
"sha256:f23876b018dfa5d3e98e96f5644b109090f16a4acb22064e0f06933663005d39",
"sha256:f7bd0ffbcd03dc39490a1f40b2669cc414fae0c4e16b77bb26806a4d0b7d1452"
],
+ "markers": "python_version >= '3.7'",
"version": "==6.4.2"
},
"idna": {
@@ -896,6 +937,7 @@
"sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff",
"sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"
],
+ "markers": "python_version >= '3.5'",
"version": "==3.3"
},
"iniconfig": {
@@ -954,6 +996,7 @@
"sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb",
"sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"
],
+ "markers": "python_version >= '3.6'",
"version": "==21.3"
},
"pathspec": {
@@ -968,6 +1011,7 @@
"sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159",
"sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"
],
+ "markers": "python_version >= '3.6'",
"version": "==1.0.0"
},
"py": {
@@ -975,6 +1019,7 @@
"sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719",
"sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"
],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==1.11.0"
},
"pyparsing": {
@@ -982,6 +1027,7 @@
"sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb",
"sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"
],
+ "markers": "python_full_version >= '3.6.8'",
"version": "==3.0.9"
},
"pytest": {
@@ -1010,82 +1056,83 @@
},
"regex": {
"hashes": [
- "sha256:00d2e907d3c5e4f85197c8d2263a9cc2d34bf234a9c6236ae42a3fb0bc09b759",
- "sha256:0186edcda692c38381db8ac257c2d023fd2e08818d45dc5bee4ed84212045f51",
- "sha256:06c509bd7dcb7966bdb03974457d548e54d8327bad5b0c917e87248edc43e2eb",
- "sha256:0a3f3f45c5902eb4d90266002ccb035531ae9b9278f6d5e8028247c34d192099",
- "sha256:0c1821146b429e6fdbd13ea10f26765e48d5284bc79749468cfbfe3ceb929f0d",
- "sha256:0d93167b7d7731fa9ff9fdc1bae84ec9c7133b01a35f8cc04e926d48da6ce1f7",
- "sha256:0fd8c3635fa03ef79d07c7b3ed693b3f3930ccb52c0c51761c3296a7525b135c",
- "sha256:119091c675e6ad19da8770f89aa1d52f4ad2a2018d631956f3e90c45882df880",
- "sha256:121981ba84309dabefd5e1debd49be6d51624e54b4d44bfc184cd8d555ff1df1",
- "sha256:1244e9b9b4b81c9c34e8a84273ffaeebdc78abc98a5b02dcdd49845eb3c63bd7",
- "sha256:12e1404dfb4e928d3273a10e3468877fe84bdcd3c50b655a2c9613cfc5d9fe63",
- "sha256:13d74951c14708f00700bb29475129ecbc40e01b4029c62ee7bfe9d1f59f31ce",
- "sha256:162a5939a6fdf48658d3565eeff35acdd207e07367bf5caaff3d9ea7cb77d7a9",
- "sha256:1703490c5b850fa9cef1af00c58966756042e6ca22f4fb5bb857345cd535834f",
- "sha256:18e6203cfd81df42a987175aaeed7ba46bcb42130cd81763e2d5edcff0006d5d",
- "sha256:192c2784833aea6fc7b004730bf1b91b8b8c6b998b30271aaf3bd8adfef20a96",
- "sha256:1948d3ceac5b2d55bc93159c1e0679a256a87a54c735be5cef4543a9e692dbb9",
- "sha256:206a327e628bc529d64b21ff79a5e2564f5aec7dc7abcd4b2e8a4b271ec10550",
- "sha256:2e5db20412f0db8798ff72473d16da5f13ec808e975b49188badb2462f529fa9",
- "sha256:2f94b0befc811fe74a972b1739fffbf74c0dc1a91102aca8e324aa4f2c6991bd",
- "sha256:303676797c4c7978726e74eb8255d68f7125a3a29da71ff453448f2117290e9a",
- "sha256:34ae4f35db30caa4caf85c55069fcb7a05966a3a5ba6e9e1dab5477d84fbb08a",
- "sha256:3c6df8be7d1dd35a0d9a200fbc29f888c4452c8882d284f87608046152e049e6",
- "sha256:402fa998c5988d11ed34585eb65740dcebd0fd11844d12eb0a6b4be178eb9c64",
- "sha256:40a28759d345c0bb1f5b0ac74ac04f5d48136019522c95c0ec4b07786f67ce20",
- "sha256:414ae507ba88264444baf771fec43ce0adcd4c5dbb304d3e0716f3f4d4499d2e",
- "sha256:42da079e31ae9818ffa7a35cdd16ab7104e3f7eca9c0958040aede827b2e55c6",
- "sha256:473a7d21932ce7c314953b33c32e63df690181860edcdf14bba1278cdf71b07f",
- "sha256:49fcb45931a693b0e901972c5e077ea2cf30ec39da699645c43cb8b1542c6e14",
- "sha256:4c5913cb9769038bd03e42318955c2f15a688384a6a0b807bcfc8271603d9277",
- "sha256:4cfeb71095c8d8380a5df5a38ff94d27a3f483717e509130a822b4d6400b7991",
- "sha256:4dc74f0171eede67d79a79c06eca0fe5b7b280dbb8c27ad1fae4ced2ad66268f",
- "sha256:5b1cffff2d9f832288fe516021cb81c95c57c0067b13a82f1d2daabdbc2f4270",
- "sha256:601c99ac775b6c89699a48976f3dbb000b47d3ca59362c8abc9582e6d0780d91",
- "sha256:667a06bb8d72b6da3d9cf38dac4ba969688868ed2279a692e993d2c0e1c30aba",
- "sha256:673549a0136c7893f567ed71ab5225ed3701c79b17c0a7faee846c645fc24010",
- "sha256:67bd3bdd27db7a6460384869dd4b9c54267d805b67d70b20495bb5767f8e051c",
- "sha256:727edff0a4eaff3b6d26cbb50216feac9055aba7e6290eec23c061c2fe2fab55",
- "sha256:782627a1cb8fbb1c78d8e841f5b71c2c683086c038f975bebdac7cce7678a96f",
- "sha256:7d462ba84655abeddae4dfc517fe1afefb5430b3b5acb0a954de12a47aea7183",
- "sha256:8ab39aa445d00902c43a1e951871bedc7f18d095a21eccba153d594faac34aea",
- "sha256:8e2075ed4ea2e231e2e98b16cfa5dae87e9a6045a71104525e1efc29aa8faa8e",
- "sha256:9daeccb2764bf4cc280c40c6411ae176bb0876948e536590a052b3d647254c95",
- "sha256:9e4006942334fa954ebd32fa0728718ec870f95f4ba7cda9edc46dd49c294f22",
- "sha256:9f1c8fffd4def0b76c0947b8cb261b266e31041785dc2dc2db7569407a2f54fe",
- "sha256:a00cd58a30a1041c193777cb1bc090200b05ff4b073d5935738afd1023e63069",
- "sha256:a0220a7a16fd4bfc700661f920510defd31ef7830ce992d5cc51777aa8ccd724",
- "sha256:a048f91823862270905cb22ef88038b08aac852ce48e0ecc4b4bf1b895ec37d9",
- "sha256:a3c47c71fde0c5d584402e67546c81af9951540f1f622d821e9c20761556473a",
- "sha256:a6d9ea727fd1233ee746bf44dd37e7d4320b3ed8ff09e73d7638c969b28d280f",
- "sha256:ab0709daedc1099bbd4371ae17eeedd4efc1cf70fcdcfe5de1374a0944b61f80",
- "sha256:ab1cb36b411f16da6e057ef8e6657dd0af36f59a667f07e0b4b617e44e53d7b2",
- "sha256:ae1c5b435d44aa91d48cc710f20c3485e0584a3ad3565d5ae031d61a35f674f4",
- "sha256:b279b9bb401af41130fd2a09427105100bc8c624ed45da1c81c1c0d0aa639734",
- "sha256:b72a4ec79a15f6066d14ae1c472b743af4b4ecee14420e8d6e4a336b49b8f21c",
- "sha256:c2cd93725911c0159d597b90c96151070ef7e0e67604637e2f2abe06c34bf079",
- "sha256:c7c5f914b0eb5242c09f91058b80295525897e873b522575ab235b48db125597",
- "sha256:d07d849c9e2eca80adb85d3567302a47195a603ad7b1f0a07508e253c041f954",
- "sha256:d2672d68cf6c8452b6758fc3cd2d8feac966d511eed79a68182a5297b473af9c",
- "sha256:d35bbcbf70d14f724e7489746cf68efe122796578addd98f91428e144d0ad266",
- "sha256:d40b4447784dbe0896a6d10a178f6724598161f942c56f5a60dc0ef7fe63f7a1",
- "sha256:d561dcb0fb0ab858291837d51330696a45fd3ba6912a332a4ee130e5484b9e47",
- "sha256:d7f5ccfff648093152cadf6d886c7bd922047532f72024c953a79c7553aac2fe",
- "sha256:dce6b2ad817e3eb107f8704782b091b0631dd3adf47f14bdc086165d05b528b0",
- "sha256:e1fdda3ec7e9785065b67941693995cab95b54023a21db9bf39e54cc7b2c3526",
- "sha256:e2a262ec85c595fc8e1f3162cafc654d2219125c00ea3a190c173cea70d2cc7a",
- "sha256:e2fc1e3928c1189c0382c547c17717c6d9f425fffe619ef94270fe4c6c8be0a6",
- "sha256:ea27acd97a752cfefa9907da935e583efecb302e6e9866f37565968c8407ad58",
- "sha256:ee769a438827e443ed428e66d0aa7131c653ecd86ddc5d4644a81ed1d93af0e7",
- "sha256:f32e0d1c7e7b0b9c3cac76f3d278e7ee6b99c95672d2c1c6ea625033431837c0",
- "sha256:f355caec5bbce20421dc26e53787b10e32fd0df68db2b795435217210c08d69c",
- "sha256:f87e9108bb532f8a1fc6bf7e69b930a35c7b0267b8fef0a3ede0bcb4c5aaa531",
- "sha256:f8a2fd2f62a77536e4e3193303bec380df40d99e253b1c8f9b6eafa07eaeff67",
- "sha256:fbdf4fc6adf38fab1091c579ece3fe9f493bd0f1cfc3d2c76d2e52461ca4f8a9"
+ "sha256:045682b6457f0224deb10c9720b8008dc798b1ad4331de9302fd4b615211e5a5",
+ "sha256:04e3869fc6fc24b75d38e7547797bb0a82d6cccd49e8ce6ae21a0b87aeb9fac7",
+ "sha256:073bd76a1f03e05a6ca0df705b6117f75b10c340af068b55becb1334fc6426c2",
+ "sha256:0aa48740a1385cb668ffd828a7e2c8078ce29c72d64651c8226bd2b7cb5dba0c",
+ "sha256:0e380ebd841201f980ab022d07033be12c9438a9b2e0f60324c9e3ba31790918",
+ "sha256:0f9b8ef2e46d627b2a85d3f4fe433ead283c420adcf9461906c3db10766dd3b1",
+ "sha256:10d3a5dda21a125cfdc31e45ae6ce6bdd1e45cb194199801248d6580be8dc337",
+ "sha256:11965ae779a0ccfb6d17996d531e2f522124e04d98cc65742b96bf8f50758ab9",
+ "sha256:1c61175730596266015c4e005c65cbfeeb1ef019ccd024870169c6593a26bdb0",
+ "sha256:1d1d79a87a33fbd6573e30eb53969a4190035894c4390a0787fb823e9e86b72f",
+ "sha256:1f0bf228c948f543876f4fb310322a4ff7e398667dd58aeb4815dc9e30bf867e",
+ "sha256:2026a1c108752e48577f9720076bf6e31a60aaf0c3000fffad4e2527fdffbe95",
+ "sha256:204705b7ec16267d39870a19e72e832b12739dc48a26d923a9cb94043660d50f",
+ "sha256:248b16534d1ef8f10a72cda0f97b3dfa25b3d9123a7e726d1594cb07a541bba0",
+ "sha256:27b5011449213dfd880e592ea6d311d00739e87d9512bad507ee18c9c92a20ac",
+ "sha256:2880d21e9507869ab1636e50461afb9ffb08797f1cb76f70d3ad52e7dd13a335",
+ "sha256:2d3f9fc885ecd8b0eb248d0e190aa7264b977cc23b6da7c08444065170c57e2e",
+ "sha256:2d8b50ae3cbaaa2e5ed89ed81fc025ec64b1a54c4f34e6bdaad9dc63fc2afa6b",
+ "sha256:3ff5b2b6a136307a5551e7821d83ab12c46f57c32bf23a27877c9c6bdb55aa61",
+ "sha256:40b4436466d47271fe3b4df63e55307c91a40cda0875a9ff3b7231a08394b283",
+ "sha256:435c94d5939a7cf4b0af1cce30d196451bae441ebe64f63d08517ab490ceb385",
+ "sha256:4c0b7c413d4a8a55d72df18acbeb50276eee19cec7e2f54ed3bddc46bc3b3aeb",
+ "sha256:4cfe87490d0a801749b42491ef7e968342e5787decbf57d5402fc2c17f7302e3",
+ "sha256:4f480661cd0809a1177b09581c12c9ceff9ca989e4a0c8c0f10379dffa3b4c4c",
+ "sha256:53370620db6058dbb464324b053fee8608518d76ff6352b2835d71e2ae8ef293",
+ "sha256:53eca0cece2ddb592b8dd9746f0b258d0c8a45f5a3ac8eb96833058f64778fca",
+ "sha256:5751bfe0d939d7110396510a39e48ee928b36b55177207c47766b093886a3945",
+ "sha256:5d750f99c40a7e994df1cf1295bbb3e873417ca69508664fe9f65db92e46ca40",
+ "sha256:6019737db5c46a24f307eae5069fced0752e3a22380398bbefbef77b068b9537",
+ "sha256:605ca47681c7405723a4970d66d13fa3a3a66efa6b8499d7ab7bce1ddb44a36f",
+ "sha256:69120a8fb1eb932b6e3ededb16448be6444eb952f9350c21dddbef947fea5690",
+ "sha256:72713de336b8d895f91aad34f5591f33d1d8727bd739af3ae2657411ef6e0739",
+ "sha256:74077b462a9b255c5fca247484f46b0f25c32676fe4645cb6b5304b2d997357a",
+ "sha256:74f0067495a842f7cc198b14031a2893d377bed38e19d785f35095082ab5a556",
+ "sha256:817a0618c149d77e493963cd98851ff49d6ab8bcab247fdbf85bb89a14dca5c3",
+ "sha256:869a0f6405ec569863e09909617138af575b5e2bc5181184e60f339a4c8a6d7a",
+ "sha256:86b0cf786efd587c27abd1d07020c555a82275bba3506d916d42aed7a3744967",
+ "sha256:8cbc407c44003a1cb4aaa2d48cc19b45dde07ba0ae2f541c6750ad18f8c118f5",
+ "sha256:90e082f262cf858cbcde330999ea5612e12918982033b716d2c5d8b1bc7a01db",
+ "sha256:95d8f1083ff4546ec14fb46dc41b042d372258f8c319df1e2316d8fe1bd3f085",
+ "sha256:97211bae1bc51f153764485a54d8d1130196cae33d02285c33732b26c5328b8d",
+ "sha256:98dbaf6a86991e2b09f4d8a7669b4304755bda519565971dc3b87ee00fd6eab2",
+ "sha256:9e297ff33172853e9a9e46dbb0c2ebf44fb38ebefce659698df4eb9dfce0a748",
+ "sha256:9e64492c8105312f080e25e457db70f9b0d02e6ba3c1ea14468087b0e3aa876f",
+ "sha256:a11241808e59deec8314792bbd8a6f0a8a7a95b742709e134c73a3216dbb26ae",
+ "sha256:a33c36dcc1760d66f1969d7d3dc8956f45a3d502178053074b8489f67718138f",
+ "sha256:a4e1e7ba8c58c1f0b828418f9a2635c0f6344bf107308b8fe65f234a13c8462a",
+ "sha256:a55c4d5e5076cc5ece625dd1f7015c9a0818ba1f9ad9db421b495d7ece088e56",
+ "sha256:a6f5cc82e1fa380eb6b8040d626df6ba9f492b6886527f53d59838b11e9caaef",
+ "sha256:a96826b5c9dc68417ddb29843998473f9c2c047911e6fca36a9f81a898087b01",
+ "sha256:acf6cbcc19d86f44e8a9d3cf1f6946a71dc55c2ec8ae374c547b1eefd83b954f",
+ "sha256:b4be25d3c640a35671431d8ef8cd522319254074b150147fcacad90c91ea42c2",
+ "sha256:b5a31abc27f9bda7a455bfa1e1bd623da50e3a343a040084c879d07394a93481",
+ "sha256:bb812e590e3881a93d4d291270440b3795fa4c0bc1b03ba15fe1cc88d2ea4347",
+ "sha256:bbd542b4afd2ed5491a480e9b15f4bca13e0170ef1895064fd15741382fdebe0",
+ "sha256:bf27111f56b762238bd3ce4c5e8ad34167d85dd3c077721a0c093517526a94af",
+ "sha256:c7a4dc9436b7a55c36daa3959e92d70337c547651ebeda685dd6bb083f0b77ab",
+ "sha256:c88a1068ac8e5dc579d5104903fa2c488448c1137e580a77d1438d98070c4243",
+ "sha256:c8cffb5040bf432355cfe51378072f20087609694066ace80710bdba04cf9ce2",
+ "sha256:c944921b0e77f1923dd89cda65534223ac107e24d71b1dfe174237faa5efd32a",
+ "sha256:ca2a7233275acf0087ecd15e5fb0eeb722a1f4de453b49bb1443edf2c2f5a997",
+ "sha256:cf0a3b9744f94693f3ebcca1c259354f0043c19a4ce938f80ad6d1816b8fd8f0",
+ "sha256:dbe2f16a66f64a00dc9ccc0db7f8b5ff014f409840e03675eb431f03b50ddffd",
+ "sha256:deb7b067b3b9751c60dc7f6de68476138d550c074a5016ba944cc55863fa86d1",
+ "sha256:df669bacbda209e9b00928f1d00432b27a16c3e051f9f7e5ea306f9b78bf3e7c",
+ "sha256:e2dd4ca82c2241be9582d2ae060070f2bccb0c98295b608009d5cc6e6041eaed",
+ "sha256:e432cf909c53506da4c8308753b2671ee37d2d8d1de8b4b54ab76e91ca7ba0b5",
+ "sha256:e4e7f1aba3aaf08e11d33fd5c2d8dd8cbf573049474e11256c91e3ba3d5e1642",
+ "sha256:e51ab7fbfe5ac3002b9aee527bcb164b17fd92f5663ebf2a4e5917dd9d577864",
+ "sha256:ea00c7f86405d88995e7bab5609e343fdedfe1ffc8191d3b5ed0f8c7f5eb17ec",
+ "sha256:f7006d7c74e25f8bc592604a5a72ba624f10ebd5c0683ab4d3e940a88ac0098c",
+ "sha256:f93e3e5acf82812ea92a1ccdcce690aab18c4044dd824f6b959d2b6069d84312",
+ "sha256:fa8a4bc81b15f49c57ede3fd636786c6619179661acf2430fcc387d75bf28d33",
+ "sha256:fc44c49f33dd75e58b5ff2a5ac50c96c84b6b209d36b4790c85bca08a3b9017d"
],
- "version": "==2022.7.9"
+ "markers": "python_version >= '3.6'",
+ "version": "==2022.7.24"
},
"requests": {
"hashes": [
@@ -1100,6 +1147,7 @@
"sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b",
"sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"
],
+ "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2'",
"version": "==0.10.2"
},
"tomli": {
@@ -1107,6 +1155,7 @@
"sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc",
"sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"
],
+ "markers": "python_version >= '3.7'",
"version": "==2.0.1"
},
"typed-ast": {
@@ -1136,8 +1185,25 @@
"sha256:ebd9d7f80ccf7a82ac5f88c521115cc55d84e35bf8b446fcd7836eb6b98929a3",
"sha256:ed855bbe3eb3715fca349c80174cfcfd699c2f9de574d40527b8429acae23a66"
],
+ "markers": "python_version >= '3.6'",
"version": "==1.5.4"
},
+ "types-mock": {
+ "hashes": [
+ "sha256:4535fbb3912b88a247d43cdb41db0c8b2e187138986f6f01a989717e56105848",
+ "sha256:a849bc2d966063f4946013bf404822ee2b96f77a8dccda4174b70ab61c5293fe"
+ ],
+ "index": "pypi",
+ "version": "==4.0.15"
+ },
+ "types-protobuf": {
+ "hashes": [
+ "sha256:d291388678af91bb045fafa864f142dc4ac22f5d4cdca097c7d8d8a32fa9b3ab",
+ "sha256:d2b26861b0cb46a3c8669b0df507b7ef72e487da66d61f9f3576aa76ce028a83"
+ ],
+ "index": "pypi",
+ "version": "==3.19.22"
+ },
"typing-extensions": {
"hashes": [
"sha256:25642c956049920a5aa49edcdd6ab1e06d7e5d467fc00e0506c44ac86fbfca02",
@@ -1151,6 +1217,7 @@
"sha256:8298d6d56d39be0e3bc13c1c97d133f9b45d797169a0e11cdd0e0489d786f7ec",
"sha256:879ba4d1e89654d9769ce13121e0f94310ea32e8d2f8cf587b77c08bbcdb30d6"
],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5' and python_version < '4'",
"version": "==1.26.10"
}
}
diff --git a/README.md b/README.md
index f0f9df627..5c6b702a9 100644
--- a/README.md
+++ b/README.md
@@ -7,7 +7,7 @@ LNbits

-# LNbits v0.3 BETA, free and open-source lightning-network wallet/accounts system
+# LNbits v0.9 BETA, free and open-source lightning-network wallet/accounts system
(Join us on [https://t.me/lnbits](https://t.me/lnbits))
diff --git a/build.py b/build.py
new file mode 100644
index 000000000..5cc8b7de1
--- /dev/null
+++ b/build.py
@@ -0,0 +1,110 @@
+import warnings
+import subprocess
+import glob
+import os
+from os import path
+from typing import Any, List, NamedTuple, Optional
+from pathlib import Path
+
+LNBITS_PATH = path.dirname(path.realpath(__file__)) + "/lnbits"
+
+def get_js_vendored(prefer_minified: bool = False) -> List[str]:
+ paths = get_vendored(".js", prefer_minified)
+
+ def sorter(key: str):
+ if "moment@" in key:
+ return 1
+ if "vue@" in key:
+ return 2
+ if "vue-router@" in key:
+ return 3
+ if "polyfills" in key:
+ return 4
+ return 9
+
+ return sorted(paths, key=sorter)
+
+
+def get_css_vendored(prefer_minified: bool = False) -> List[str]:
+ paths = get_vendored(".css", prefer_minified)
+
+ def sorter(key: str):
+ if "quasar@" in key:
+ return 1
+ if "vue@" in key:
+ return 2
+ if "chart.js@" in key:
+ return 100
+ return 9
+
+ return sorted(paths, key=sorter)
+
+
+def get_vendored(ext: str, prefer_minified: bool = False) -> List[str]:
+ paths: List[str] = []
+ for path in glob.glob(
+ os.path.join(LNBITS_PATH, "static/vendor/**"), recursive=True
+ ):
+ if path.endswith(".min" + ext):
+ # path is minified
+ unminified = path.replace(".min" + ext, ext)
+ if prefer_minified:
+ paths.append(path)
+ if unminified in paths:
+ paths.remove(unminified)
+ elif unminified not in paths:
+ paths.append(path)
+
+ elif path.endswith(ext):
+ # path is not minified
+ minified = path.replace(ext, ".min" + ext)
+ if not prefer_minified:
+ paths.append(path)
+ if minified in paths:
+ paths.remove(minified)
+ elif minified not in paths:
+ paths.append(path)
+
+ return sorted(paths)
+
+
+def url_for_vendored(abspath: str) -> str:
+ return "/" + os.path.relpath(abspath, LNBITS_PATH)
+
+def transpile_scss():
+ with warnings.catch_warnings():
+ warnings.simplefilter("ignore")
+ from scss.compiler import compile_string # type: ignore
+
+ with open(os.path.join(LNBITS_PATH, "static/scss/base.scss")) as scss:
+ with open(os.path.join(LNBITS_PATH, "static/css/base.css"), "w") as css:
+ css.write(compile_string(scss.read()))
+
+def bundle_vendored():
+ for getfiles, outputpath in [
+ (get_js_vendored, os.path.join(LNBITS_PATH, "static/bundle.js")),
+ (get_css_vendored, os.path.join(LNBITS_PATH, "static/bundle.css")),
+ ]:
+ output = ""
+ for path in getfiles():
+ with open(path) as f:
+ output += "/* " + url_for_vendored(path) + " */\n" + f.read() + ";\n"
+ with open(outputpath, "w") as f:
+ f.write(output)
+
+
+def build():
+ transpile_scss()
+ bundle_vendored()
+# root = Path("lnbits/static/foo")
+# root.mkdir(parents=True)
+# root.joinpath("example.css").write_text("")
+
+if __name__ == "__main__":
+ build()
+
+#def build(setup_kwargs):
+# """Build """
+# transpile_scss()
+# bundle_vendored()
+# subprocess.run(["ls", "-la", "./lnbits/static"])
diff --git a/docs/guide/installation.md b/docs/guide/installation.md
index 591516f1a..f38f606d1 100644
--- a/docs/guide/installation.md
+++ b/docs/guide/installation.md
@@ -8,13 +8,32 @@ nav_order: 2
# Basic installation
-You can choose between two python package managers, `venv` and `pipenv`. Both are fine but if you don't know what you're doing, just go for the first option.
+You can choose between four package managers, `poetry`, `pipenv`, `venv` and `nix`.
By default, LNbits will use SQLite as its database. You can also use PostgreSQL which is recommended for applications with a high load (see guide below).
-## Option 1: pipenv
+## Option 1: poetry
-You can also use Pipenv to manage your python packages.
+```sh
+git clone https://github.com/lnbits/lnbits-legend.git
+cd lnbits-legend/
+
+curl -sSL https://install.python-poetry.org | python3 -
+poetry install
+
+# You may need to install python 3.9, update your python following this guide https://linuxize.com/post/how-to-install-python-3-9-on-ubuntu-20-04/
+
+ mkdir data && cp .env.example .env
+```
+
+#### Running the server
+
+```sh
+poetry run lnbits
+# To change port/host pass 'poetry run lnbits --port 9000 --host 0.0.0.0'
+```
+
+## Option 2: pipenv
```sh
git clone https://github.com/lnbits/lnbits-legend.git
@@ -44,9 +63,7 @@ pipenv run python -m uvicorn lnbits.__main__:app --port 5000 --host 0.0.0.0
Add the flag `--reload` for development (includes hot-reload).
-## Option 2: venv
-
-Download this repo and install the dependencies:
+## Option 3: venv
```sh
git clone https://github.com/lnbits/lnbits-legend.git
@@ -67,6 +84,26 @@ mkdir data && cp .env.example .env
If you want to host LNbits on the internet, run with the option `--host 0.0.0.0`.
+## Option 4: Nix
+
+```sh
+git clone https://github.com/lnbits/lnbits-legend.git
+cd lnbits-legend/
+# Install nix, modern debian distros usually already include
+sh <(curl -L https://nixos.org/nix/install) --daemon
+
+nix build .#lnbits
+mkdir data
+
+```
+
+#### Running the server
+
+```sh
+# .env variables are currently passed when running
+LNBITS_DATA_FOLDER=data LNBITS_BACKEND_WALLET_CLASS=LNbitsWallet LNBITS_ENDPOINT=https://legend.lnbits.com LNBITS_KEY=7b1a78d6c78f48b09a202f2dcb2d22eb ./result/bin/lnbits --port 9000
+```
+
### Troubleshooting
Problems installing? These commands have helped us install LNbits.
diff --git a/docs/guide/wallets.md b/docs/guide/wallets.md
index 7a3b6a273..dfea66aa2 100644
--- a/docs/guide/wallets.md
+++ b/docs/guide/wallets.md
@@ -17,7 +17,6 @@ A backend wallet can be configured using the following LNbits environment variab
### CLightning
Using this wallet requires the installation of the `pylightning` Python package.
-If you want to use LNURLp you should use SparkWallet because of an issue with description_hash and CLightning.
- `LNBITS_BACKEND_WALLET_CLASS`: **CLightningWallet**
- `CLIGHTNING_RPC`: /file/path/lightning-rpc
diff --git a/flake.lock b/flake.lock
new file mode 100644
index 000000000..0ca2db014
--- /dev/null
+++ b/flake.lock
@@ -0,0 +1,77 @@
+{
+ "nodes": {
+ "flake-utils": {
+ "locked": {
+ "lastModified": 1656928814,
+ "narHash": "sha256-RIFfgBuKz6Hp89yRr7+NR5tzIAbn52h8vT6vXkYjZoM=",
+ "owner": "numtide",
+ "repo": "flake-utils",
+ "rev": "7e2a3b3dfd9af950a856d66b0a7d01e3c18aa249",
+ "type": "github"
+ },
+ "original": {
+ "owner": "numtide",
+ "repo": "flake-utils",
+ "type": "github"
+ }
+ },
+ "nixpkgs": {
+ "locked": {
+ "lastModified": 1657114324,
+ "narHash": "sha256-fWuaUNXrHcz/ciHRHlcSO92dvV3EVS0GJQUSBO5JIB4=",
+ "owner": "nixos",
+ "repo": "nixpkgs",
+ "rev": "a5c867d9fe9e4380452628e8f171c26b69fa9d3d",
+ "type": "github"
+ },
+ "original": {
+ "owner": "nixos",
+ "ref": "nixos-unstable",
+ "repo": "nixpkgs",
+ "type": "github"
+ }
+ },
+ "nixpkgs_2": {
+ "locked": {
+ "lastModified": 1657261001,
+ "narHash": "sha256-sUZeuRYfhG59uD6xafM07bc7bAIkpcGq84Vj4B+cyms=",
+ "owner": "NixOS",
+ "repo": "nixpkgs",
+ "rev": "0be91cefefde5701f8fa957904618a13e3bb51d8",
+ "type": "github"
+ },
+ "original": {
+ "owner": "NixOS",
+ "repo": "nixpkgs",
+ "type": "github"
+ }
+ },
+ "poetry2nix": {
+ "inputs": {
+ "flake-utils": "flake-utils",
+ "nixpkgs": "nixpkgs_2"
+ },
+ "locked": {
+ "lastModified": 1657149754,
+ "narHash": "sha256-iSnZoqwNDDVoO175whSuvl4sS9lAb/2zZ3Sa4ywo970=",
+ "owner": "nix-community",
+ "repo": "poetry2nix",
+ "rev": "fc1930e011dea149db81863aac22fe701f36f1b5",
+ "type": "github"
+ },
+ "original": {
+ "owner": "nix-community",
+ "repo": "poetry2nix",
+ "type": "github"
+ }
+ },
+ "root": {
+ "inputs": {
+ "nixpkgs": "nixpkgs",
+ "poetry2nix": "poetry2nix"
+ }
+ }
+ },
+ "root": "root",
+ "version": 7
+}
diff --git a/flake.nix b/flake.nix
new file mode 100644
index 000000000..af25ba5c3
--- /dev/null
+++ b/flake.nix
@@ -0,0 +1,55 @@
+{
+ inputs = {
+ nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
+ poetry2nix.url = "github:nix-community/poetry2nix";
+ };
+ outputs = { self, nixpkgs, poetry2nix }@inputs:
+ let
+ supportedSystems = [ "x86_64-linux" "aarch64-linux" ];
+ forSystems = systems: f:
+ nixpkgs.lib.genAttrs systems
+ (system: f system (import nixpkgs { inherit system; overlays = [ poetry2nix.overlay self.overlays.default ]; }));
+ forAllSystems = forSystems supportedSystems;
+ projectName = "lnbits";
+ in
+ {
+ devShells = forAllSystems (system: pkgs: {
+ default = pkgs.mkShell {
+ buildInputs = with pkgs; [
+ nodePackages.prettier
+ ];
+ };
+ });
+ overlays = {
+ default = final: prev: {
+ ${projectName} = self.packages.${final.hostPlatform.system}.${projectName};
+ };
+ };
+ packages = forAllSystems (system: pkgs: {
+ default = self.packages.${system}.${projectName};
+ ${projectName} = pkgs.poetry2nix.mkPoetryApplication {
+ projectDir = ./.;
+ python = pkgs.python39;
+ };
+ });
+ nixosModules = {
+ default = { pkgs, lib, config, ... }: {
+ imports = [ "${./nix/modules/${projectName}-service.nix}" ];
+ nixpkgs.overlays = [ self.overlays.default ];
+ };
+ };
+ checks = forAllSystems (system: pkgs:
+ let
+ vmTests = import ./nix/tests {
+ makeTest = (import (nixpkgs + "/nixos/lib/testing-python.nix") { inherit system; }).makeTest;
+ inherit inputs pkgs;
+ };
+ in
+ pkgs.lib.optionalAttrs pkgs.stdenv.isLinux vmTests # vmTests can only be ran on Linux, so append them only if on Linux.
+ //
+ {
+ # Other checks here...
+ }
+ );
+ };
+}
diff --git a/lnbits/__main__.py b/lnbits/__main__.py
index 90464a64b..90cb1997b 100644
--- a/lnbits/__main__.py
+++ b/lnbits/__main__.py
@@ -4,7 +4,7 @@ import uvloop
from loguru import logger
from starlette.requests import Request
-from .commands import bundle_vendored, migrate_databases, transpile_scss
+from .commands import migrate_databases
from .settings import (
DEBUG,
HOST,
@@ -19,8 +19,6 @@ from .settings import (
uvloop.install()
asyncio.create_task(migrate_databases())
-transpile_scss()
-bundle_vendored()
from .app import create_app
diff --git a/lnbits/app.py b/lnbits/app.py
index a7c8fdaf3..e1594a7b4 100644
--- a/lnbits/app.py
+++ b/lnbits/app.py
@@ -17,7 +17,6 @@ from loguru import logger
import lnbits.settings
from lnbits.core.tasks import register_task_listeners
-from .commands import db_migrate, handle_assets
from .core import core_app
from .core.views.generic import core_html_routes
from .helpers import (
@@ -45,10 +44,19 @@ def create_app(config_object="lnbits.settings") -> FastAPI:
"""
configure_logger()
- app = FastAPI()
- app.mount("/static", StaticFiles(directory="lnbits/static"), name="static")
+ app = FastAPI(
+ title="LNbits API",
+ description="API for LNbits, the free and open source bitcoin wallet and accounts system with plugins.",
+ license_info={
+ "name": "MIT License",
+ "url": "https://raw.githubusercontent.com/lnbits/lnbits-legend/main/LICENSE",
+ },
+ )
+ app.mount("/static", StaticFiles(packages=[("lnbits", "static")]), name="static")
app.mount(
- "/core/static", StaticFiles(directory="lnbits/core/static"), name="core_static"
+ "/core/static",
+ StaticFiles(packages=[("lnbits.core", "static")]),
+ name="core_static",
)
origins = ["*"]
@@ -84,7 +92,6 @@ def create_app(config_object="lnbits.settings") -> FastAPI:
check_funding_source(app)
register_assets(app)
register_routes(app)
- # register_commands(app)
register_async_tasks(app)
register_exception_handlers(app)
@@ -137,12 +144,6 @@ def register_routes(app: FastAPI) -> None:
)
-def register_commands(app: FastAPI):
- """Register Click commands."""
- app.cli.add_command(db_migrate)
- app.cli.add_command(handle_assets)
-
-
def register_assets(app: FastAPI):
"""Serve each vendored asset separately or a bundle."""
diff --git a/lnbits/core/crud.py b/lnbits/core/crud.py
index 770e2906a..db802d7bb 100644
--- a/lnbits/core/crud.py
+++ b/lnbits/core/crud.py
@@ -113,7 +113,7 @@ async def create_wallet(
async def update_wallet(
wallet_id: str, new_name: str, conn: Optional[Connection] = None
) -> Optional[Wallet]:
- await (conn or db).execute(
+ return await (conn or db).execute(
"""
UPDATE wallets SET
name = ?
diff --git a/lnbits/core/models.py b/lnbits/core/models.py
index ab73b7020..0f7eba737 100644
--- a/lnbits/core/models.py
+++ b/lnbits/core/models.py
@@ -106,6 +106,8 @@ class Payment(BaseModel):
@property
def tag(self) -> Optional[str]:
+ if self.extra is None:
+ return ""
return self.extra.get("tag")
@property
diff --git a/lnbits/core/services.py b/lnbits/core/services.py
index 0b565ebb9..b9aec8997 100644
--- a/lnbits/core/services.py
+++ b/lnbits/core/services.py
@@ -109,18 +109,15 @@ async def pay_invoice(
raise ValueError("Amount in invoice is too high.")
# put all parameters that don't change here
- PaymentKwargs = TypedDict(
- "PaymentKwargs",
- {
- "wallet_id": str,
- "payment_request": str,
- "payment_hash": str,
- "amount": int,
- "memo": str,
- "extra": Optional[Dict],
- },
- )
- payment_kwargs: PaymentKwargs = dict(
+ class PaymentKwargs(TypedDict):
+ wallet_id: str
+ payment_request: str
+ payment_hash: str
+ amount: int
+ memo: str
+ extra: Optional[Dict]
+
+ payment_kwargs: PaymentKwargs = PaymentKwargs(
wallet_id=wallet_id,
payment_request=payment_request,
payment_hash=invoice.payment_hash,
@@ -272,6 +269,7 @@ async def perform_lnurlauth(
cb = urlparse(callback)
k1 = unhexlify(parse_qs(cb.query)["k1"][0])
+
key = wallet.wallet.lnurlauth_key(cb.netloc)
def int_to_bytes_suitable_der(x: int) -> bytes:
diff --git a/lnbits/core/tasks.py b/lnbits/core/tasks.py
index 5fea769d4..07b8a8933 100644
--- a/lnbits/core/tasks.py
+++ b/lnbits/core/tasks.py
@@ -55,7 +55,7 @@ async def dispatch_webhook(payment: Payment):
data = payment.dict()
try:
logger.debug("sending webhook", payment.webhook)
- r = await client.post(payment.webhook, json=data, timeout=40)
+ r = await client.post(payment.webhook, json=data, timeout=40) # type: ignore
await mark_webhook_sent(payment, r.status_code)
except (httpx.ConnectError, httpx.RequestError):
await mark_webhook_sent(payment, -1)
diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py
index bc3e759fa..623b979a8 100644
--- a/lnbits/core/views/api.py
+++ b/lnbits/core/views/api.py
@@ -3,7 +3,7 @@ import hashlib
import json
from binascii import unhexlify
from http import HTTPStatus
-from typing import Dict, List, Optional, Union
+from typing import Dict, List, Optional, Tuple, Union
from urllib.parse import ParseResult, parse_qs, urlencode, urlparse, urlunparse
import httpx
@@ -185,7 +185,7 @@ async def api_payments_create_invoice(data: CreateInvoiceData, wallet: Wallet):
assert (
data.lnurl_balance_check is not None
), "lnurl_balance_check is required"
- save_balance_check(wallet.id, data.lnurl_balance_check)
+ await save_balance_check(wallet.id, data.lnurl_balance_check)
async with httpx.AsyncClient() as client:
try:
@@ -248,7 +248,7 @@ async def api_payments_pay_invoice(bolt11: str, wallet: Wallet):
)
async def api_payments_create(
wallet: WalletTypeInfo = Depends(require_invoice_key),
- invoiceData: CreateInvoiceData = Body(...),
+ invoiceData: CreateInvoiceData = Body(...), # type: ignore
):
if invoiceData.out is True and wallet.wallet_type == 0:
if not invoiceData.bolt11:
@@ -291,7 +291,7 @@ async def api_payments_pay_lnurl(
timeout=40,
)
if r.is_error:
- raise httpx.ConnectError
+ raise httpx.ConnectError("LNURL callback connection error")
except (httpx.ConnectError, httpx.RequestError):
raise HTTPException(
status_code=HTTPStatus.BAD_REQUEST,
@@ -354,7 +354,7 @@ async def subscribe(request: Request, wallet: Wallet):
logger.debug("adding sse listener", payment_queue)
api_invoice_listeners.append(payment_queue)
- send_queue: asyncio.Queue[tuple[str, Payment]] = asyncio.Queue(0)
+ send_queue: asyncio.Queue[Tuple[str, Payment]] = asyncio.Queue(0)
async def payment_received() -> None:
while True:
@@ -393,16 +393,13 @@ async def api_payments_sse(
async def api_payment(payment_hash, X_Api_Key: Optional[str] = Header(None)):
# We use X_Api_Key here because we want this call to work with and without keys
# If a valid key is given, we also return the field "details", otherwise not
- wallet = None
- try:
- if X_Api_Key.extra:
- logger.warning("No key")
- except:
- wallet = await get_wallet_for_key(X_Api_Key)
+ wallet = await get_wallet_for_key(X_Api_Key) if type(X_Api_Key) == str else None
+
+ # we have to specify the wallet id here, because postgres and sqlite return internal payments in different order
+ # and get_standalone_payment otherwise just fetches the first one, causing unpredictable results
payment = await get_standalone_payment(
payment_hash, wallet_id=wallet.id if wallet else None
- ) # we have to specify the wallet id here, because postgres and sqlite return internal payments in different order
- # and get_standalone_payment otherwise just fetches the first one, causing unpredictable results
+ )
if payment is None:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND, detail="Payment does not exist."
@@ -488,7 +485,8 @@ async def api_lnurlscan(code: str, wallet: WalletTypeInfo = Depends(get_key_type
)
try:
- tag = data["tag"]
+ tag: str = data.get("tag")
+ params.update(**data)
if tag == "channelRequest":
raise HTTPException(
status_code=HTTPStatus.BAD_REQUEST,
@@ -498,10 +496,7 @@ async def api_lnurlscan(code: str, wallet: WalletTypeInfo = Depends(get_key_type
"message": "unsupported",
},
)
-
- params.update(**data)
-
- if tag == "withdrawRequest":
+ elif tag == "withdrawRequest":
params.update(kind="withdraw")
params.update(fixed=data["minWithdrawable"] == data["maxWithdrawable"])
@@ -519,8 +514,7 @@ async def api_lnurlscan(code: str, wallet: WalletTypeInfo = Depends(get_key_type
query=urlencode(qs, doseq=True)
)
params.update(callback=urlunparse(parsed_callback))
-
- if tag == "payRequest":
+ elif tag == "payRequest":
params.update(kind="pay")
params.update(fixed=data["minSendable"] == data["maxSendable"])
@@ -538,8 +532,8 @@ async def api_lnurlscan(code: str, wallet: WalletTypeInfo = Depends(get_key_type
params.update(image=data_uri)
if k == "text/email" or k == "text/identifier":
params.update(targetUser=v)
-
params.update(commentAllowed=data.get("commentAllowed", 0))
+
except KeyError as exc:
raise HTTPException(
status_code=HTTPStatus.SERVICE_UNAVAILABLE,
@@ -612,8 +606,8 @@ class ConversionData(BaseModel):
async def api_fiat_as_sats(data: ConversionData):
output = {}
if data.from_ == "sat":
- output["sats"] = int(data.amount)
output["BTC"] = data.amount / 100000000
+ output["sats"] = int(data.amount)
for currency in data.to.split(","):
output[currency.strip().upper()] = await satoshis_amount_as_fiat(
data.amount, currency.strip()
diff --git a/lnbits/core/views/generic.py b/lnbits/core/views/generic.py
index 44666ce16..21572b285 100644
--- a/lnbits/core/views/generic.py
+++ b/lnbits/core/views/generic.py
@@ -55,9 +55,9 @@ async def home(request: Request, lightning: str = None):
)
async def extensions(
request: Request,
- user: User = Depends(check_user_exists),
- enable: str = Query(None),
- disable: str = Query(None),
+ user: User = Depends(check_user_exists), # type: ignore
+ enable: str = Query(None), # type: ignore
+ disable: str = Query(None), # type: ignore
):
extension_to_enable = enable
extension_to_disable = disable
@@ -88,7 +88,7 @@ async def extensions(
# Update user as his extensions have been updated
if extension_to_enable or extension_to_disable:
- user = await get_user(user.id)
+ user = await get_user(user.id) # type: ignore
return template_renderer().TemplateResponse(
"core/extensions.html", {"request": request, "user": user.dict()}
@@ -109,10 +109,10 @@ nothing: create everything
""",
)
async def wallet(
- request: Request = Query(None),
- nme: Optional[str] = Query(None),
- usr: Optional[UUID4] = Query(None),
- wal: Optional[UUID4] = Query(None),
+ request: Request = Query(None), # type: ignore
+ nme: Optional[str] = Query(None), # type: ignore
+ usr: Optional[UUID4] = Query(None), # type: ignore
+ wal: Optional[UUID4] = Query(None), # type: ignore
):
user_id = usr.hex if usr else None
wallet_id = wal.hex if wal else None
@@ -121,7 +121,7 @@ async def wallet(
if not user_id:
user = await get_user((await create_account()).id)
- logger.info(f"Create user {user.id}")
+ logger.info(f"Create user {user.id}") # type: ignore
else:
user = await get_user(user_id)
if not user:
@@ -135,22 +135,22 @@ async def wallet(
if LNBITS_ADMIN_USERS and user_id in LNBITS_ADMIN_USERS:
user.admin = True
if not wallet_id:
- if user.wallets and not wallet_name:
- wallet = user.wallets[0]
+ if user.wallets and not wallet_name: # type: ignore
+ wallet = user.wallets[0] # type: ignore
else:
- wallet = await create_wallet(user_id=user.id, wallet_name=wallet_name)
+ wallet = await create_wallet(user_id=user.id, wallet_name=wallet_name) # type: ignore
logger.info(
- f"Created new wallet {wallet_name if wallet_name else '(no name)'} for user {user.id}"
+ f"Created new wallet {wallet_name if wallet_name else '(no name)'} for user {user.id}" # type: ignore
)
return RedirectResponse(
- f"/wallet?usr={user.id}&wal={wallet.id}",
+ f"/wallet?usr={user.id}&wal={wallet.id}", # type: ignore
status_code=status.HTTP_307_TEMPORARY_REDIRECT,
)
logger.debug(f"Access wallet {wallet_name}{'of user '+ user.id if user else ''}")
- wallet = user.get_wallet(wallet_id)
- if not wallet:
+ userwallet = user.get_wallet(wallet_id) # type: ignore
+ if not userwallet:
return template_renderer().TemplateResponse(
"error.html", {"request": request, "err": "Wallet not found"}
)
@@ -159,10 +159,10 @@ async def wallet(
"core/wallet.html",
{
"request": request,
- "user": user.dict(),
- "wallet": wallet.dict(),
+ "user": user.dict(), # type: ignore
+ "wallet": userwallet.dict(),
"service_fee": service_fee,
- "web_manifest": f"/manifest/{user.id}.webmanifest",
+ "web_manifest": f"/manifest/{user.id}.webmanifest", # type: ignore
},
)
@@ -216,20 +216,20 @@ async def lnurl_full_withdraw_callback(request: Request):
@core_html_routes.get("/deletewallet", response_class=RedirectResponse)
-async def deletewallet(request: Request, wal: str = Query(...), usr: str = Query(...)):
+async def deletewallet(request: Request, wal: str = Query(...), usr: str = Query(...)): # type: ignore
user = await get_user(usr)
- user_wallet_ids = [u.id for u in user.wallets]
+ user_wallet_ids = [u.id for u in user.wallets] # type: ignore
if wal not in user_wallet_ids:
raise HTTPException(HTTPStatus.FORBIDDEN, "Not your wallet.")
else:
- await delete_wallet(user_id=user.id, wallet_id=wal)
+ await delete_wallet(user_id=user.id, wallet_id=wal) # type: ignore
user_wallet_ids.remove(wal)
logger.debug("Deleted wallet {wal} of user {user.id}")
if user_wallet_ids:
return RedirectResponse(
- url_for("/wallet", usr=user.id, wal=user_wallet_ids[0]),
+ url_for("/wallet", usr=user.id, wal=user_wallet_ids[0]), # type: ignore
status_code=status.HTTP_307_TEMPORARY_REDIRECT,
)
@@ -242,7 +242,7 @@ async def deletewallet(request: Request, wal: str = Query(...), usr: str = Query
async def lnurl_balance_notify(request: Request, service: str):
bc = await get_balance_check(request.query_params.get("wal"), service)
if bc:
- redeem_lnurl_withdraw(bc.wallet, bc.url)
+ await redeem_lnurl_withdraw(bc.wallet, bc.url)
@core_html_routes.get(
@@ -252,7 +252,7 @@ async def lnurlwallet(request: Request):
async with db.connect() as conn:
account = await create_account(conn=conn)
user = await get_user(account.id, conn=conn)
- wallet = await create_wallet(user_id=user.id, conn=conn)
+ wallet = await create_wallet(user_id=user.id, conn=conn) # type: ignore
asyncio.create_task(
redeem_lnurl_withdraw(
@@ -265,7 +265,7 @@ async def lnurlwallet(request: Request):
)
return RedirectResponse(
- f"/wallet?usr={user.id}&wal={wallet.id}",
+ f"/wallet?usr={user.id}&wal={wallet.id}", # type: ignore
status_code=status.HTTP_307_TEMPORARY_REDIRECT,
)
diff --git a/lnbits/decorators.py b/lnbits/decorators.py
index e65b9041a..6685cfb2d 100644
--- a/lnbits/decorators.py
+++ b/lnbits/decorators.py
@@ -1,4 +1,5 @@
from http import HTTPStatus
+from typing import Union
from cerberus import Validator # type: ignore
from fastapi import status
@@ -29,20 +30,21 @@ class KeyChecker(SecurityBase):
self._key_type = "invoice"
self._api_key = api_key
if api_key:
- self.model: APIKey = APIKey(
+ key = APIKey(
**{"in": APIKeyIn.query},
name="X-API-KEY",
description="Wallet API Key - QUERY",
)
else:
- self.model: APIKey = APIKey(
+ key = APIKey(
**{"in": APIKeyIn.header},
name="X-API-KEY",
description="Wallet API Key - HEADER",
)
- self.wallet = None
+ self.wallet = None # type: ignore
+ self.model: APIKey = key
- async def __call__(self, request: Request) -> Wallet:
+ async def __call__(self, request: Request):
try:
key_value = (
self._api_key
@@ -52,7 +54,7 @@ class KeyChecker(SecurityBase):
# FIXME: Find another way to validate the key. A fetch from DB should be avoided here.
# Also, we should not return the wallet here - thats silly.
# Possibly store it in a Redis DB
- self.wallet = await get_wallet_for_key(key_value, self._key_type)
+ self.wallet = await get_wallet_for_key(key_value, self._key_type) # type: ignore
if not self.wallet:
raise HTTPException(
status_code=HTTPStatus.UNAUTHORIZED,
@@ -120,8 +122,8 @@ api_key_query = APIKeyQuery(
async def get_key_type(
r: Request,
- api_key_header: str = Security(api_key_header),
- api_key_query: str = Security(api_key_query),
+ api_key_header: str = Security(api_key_header), # type: ignore
+ api_key_query: str = Security(api_key_query), # type: ignore
) -> WalletTypeInfo:
# 0: admin
# 1: invoice
@@ -134,9 +136,9 @@ async def get_key_type(
token = api_key_header if api_key_header else api_key_query
try:
- checker = WalletAdminKeyChecker(api_key=token)
- await checker.__call__(r)
- wallet = WalletTypeInfo(0, checker.wallet)
+ admin_checker = WalletAdminKeyChecker(api_key=token)
+ await admin_checker.__call__(r)
+ wallet = WalletTypeInfo(0, admin_checker.wallet) # type: ignore
if (LNBITS_ADMIN_USERS and wallet.wallet.user not in LNBITS_ADMIN_USERS) and (
LNBITS_ADMIN_EXTENSIONS and pathname in LNBITS_ADMIN_EXTENSIONS
):
@@ -153,9 +155,9 @@ async def get_key_type(
raise
try:
- checker = WalletInvoiceKeyChecker(api_key=token)
- await checker.__call__(r)
- wallet = WalletTypeInfo(1, checker.wallet)
+ invoice_checker = WalletInvoiceKeyChecker(api_key=token)
+ await invoice_checker.__call__(r)
+ wallet = WalletTypeInfo(1, invoice_checker.wallet) # type: ignore
if (LNBITS_ADMIN_USERS and wallet.wallet.user not in LNBITS_ADMIN_USERS) and (
LNBITS_ADMIN_EXTENSIONS and pathname in LNBITS_ADMIN_EXTENSIONS
):
@@ -167,15 +169,16 @@ async def get_key_type(
if e.status_code == HTTPStatus.BAD_REQUEST:
raise
if e.status_code == HTTPStatus.UNAUTHORIZED:
- return WalletTypeInfo(2, None)
+ return WalletTypeInfo(2, None) # type: ignore
except:
raise
+ return wallet
async def require_admin_key(
r: Request,
- api_key_header: str = Security(api_key_header),
- api_key_query: str = Security(api_key_query),
+ api_key_header: str = Security(api_key_header), # type: ignore
+ api_key_query: str = Security(api_key_query), # type: ignore
):
token = api_key_header if api_key_header else api_key_query
@@ -193,8 +196,8 @@ async def require_admin_key(
async def require_invoice_key(
r: Request,
- api_key_header: str = Security(api_key_header),
- api_key_query: str = Security(api_key_query),
+ api_key_header: str = Security(api_key_header), # type: ignore
+ api_key_query: str = Security(api_key_query), # type: ignore
):
token = api_key_header if api_key_header else api_key_query
diff --git a/lnbits/extensions/bleskomat/__init__.py b/lnbits/extensions/bleskomat/__init__.py
index cc89760e5..bef362dc7 100644
--- a/lnbits/extensions/bleskomat/__init__.py
+++ b/lnbits/extensions/bleskomat/__init__.py
@@ -9,7 +9,7 @@ db = Database("ext_bleskomat")
bleskomat_static_files = [
{
"path": "/bleskomat/static",
- "app": StaticFiles(directory="lnbits/extensions/bleskomat/static"),
+ "app": StaticFiles(packages=[("lnbits", "extensions/bleskomat/static")]),
"name": "bleskomat_static",
}
]
diff --git a/lnbits/extensions/bleskomat/templates/bleskomat/_api_docs.html b/lnbits/extensions/bleskomat/templates/bleskomat/_api_docs.html
index 210d534c2..2a7160bd8 100644
--- a/lnbits/extensions/bleskomat/templates/bleskomat/_api_docs.html
+++ b/lnbits/extensions/bleskomat/templates/bleskomat/_api_docs.html
@@ -62,4 +62,5 @@
+
diff --git a/lnbits/extensions/copilot/__init__.py b/lnbits/extensions/copilot/__init__.py
index 8a6342678..806801ce1 100644
--- a/lnbits/extensions/copilot/__init__.py
+++ b/lnbits/extensions/copilot/__init__.py
@@ -12,7 +12,7 @@ db = Database("ext_copilot")
copilot_static_files = [
{
"path": "/copilot/static",
- "app": StaticFiles(directory="lnbits/extensions/copilot/static"),
+ "app": StaticFiles(packages=[("lnbits", "extensions/copilot/static")]),
"name": "copilot_static",
}
]
diff --git a/lnbits/extensions/copilot/templates/copilot/_api_docs.html b/lnbits/extensions/copilot/templates/copilot/_api_docs.html
index eed25169b..6105d1691 100644
--- a/lnbits/extensions/copilot/templates/copilot/_api_docs.html
+++ b/lnbits/extensions/copilot/templates/copilot/_api_docs.html
@@ -14,6 +14,7 @@
label="API info"
:content-inset-level="0.5"
>
+
diff --git a/lnbits/extensions/discordbot/__init__.py b/lnbits/extensions/discordbot/__init__.py
index ff60dd628..21989b24a 100644
--- a/lnbits/extensions/discordbot/__init__.py
+++ b/lnbits/extensions/discordbot/__init__.py
@@ -9,7 +9,7 @@ db = Database("ext_discordbot")
discordbot_static_files = [
{
"path": "/discordbot/static",
- "app": StaticFiles(directory="lnbits/extensions/discordbot/static"),
+ "app": StaticFiles(packages=[("lnbits", "extensions/discordbot/static")]),
"name": "discordbot_static",
}
]
diff --git a/lnbits/extensions/discordbot/templates/discordbot/_api_docs.html b/lnbits/extensions/discordbot/templates/discordbot/_api_docs.html
index fcda086a3..b57e4ab1f 100644
--- a/lnbits/extensions/discordbot/templates/discordbot/_api_docs.html
+++ b/lnbits/extensions/discordbot/templates/discordbot/_api_docs.html
@@ -34,6 +34,7 @@
label="API info"
:content-inset-level="0.5"
>
+
diff --git a/lnbits/extensions/events/crud.py b/lnbits/extensions/events/crud.py
index 9e04476d3..4cc86ac4d 100644
--- a/lnbits/extensions/events/crud.py
+++ b/lnbits/extensions/events/crud.py
@@ -16,7 +16,7 @@ async def create_ticket(
INSERT INTO events.ticket (id, wallet, event, name, email, registered, paid)
VALUES (?, ?, ?, ?, ?, ?, ?)
""",
- (payment_hash, wallet, event, name, email, False, False),
+ (payment_hash, wallet, event, name, email, False, True),
)
ticket = await get_ticket(payment_hash)
diff --git a/lnbits/extensions/events/templates/events/_api_docs.html b/lnbits/extensions/events/templates/events/_api_docs.html
index a5c821747..d2fa890e5 100644
--- a/lnbits/extensions/events/templates/events/_api_docs.html
+++ b/lnbits/extensions/events/templates/events/_api_docs.html
@@ -20,4 +20,5 @@
+
diff --git a/lnbits/extensions/events/templates/events/display.html b/lnbits/extensions/events/templates/events/display.html
index 4c1f557f1..4589c5785 100644
--- a/lnbits/extensions/events/templates/events/display.html
+++ b/lnbits/extensions/events/templates/events/display.html
@@ -135,15 +135,7 @@
var self = this
axios
- .post(
- '/events/api/v1/tickets/' + '{{ event_id }}/{{ event_price }}',
- {
- event: '{{ event_id }}',
- event_name: '{{ event_name }}',
- name: self.formDialog.data.name,
- email: self.formDialog.data.email
- }
- )
+ .get('/events/api/v1/tickets/' + '{{ event_id }}')
.then(function (response) {
self.paymentReq = response.data.payment_request
self.paymentCheck = response.data.payment_hash
@@ -161,7 +153,17 @@
paymentChecker = setInterval(function () {
axios
- .get('/events/api/v1/tickets/' + self.paymentCheck)
+ .post(
+ '/events/api/v1/tickets/' +
+ '{{ event_id }}/' +
+ self.paymentCheck,
+ {
+ event: '{{ event_id }}',
+ event_name: '{{ event_name }}',
+ name: self.formDialog.data.name,
+ email: self.formDialog.data.email
+ }
+ )
.then(function (res) {
if (res.data.paid) {
clearInterval(paymentChecker)
diff --git a/lnbits/extensions/events/templates/events/register.html b/lnbits/extensions/events/templates/events/register.html
index 4dff9afbb..43d43070b 100644
--- a/lnbits/extensions/events/templates/events/register.html
+++ b/lnbits/extensions/events/templates/events/register.html
@@ -133,7 +133,10 @@
var self = this
LNbits.api
- .request('GET', '/events/api/v1/register/ticket/' + res)
+ .request(
+ 'GET',
+ '/events/api/v1/register/ticket/' + res.split('//')[1]
+ )
.then(function (response) {
self.$q.notify({
type: 'positive',
diff --git a/lnbits/extensions/events/templates/events/ticket.html b/lnbits/extensions/events/templates/events/ticket.html
index a53f834f9..21b7cfa82 100644
--- a/lnbits/extensions/events/templates/events/ticket.html
+++ b/lnbits/extensions/events/templates/events/ticket.html
@@ -13,9 +13,8 @@
diff --git a/lnbits/extensions/events/views_api.py b/lnbits/extensions/events/views_api.py
index 87edb07da..56e6b06ca 100644
--- a/lnbits/extensions/events/views_api.py
+++ b/lnbits/extensions/events/views_api.py
@@ -97,8 +97,8 @@ async def api_tickets(
return [ticket.dict() for ticket in await get_tickets(wallet_ids)]
-@events_ext.post("/api/v1/tickets/{event_id}/{sats}")
-async def api_ticket_make_ticket(event_id, sats, data: CreateTicket):
+@events_ext.get("/api/v1/tickets/{event_id}")
+async def api_ticket_make_ticket(event_id):
event = await get_event(event_id)
if not event:
raise HTTPException(
@@ -107,37 +107,36 @@ async def api_ticket_make_ticket(event_id, sats, data: CreateTicket):
try:
payment_hash, payment_request = await create_invoice(
wallet_id=event.wallet,
- amount=int(sats),
+ amount=event.price_per_ticket,
memo=f"{event_id}",
extra={"tag": "events"},
)
except Exception as e:
raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=str(e))
- ticket = await create_ticket(
- payment_hash=payment_hash,
- wallet=event.wallet,
- event=event_id,
- name=data.name,
- email=data.email,
- )
-
- if not ticket:
- raise HTTPException(
- status_code=HTTPStatus.NOT_FOUND, detail=f"Event could not be fetched."
- )
-
return {"payment_hash": payment_hash, "payment_request": payment_request}
-@events_ext.get("/api/v1/tickets/{payment_hash}")
-async def api_ticket_send_ticket(payment_hash):
- ticket = await get_ticket(payment_hash)
-
+@events_ext.post("/api/v1/tickets/{event_id}/{payment_hash}")
+async def api_ticket_send_ticket(event_id, payment_hash, data: CreateTicket):
+ event = await get_event(event_id)
try:
status = await api_payment(payment_hash)
if status["paid"]:
- await set_ticket_paid(payment_hash=payment_hash)
+ ticket = await create_ticket(
+ payment_hash=payment_hash,
+ wallet=event.wallet,
+ event=event_id,
+ name=data.name,
+ email=data.email,
+ )
+
+ if not ticket:
+ raise HTTPException(
+ status_code=HTTPStatus.NOT_FOUND,
+ detail=f"Event could not be fetched.",
+ )
+
return {"paid": True, "ticket_id": ticket.id}
except Exception:
raise HTTPException(status_code=HTTPStatus.NOT_FOUND, detail="Not paid")
diff --git a/lnbits/extensions/jukebox/__init__.py b/lnbits/extensions/jukebox/__init__.py
index 702a6c67b..4559dccfd 100644
--- a/lnbits/extensions/jukebox/__init__.py
+++ b/lnbits/extensions/jukebox/__init__.py
@@ -12,7 +12,7 @@ db = Database("ext_jukebox")
jukebox_static_files = [
{
"path": "/jukebox/static",
- "app": StaticFiles(directory="lnbits/extensions/jukebox/static"),
+ "app": StaticFiles(packages=[("lnbits", "extensions/jukebox/static")]),
"name": "jukebox_static",
}
]
diff --git a/lnbits/extensions/jukebox/templates/jukebox/_api_docs.html b/lnbits/extensions/jukebox/templates/jukebox/_api_docs.html
index f4daaf39e..4bae49651 100644
--- a/lnbits/extensions/jukebox/templates/jukebox/_api_docs.html
+++ b/lnbits/extensions/jukebox/templates/jukebox/_api_docs.html
@@ -24,6 +24,8 @@
label="API info"
:content-inset-level="0.5"
>
+
+
diff --git a/lnbits/extensions/livestream/__init__.py b/lnbits/extensions/livestream/__init__.py
index 17fc2d3b8..e5f2987c8 100644
--- a/lnbits/extensions/livestream/__init__.py
+++ b/lnbits/extensions/livestream/__init__.py
@@ -12,7 +12,7 @@ db = Database("ext_livestream")
livestream_static_files = [
{
"path": "/livestream/static",
- "app": StaticFiles(directory="lnbits/extensions/livestream/static"),
+ "app": StaticFiles(packages=[("lnbits", "extensions/livestream/static")]),
"name": "livestream_static",
}
]
diff --git a/lnbits/extensions/livestream/templates/livestream/_api_docs.html b/lnbits/extensions/livestream/templates/livestream/_api_docs.html
index fb4853ced..34b6518dc 100644
--- a/lnbits/extensions/livestream/templates/livestream/_api_docs.html
+++ b/lnbits/extensions/livestream/templates/livestream/_api_docs.html
@@ -17,6 +17,8 @@
label="API info"
:content-inset-level="0.5"
>
+
+
+
diff --git a/lnbits/extensions/lndhub/templates/lndhub/_instructions.html b/lnbits/extensions/lndhub/templates/lndhub/_instructions.html
index 4db79aba8..005bced5e 100644
--- a/lnbits/extensions/lndhub/templates/lndhub/_instructions.html
+++ b/lnbits/extensions/lndhub/templates/lndhub/_instructions.html
@@ -31,5 +31,6 @@
+
diff --git a/lnbits/extensions/lnticket/templates/lnticket/_api_docs.html b/lnbits/extensions/lnticket/templates/lnticket/_api_docs.html
index 69328f384..e93409244 100644
--- a/lnbits/extensions/lnticket/templates/lnticket/_api_docs.html
+++ b/lnbits/extensions/lnticket/templates/lnticket/_api_docs.html
@@ -19,4 +19,5 @@
+
diff --git a/lnbits/extensions/lnurldevice/templates/lnurldevice/_api_docs.html b/lnbits/extensions/lnurldevice/templates/lnurldevice/_api_docs.html
index 940d4691e..7f9afa27b 100644
--- a/lnbits/extensions/lnurldevice/templates/lnurldevice/_api_docs.html
+++ b/lnbits/extensions/lnurldevice/templates/lnurldevice/_api_docs.html
@@ -17,6 +17,12 @@
label="API info"
:content-inset-level="0.5"
>
+
+
@@ -51,6 +52,7 @@
expand-separator
label="Create a pay link"
>
+
POST /lnurlp/api/v1/links
diff --git a/lnbits/extensions/lnurlpayout/config.json.example b/lnbits/extensions/lnurlpayout/config.json.example
index 1e72c0c1e..b4160d7bd 100644
--- a/lnbits/extensions/lnurlpayout/config.json.example
+++ b/lnbits/extensions/lnurlpayout/config.json.example
@@ -2,5 +2,5 @@
"name": "LNURLPayout",
"short_description": "Autodump wallet funds to LNURLpay",
"icon": "exit_to_app",
- "contributors": ["arcbtc"]
+ "contributors": ["arcbtc","talvasconcelos"]
}
diff --git a/lnbits/extensions/lnurlpayout/templates/lnurlpayout/_api_docs.html b/lnbits/extensions/lnurlpayout/templates/lnurlpayout/_api_docs.html
index 7febea44c..afe24c423 100644
--- a/lnbits/extensions/lnurlpayout/templates/lnurlpayout/_api_docs.html
+++ b/lnbits/extensions/lnurlpayout/templates/lnurlpayout/_api_docs.html
@@ -4,6 +4,7 @@
label="API info"
:content-inset-level="0.5"
>
+
diff --git a/lnbits/extensions/offlineshop/__init__.py b/lnbits/extensions/offlineshop/__init__.py
index a601c1b85..0b776a8ce 100644
--- a/lnbits/extensions/offlineshop/__init__.py
+++ b/lnbits/extensions/offlineshop/__init__.py
@@ -9,7 +9,7 @@ db = Database("ext_offlineshop")
offlineshop_static_files = [
{
"path": "/offlineshop/static",
- "app": StaticFiles(directory="lnbits/extensions/offlineshop/static"),
+ "app": StaticFiles(packages=[("lnbits", "extensions/offlineshop/static")]),
"name": "offlineshop_static",
}
]
diff --git a/lnbits/extensions/offlineshop/templates/offlineshop/_api_docs.html b/lnbits/extensions/offlineshop/templates/offlineshop/_api_docs.html
index a472d549e..0a4b9df8c 100644
--- a/lnbits/extensions/offlineshop/templates/offlineshop/_api_docs.html
+++ b/lnbits/extensions/offlineshop/templates/offlineshop/_api_docs.html
@@ -47,6 +47,7 @@
label="API info"
:content-inset-level="0.5"
>
+
+
diff --git a/lnbits/extensions/satsdice/templates/satsdice/_api_docs.html b/lnbits/extensions/satsdice/templates/satsdice/_api_docs.html
index a80fd37ad..e85e9586c 100644
--- a/lnbits/extensions/satsdice/templates/satsdice/_api_docs.html
+++ b/lnbits/extensions/satsdice/templates/satsdice/_api_docs.html
@@ -4,6 +4,7 @@
label="API info"
:content-inset-level="0.5"
>
+
diff --git a/lnbits/extensions/satspay/templates/satspay/_api_docs.html b/lnbits/extensions/satspay/templates/satspay/_api_docs.html
index 77451ae59..336ab8997 100644
--- a/lnbits/extensions/satspay/templates/satspay/_api_docs.html
+++ b/lnbits/extensions/satspay/templates/satspay/_api_docs.html
@@ -15,6 +15,7 @@
label="API info"
:content-inset-level="0.5"
>
+
diff --git a/lnbits/extensions/splitpayments/__init__.py b/lnbits/extensions/splitpayments/__init__.py
index df6feb943..9989728ea 100644
--- a/lnbits/extensions/splitpayments/__init__.py
+++ b/lnbits/extensions/splitpayments/__init__.py
@@ -12,7 +12,7 @@ db = Database("ext_splitpayments")
splitpayments_static_files = [
{
"path": "/splitpayments/static",
- "app": StaticFiles(directory="lnbits/extensions/splitpayments/static"),
+ "app": StaticFiles(packages=[("lnbits", "extensions/splitpayments/static")]),
"name": "splitpayments_static",
}
]
diff --git a/lnbits/extensions/splitpayments/templates/splitpayments/_api_docs.html b/lnbits/extensions/splitpayments/templates/splitpayments/_api_docs.html
index 4cf7190c3..4b5ed979c 100644
--- a/lnbits/extensions/splitpayments/templates/splitpayments/_api_docs.html
+++ b/lnbits/extensions/splitpayments/templates/splitpayments/_api_docs.html
@@ -28,6 +28,12 @@
label="API info"
:content-inset-level="0.5"
>
+
+
diff --git a/lnbits/extensions/subdomains/templates/subdomains/_api_docs.html b/lnbits/extensions/subdomains/templates/subdomains/_api_docs.html
index b839c641d..db3b24771 100644
--- a/lnbits/extensions/subdomains/templates/subdomains/_api_docs.html
+++ b/lnbits/extensions/subdomains/templates/subdomains/_api_docs.html
@@ -22,5 +22,6 @@
>
+
diff --git a/lnbits/extensions/tipjar/templates/tipjar/_api_docs.html b/lnbits/extensions/tipjar/templates/tipjar/_api_docs.html
index 95ba6e064..cfb8136b4 100644
--- a/lnbits/extensions/tipjar/templates/tipjar/_api_docs.html
+++ b/lnbits/extensions/tipjar/templates/tipjar/_api_docs.html
@@ -12,4 +12,5 @@
>
+
diff --git a/lnbits/extensions/tpos/templates/tpos/_api_docs.html b/lnbits/extensions/tpos/templates/tpos/_api_docs.html
index 8930d9903..cbb21be13 100644
--- a/lnbits/extensions/tpos/templates/tpos/_api_docs.html
+++ b/lnbits/extensions/tpos/templates/tpos/_api_docs.html
@@ -4,6 +4,7 @@
label="API info"
:content-inset-level="0.5"
>
+
diff --git a/lnbits/extensions/tpos/templates/tpos/tpos.html b/lnbits/extensions/tpos/templates/tpos/tpos.html
index e11f60797..ebc6595e2 100644
--- a/lnbits/extensions/tpos/templates/tpos/tpos.html
+++ b/lnbits/extensions/tpos/templates/tpos/tpos.html
@@ -308,7 +308,9 @@
},
sat: function () {
if (!this.exchangeRate) return 0
- return Math.ceil((this.amount / this.exchangeRate) * 100000000)
+ return Math.ceil(
+ ((this.amount - this.tipAmount) / this.exchangeRate) * 100000000
+ )
},
tipAmountSat: function () {
if (!this.exchangeRate) return 0
@@ -423,10 +425,9 @@
'{{ tpos.tip_options | tojson }}' == 'null'
? null
: JSON.parse('{{ tpos.tip_options }}')
- console.log(typeof this.tip_options, this.tip_options)
setInterval(function () {
getRates()
- }, 20000)
+ }, 120000)
}
})
diff --git a/lnbits/extensions/usermanager/templates/usermanager/_api_docs.html b/lnbits/extensions/usermanager/templates/usermanager/_api_docs.html
index 34b3c39b0..886589e66 100644
--- a/lnbits/extensions/usermanager/templates/usermanager/_api_docs.html
+++ b/lnbits/extensions/usermanager/templates/usermanager/_api_docs.html
@@ -28,6 +28,7 @@
label="API info"
:content-inset-level="0.5"
>
+
diff --git a/lnbits/extensions/watchonly/templates/watchonly/_api_docs.html b/lnbits/extensions/watchonly/templates/watchonly/_api_docs.html
index b421186ad..94b44a443 100644
--- a/lnbits/extensions/watchonly/templates/watchonly/_api_docs.html
+++ b/lnbits/extensions/watchonly/templates/watchonly/_api_docs.html
@@ -20,6 +20,7 @@
label="API info"
:content-inset-level="0.5"
>
+
diff --git a/lnbits/extensions/withdraw/__init__.py b/lnbits/extensions/withdraw/__init__.py
index 58ccfe7e8..a0f4b6068 100644
--- a/lnbits/extensions/withdraw/__init__.py
+++ b/lnbits/extensions/withdraw/__init__.py
@@ -9,7 +9,7 @@ db = Database("ext_withdraw")
withdraw_static_files = [
{
"path": "/withdraw/static",
- "app": StaticFiles(directory="lnbits/extensions/withdraw/static"),
+ "app": StaticFiles(packages=[("lnbits", "extensions/withdraw/static")]),
"name": "withdraw_static",
}
]
diff --git a/lnbits/extensions/withdraw/templates/withdraw/_api_docs.html b/lnbits/extensions/withdraw/templates/withdraw/_api_docs.html
index 095aad3ad..ff88189d5 100644
--- a/lnbits/extensions/withdraw/templates/withdraw/_api_docs.html
+++ b/lnbits/extensions/withdraw/templates/withdraw/_api_docs.html
@@ -4,6 +4,7 @@
label="API info"
:content-inset-level="0.5"
>
+
List[Extension]:
- output = []
+ output: List[Extension] = []
if "all" in self._disabled:
return output
diff --git a/lnbits/server.py b/lnbits/server.py
new file mode 100644
index 000000000..4a63b3b7b
--- /dev/null
+++ b/lnbits/server.py
@@ -0,0 +1,18 @@
+import click
+import uvicorn
+
+
+@click.command()
+@click.option("--port", default="5000", help="Port to run LNBits on")
+@click.option("--host", default="127.0.0.1", help="Host to run LNBits on")
+def main(port, host):
+ """Launched with `poetry run lnbits` at root level"""
+ uvicorn.run("lnbits.__main__:app", port=port, host=host)
+
+
+if __name__ == "__main__":
+ main()
+
+# def main():
+# """Launched with `poetry run start` at root level"""
+# uvicorn.run("lnbits.__main__:app")
diff --git a/lnbits/tasks.py b/lnbits/tasks.py
index 86863f98f..f4d0a928d 100644
--- a/lnbits/tasks.py
+++ b/lnbits/tasks.py
@@ -66,7 +66,7 @@ async def webhook_handler():
raise HTTPException(status_code=HTTPStatus.NO_CONTENT)
-internal_invoice_queue = asyncio.Queue(0)
+internal_invoice_queue: asyncio.Queue = asyncio.Queue(0)
async def internal_invoice_listener():
diff --git a/lnbits/utils/exchange_rates.py b/lnbits/utils/exchange_rates.py
index 0432b364a..2801146b5 100644
--- a/lnbits/utils/exchange_rates.py
+++ b/lnbits/utils/exchange_rates.py
@@ -1,5 +1,5 @@
import asyncio
-from typing import Callable, NamedTuple
+from typing import Callable, List, NamedTuple
import httpx
from loguru import logger
@@ -227,10 +227,10 @@ async def btc_price(currency: str) -> float:
"TO": currency.upper(),
"to": currency.lower(),
}
- rates = []
- tasks = []
+ rates: List[float] = []
+ tasks: List[asyncio.Task] = []
- send_channel = asyncio.Queue()
+ send_channel: asyncio.Queue = asyncio.Queue()
async def controller():
failures = 0
diff --git a/lnbits/wallets/eclair.py b/lnbits/wallets/eclair.py
index c878d0d19..ab99c699d 100644
--- a/lnbits/wallets/eclair.py
+++ b/lnbits/wallets/eclair.py
@@ -7,7 +7,10 @@ from typing import AsyncGenerator, Dict, Optional
import httpx
from loguru import logger
-from websockets import connect
+
+# TODO: https://github.com/lnbits/lnbits-legend/issues/764
+# mypy https://github.com/aaugustin/websockets/issues/940
+from websockets import connect # type: ignore
from websockets.exceptions import (
ConnectionClosed,
ConnectionClosedError,
diff --git a/lnbits/wallets/fake.py b/lnbits/wallets/fake.py
index 3859d33c5..3126ee46a 100644
--- a/lnbits/wallets/fake.py
+++ b/lnbits/wallets/fake.py
@@ -28,7 +28,7 @@ class FakeWallet(Wallet):
logger.info(
"FakeWallet funding source is for using LNbits as a centralised, stand-alone payment system with brrrrrr."
)
- return StatusResponse(None, float("inf"))
+ return StatusResponse(None, 1000000000)
async def create_invoice(
self,
@@ -82,7 +82,7 @@ class FakeWallet(Wallet):
invoice = decode(bolt11)
if (
hasattr(invoice, "checking_id")
- and invoice.checking_id[6:] == data["privkey"][:6]
+ and invoice.checking_id[6:] == data["privkey"][:6] # type: ignore
):
return PaymentResponse(True, invoice.payment_hash, 0)
else:
@@ -97,7 +97,7 @@ class FakeWallet(Wallet):
return PaymentStatus(None)
async def paid_invoices_stream(self) -> AsyncGenerator[str, None]:
- self.queue = asyncio.Queue(0)
+ self.queue: asyncio.Queue = asyncio.Queue(0)
while True:
value = await self.queue.get()
yield value
diff --git a/lnbits/wallets/lnpay.py b/lnbits/wallets/lnpay.py
index 7171ec3fb..18b4f8bbe 100644
--- a/lnbits/wallets/lnpay.py
+++ b/lnbits/wallets/lnpay.py
@@ -119,7 +119,7 @@ class LNPayWallet(Wallet):
return PaymentStatus(statuses[r.json()["settled"]])
async def paid_invoices_stream(self) -> AsyncGenerator[str, None]:
- self.queue = asyncio.Queue(0)
+ self.queue: asyncio.Queue = asyncio.Queue(0)
while True:
value = await self.queue.get()
yield value
diff --git a/lnbits/wallets/macaroon/macaroon.py b/lnbits/wallets/macaroon/macaroon.py
index 2183dacb8..091551238 100644
--- a/lnbits/wallets/macaroon/macaroon.py
+++ b/lnbits/wallets/macaroon/macaroon.py
@@ -73,10 +73,10 @@ class AESCipher(object):
final_key += key
return final_key[:output]
- def decrypt(self, encrypted: str) -> str:
+ def decrypt(self, encrypted: str) -> str: # type: ignore
"""Decrypts a string using AES-256-CBC."""
passphrase = self.passphrase
- encrypted = base64.b64decode(encrypted)
+ encrypted = base64.b64decode(encrypted) # type: ignore
assert encrypted[0:8] == b"Salted__"
salt = encrypted[8:16]
key_iv = self.bytes_to_key(passphrase.encode(), salt, 32 + 16)
@@ -84,7 +84,7 @@ class AESCipher(object):
iv = key_iv[32:]
aes = AES.new(key, AES.MODE_CBC, iv)
try:
- return self.unpad(aes.decrypt(encrypted[16:])).decode()
+ return self.unpad(aes.decrypt(encrypted[16:])).decode() # type: ignore
except UnicodeDecodeError:
raise ValueError("Wrong passphrase")
diff --git a/lnbits/wallets/opennode.py b/lnbits/wallets/opennode.py
index d503b4dd9..be82365dd 100644
--- a/lnbits/wallets/opennode.py
+++ b/lnbits/wallets/opennode.py
@@ -127,7 +127,7 @@ class OpenNodeWallet(Wallet):
return PaymentStatus(statuses[r.json()["data"]["status"]])
async def paid_invoices_stream(self) -> AsyncGenerator[str, None]:
- self.queue = asyncio.Queue(0)
+ self.queue: asyncio.Queue = asyncio.Queue(0)
while True:
value = await self.queue.get()
yield value
diff --git a/mypy.ini b/mypy.ini
index 735f89e05..e5a974b51 100644
--- a/mypy.ini
+++ b/mypy.ini
@@ -1,7 +1,8 @@
[mypy]
ignore_missing_imports = True
-exclude = lnbits/wallets/lnd_grpc_files/
-exclude = lnbits/extensions/
-
+exclude = (?x)(
+ ^lnbits/extensions.
+ | ^lnbits/wallets/lnd_grpc_files.
+ )
[mypy-lnbits.wallets.lnd_grpc_files.*]
follow_imports = skip
diff --git a/nix/modules/lnbits-service.nix b/nix/modules/lnbits-service.nix
new file mode 100644
index 000000000..5d8e06403
--- /dev/null
+++ b/nix/modules/lnbits-service.nix
@@ -0,0 +1,106 @@
+{ config, pkgs, lib, ... }:
+
+let
+ defaultUser = "lnbits";
+ cfg = config.services.lnbits;
+ inherit (lib) mkOption mkIf types optionalAttrs;
+in
+
+{
+ options = {
+ services.lnbits = {
+ enable = mkOption {
+ default = false;
+ type = types.bool;
+ description = ''
+ Whether to enable the lnbits service
+ '';
+ };
+ openFirewall = mkOption {
+ type = types.bool;
+ default = false;
+ description = ''
+ Whether to open the ports used by lnbits in the firewall for the server
+ '';
+ };
+ package = mkOption {
+ type = types.package;
+ default = pkgs.lnbits;
+ description = ''
+ The lnbits package to use.
+ '';
+ };
+ stateDir = mkOption {
+ type = types.path;
+ default = "/var/lib/lnbits";
+ description = ''
+ The lnbits state directory which LNBITS_DATA_FOLDER will be set to
+ '';
+ };
+ host = mkOption {
+ type = types.str;
+ default = "127.0.0.1";
+ description = ''
+ The host to bind to
+ '';
+ };
+ port = mkOption {
+ type = types.port;
+ default = 8231;
+ description = ''
+ The port to run on
+ '';
+ };
+ user = mkOption {
+ type = types.str;
+ default = "lnbits";
+ description = "user to run lnbits as";
+ };
+ group = mkOption {
+ type = types.str;
+ default = "lnbits";
+ description = "group to run lnbits as";
+ };
+ };
+ };
+
+ config = mkIf cfg.enable {
+ users.users = optionalAttrs (cfg.user == defaultUser) {
+ ${defaultUser} = {
+ isSystemUser = true;
+ group = defaultUser;
+ };
+ };
+
+ users.groups = optionalAttrs (cfg.group == defaultUser) {
+ ${defaultUser} = { };
+ };
+
+ systemd.tmpfiles.rules = [
+ "d ${cfg.stateDir} 0700 ${cfg.user} ${cfg.group} - -"
+ ];
+
+ systemd.services.lnbits = {
+ enable = true;
+ description = "lnbits";
+ wantedBy = [ "multi-user.target" ];
+ after = [ "network-online.target" ];
+ environment = {
+ LNBITS_DATA_FOLDER = "${cfg.stateDir}";
+ };
+ serviceConfig = {
+ User = cfg.user;
+ Group = cfg.group;
+ WorkingDirectory = "${cfg.package.src}";
+ StateDirectory = "${cfg.stateDir}";
+ ExecStart = "${lib.getExe cfg.package} --port ${toString cfg.port} --host ${cfg.host}";
+ Restart = "always";
+ PrivateTmp = true;
+ };
+ };
+ networking.firewall = mkIf cfg.openFirewall {
+ allowedTCPPorts = [ cfg.port ];
+ };
+ };
+}
+
diff --git a/nix/tests/default.nix b/nix/tests/default.nix
new file mode 100644
index 000000000..7b8513acf
--- /dev/null
+++ b/nix/tests/default.nix
@@ -0,0 +1,4 @@
+{ pkgs, makeTest, inputs }:
+{
+ vmTest = import ./nixos-module { inherit pkgs makeTest inputs; };
+}
diff --git a/nix/tests/nixos-module/default.nix b/nix/tests/nixos-module/default.nix
new file mode 100644
index 000000000..868579121
--- /dev/null
+++ b/nix/tests/nixos-module/default.nix
@@ -0,0 +1,25 @@
+{ pkgs, makeTest, inputs }:
+makeTest {
+ nodes = {
+ client = { config, pkgs, ... }: {
+ environment.systemPackages = [ pkgs.curl ];
+ };
+ lnbits = { ... }: {
+ imports = [ inputs.self.nixosModules.default ];
+ services.lnbits = {
+ enable = true;
+ openFirewall = true;
+ host = "0.0.0.0";
+ };
+ };
+ };
+ testScript = { nodes, ... }: ''
+ start_all()
+ lnbits.wait_for_open_port(${toString nodes.lnbits.config.services.lnbits.port})
+ client.wait_for_unit("multi-user.target")
+ with subtest("Check that the lnbits webserver can be reached."):
+ assert "LNbits" in client.succeed(
+ "curl -sSf http:/lnbits:8231/ | grep title"
+ )
+ '';
+}
diff --git a/poetry.lock b/poetry.lock
new file mode 100644
index 000000000..48c508ceb
--- /dev/null
+++ b/poetry.lock
@@ -0,0 +1,1165 @@
+[[package]]
+name = "aiofiles"
+version = "0.7.0"
+description = "File support for asyncio."
+category = "main"
+optional = false
+python-versions = ">=3.6,<4.0"
+
+[[package]]
+name = "anyio"
+version = "3.6.1"
+description = "High level compatibility layer for multiple asynchronous event loop implementations"
+category = "main"
+optional = false
+python-versions = ">=3.6.2"
+
+[package.dependencies]
+idna = ">=2.8"
+sniffio = ">=1.1"
+
+[package.extras]
+doc = ["packaging", "sphinx-rtd-theme", "sphinx-autodoc-typehints (>=1.2.0)"]
+test = ["coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "contextlib2", "uvloop (<0.15)", "mock (>=4)", "uvloop (>=0.15)"]
+trio = ["trio (>=0.16)"]
+
+[[package]]
+name = "asgiref"
+version = "3.4.1"
+description = "ASGI specs, helper code, and adapters"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.extras]
+tests = ["pytest", "pytest-asyncio", "mypy (>=0.800)"]
+
+[[package]]
+name = "attrs"
+version = "21.2.0"
+description = "Classes Without Boilerplate"
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+
+[package.extras]
+dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit"]
+docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"]
+tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface"]
+tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins"]
+
+[[package]]
+name = "bech32"
+version = "1.2.0"
+description = "Reference implementation for Bech32 and segwit addresses."
+category = "main"
+optional = false
+python-versions = ">=3.5"
+
+[[package]]
+name = "bitstring"
+version = "3.1.9"
+description = "Simple construction, analysis and modification of binary data."
+category = "main"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "cerberus"
+version = "1.3.4"
+description = "Lightweight, extensible schema and data validation tool for Python dictionaries."
+category = "main"
+optional = false
+python-versions = ">=2.7"
+
+[[package]]
+name = "certifi"
+version = "2021.5.30"
+description = "Python package for providing Mozilla's CA Bundle."
+category = "main"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "cffi"
+version = "1.15.0"
+description = "Foreign Function Interface for Python calling C code."
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+pycparser = "*"
+
+[[package]]
+name = "charset-normalizer"
+version = "2.0.6"
+description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
+category = "main"
+optional = false
+python-versions = ">=3.5.0"
+
+[package.extras]
+unicode_backport = ["unicodedata2"]
+
+[[package]]
+name = "click"
+version = "8.0.1"
+description = "Composable command line interface toolkit"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+colorama = {version = "*", markers = "platform_system == \"Windows\""}
+
+[[package]]
+name = "colorama"
+version = "0.4.5"
+description = "Cross-platform colored terminal text."
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+
+[[package]]
+name = "ecdsa"
+version = "0.17.0"
+description = "ECDSA cryptographic signature library (pure python)"
+category = "main"
+optional = false
+python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
+
+[package.dependencies]
+six = ">=1.9.0"
+
+[package.extras]
+gmpy = ["gmpy"]
+gmpy2 = ["gmpy2"]
+
+[[package]]
+name = "embit"
+version = "0.4.9"
+description = "yet another bitcoin library"
+category = "main"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "environs"
+version = "9.3.3"
+description = "simplified environment variable parsing"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+marshmallow = ">=3.0.0"
+python-dotenv = "*"
+
+[package.extras]
+dev = ["pytest", "dj-database-url", "dj-email-url", "django-cache-url", "flake8 (==3.9.2)", "flake8-bugbear (==21.4.3)", "mypy (==0.910)", "pre-commit (>=2.4,<3.0)", "tox"]
+django = ["dj-database-url", "dj-email-url", "django-cache-url"]
+lint = ["flake8 (==3.9.2)", "flake8-bugbear (==21.4.3)", "mypy (==0.910)", "pre-commit (>=2.4,<3.0)"]
+tests = ["pytest", "dj-database-url", "dj-email-url", "django-cache-url"]
+
+[[package]]
+name = "fastapi"
+version = "0.78.0"
+description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production"
+category = "main"
+optional = false
+python-versions = ">=3.6.1"
+
+[package.dependencies]
+pydantic = ">=1.6.2,<1.7 || >1.7,<1.7.1 || >1.7.1,<1.7.2 || >1.7.2,<1.7.3 || >1.7.3,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0"
+starlette = "0.19.1"
+
+[package.extras]
+all = ["requests (>=2.24.0,<3.0.0)", "jinja2 (>=2.11.2,<4.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "itsdangerous (>=1.1.0,<3.0.0)", "pyyaml (>=5.3.1,<7.0.0)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0,<6.0.0)", "orjson (>=3.2.1,<4.0.0)", "email_validator (>=1.1.1,<2.0.0)", "uvicorn[standard] (>=0.12.0,<0.18.0)"]
+dev = ["python-jose[cryptography] (>=3.3.0,<4.0.0)", "passlib[bcrypt] (>=1.7.2,<2.0.0)", "autoflake (>=1.4.0,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)", "uvicorn[standard] (>=0.12.0,<0.18.0)", "pre-commit (>=2.17.0,<3.0.0)"]
+doc = ["mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=8.1.4,<9.0.0)", "mdx-include (>=1.4.1,<2.0.0)", "mkdocs-markdownextradata-plugin (>=0.1.7,<0.3.0)", "typer (>=0.4.1,<0.5.0)", "pyyaml (>=5.3.1,<7.0.0)"]
+test = ["pytest (>=6.2.4,<7.0.0)", "pytest-cov (>=2.12.0,<4.0.0)", "mypy (==0.910)", "flake8 (>=3.8.3,<4.0.0)", "black (==22.3.0)", "isort (>=5.0.6,<6.0.0)", "requests (>=2.24.0,<3.0.0)", "httpx (>=0.14.0,<0.19.0)", "email_validator (>=1.1.1,<2.0.0)", "sqlalchemy (>=1.3.18,<1.5.0)", "peewee (>=3.13.3,<4.0.0)", "databases[sqlite] (>=0.3.2,<0.6.0)", "orjson (>=3.2.1,<4.0.0)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0,<6.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "flask (>=1.1.2,<3.0.0)", "anyio[trio] (>=3.2.1,<4.0.0)", "types-ujson (==4.2.1)", "types-orjson (==3.6.2)", "types-dataclasses (==0.6.5)"]
+
+[[package]]
+name = "h11"
+version = "0.12.0"
+description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[[package]]
+name = "httpcore"
+version = "0.13.7"
+description = "A minimal low-level HTTP client."
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+anyio = ">=3.0.0,<4.0.0"
+h11 = ">=0.11,<0.13"
+sniffio = ">=1.0.0,<2.0.0"
+
+[package.extras]
+http2 = ["h2 (>=3,<5)"]
+
+[[package]]
+name = "httptools"
+version = "0.2.0"
+description = "A collection of framework independent HTTP protocol utils."
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.extras]
+test = ["Cython (==0.29.22)"]
+
+[[package]]
+name = "httpx"
+version = "0.19.0"
+description = "The next generation HTTP client."
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+certifi = "*"
+charset-normalizer = "*"
+httpcore = ">=0.13.3,<0.14.0"
+rfc3986 = {version = ">=1.3,<2", extras = ["idna2008"]}
+sniffio = "*"
+
+[package.extras]
+brotli = ["brotlicffi", "brotli"]
+http2 = ["h2 (>=3,<5)"]
+
+[[package]]
+name = "idna"
+version = "3.2"
+description = "Internationalized Domain Names in Applications (IDNA)"
+category = "main"
+optional = false
+python-versions = ">=3.5"
+
+[[package]]
+name = "importlib-metadata"
+version = "4.8.1"
+description = "Read metadata from Python packages"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+zipp = ">=0.5"
+
+[package.extras]
+docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"]
+perf = ["ipython"]
+testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-perf (>=0.9.2)", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"]
+
+[[package]]
+name = "jinja2"
+version = "3.0.1"
+description = "A very fast and expressive template engine."
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+MarkupSafe = ">=2.0"
+
+[package.extras]
+i18n = ["Babel (>=2.7)"]
+
+[[package]]
+name = "lnurl"
+version = "0.3.6"
+description = "LNURL implementation for Python."
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+bech32 = "*"
+pydantic = "*"
+
+[[package]]
+name = "loguru"
+version = "0.5.3"
+description = "Python logging made (stupidly) simple"
+category = "main"
+optional = false
+python-versions = ">=3.5"
+
+[package.dependencies]
+colorama = {version = ">=0.3.4", markers = "sys_platform == \"win32\""}
+win32-setctime = {version = ">=1.0.0", markers = "sys_platform == \"win32\""}
+
+[package.extras]
+dev = ["codecov (>=2.0.15)", "colorama (>=0.3.4)", "flake8 (>=3.7.7)", "tox (>=3.9.0)", "tox-travis (>=0.12)", "pytest (>=4.6.2)", "pytest-cov (>=2.7.1)", "Sphinx (>=2.2.1)", "sphinx-autobuild (>=0.7.1)", "sphinx-rtd-theme (>=0.4.3)", "black (>=19.10b0)", "isort (>=5.1.1)"]
+
+[[package]]
+name = "markupsafe"
+version = "2.0.1"
+description = "Safely add untrusted strings to HTML/XML markup."
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[[package]]
+name = "marshmallow"
+version = "3.13.0"
+description = "A lightweight library for converting complex datatypes to and from native Python datatypes."
+category = "main"
+optional = false
+python-versions = ">=3.5"
+
+[package.extras]
+dev = ["pytest", "pytz", "simplejson", "mypy (==0.910)", "flake8 (==3.9.2)", "flake8-bugbear (==21.4.3)", "pre-commit (>=2.4,<3.0)", "tox"]
+docs = ["sphinx (==4.1.1)", "sphinx-issues (==1.2.0)", "alabaster (==0.7.12)", "sphinx-version-warning (==1.1.2)", "autodocsumm (==0.2.6)"]
+lint = ["mypy (==0.910)", "flake8 (==3.9.2)", "flake8-bugbear (==21.4.3)", "pre-commit (>=2.4,<3.0)"]
+tests = ["pytest", "pytz", "simplejson"]
+
+[[package]]
+name = "outcome"
+version = "1.1.0"
+description = "Capture the outcome of Python function calls."
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+attrs = ">=19.2.0"
+
+[[package]]
+name = "psycopg2-binary"
+version = "2.9.1"
+description = "psycopg2 - Python-PostgreSQL Database Adapter"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[[package]]
+name = "pycparser"
+version = "2.21"
+description = "C parser in Python"
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+
+[[package]]
+name = "pycryptodomex"
+version = "3.14.1"
+description = "Cryptographic library for Python"
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+
+[[package]]
+name = "pydantic"
+version = "1.8.2"
+description = "Data validation and settings management using python 3.6 type hinting"
+category = "main"
+optional = false
+python-versions = ">=3.6.1"
+
+[package.dependencies]
+typing-extensions = ">=3.7.4.3"
+
+[package.extras]
+dotenv = ["python-dotenv (>=0.10.4)"]
+email = ["email-validator (>=1.0.3)"]
+
+[[package]]
+name = "pypng"
+version = "0.0.21"
+description = "Pure Python library for saving and loading PNG images"
+category = "main"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "pyqrcode"
+version = "1.2.1"
+description = "A QR code generator written purely in Python with SVG, EPS, PNG and terminal output."
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.extras]
+PNG = ["pypng (>=0.0.13)"]
+
+[[package]]
+name = "pyscss"
+version = "1.3.7"
+description = "pyScss, a Scss compiler for Python"
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+six = "*"
+
+[[package]]
+name = "python-dotenv"
+version = "0.19.0"
+description = "Read key-value pairs from a .env file and set them as environment variables"
+category = "main"
+optional = false
+python-versions = ">=3.5"
+
+[package.extras]
+cli = ["click (>=5.0)"]
+
+[[package]]
+name = "pyyaml"
+version = "5.4.1"
+description = "YAML parser and emitter for Python"
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
+
+[[package]]
+name = "represent"
+version = "1.6.0.post0"
+description = "Create __repr__ automatically or declaratively."
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+
+[package.dependencies]
+six = ">=1.8.0"
+
+[package.extras]
+test = ["ipython", "pytest (>=3.0.5)", "mock"]
+
+[[package]]
+name = "rfc3986"
+version = "1.5.0"
+description = "Validating URI References per RFC 3986"
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+idna = {version = "*", optional = true, markers = "extra == \"idna2008\""}
+
+[package.extras]
+idna2008 = ["idna"]
+
+[[package]]
+name = "secp256k1"
+version = "0.14.0"
+description = "FFI bindings to libsecp256k1"
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+cffi = ">=1.3.0"
+
+[[package]]
+name = "shortuuid"
+version = "1.0.1"
+description = "A generator library for concise, unambiguous and URL-safe UUIDs."
+category = "main"
+optional = false
+python-versions = ">=3.5"
+
+[[package]]
+name = "six"
+version = "1.16.0"
+description = "Python 2 and 3 compatibility utilities"
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
+
+[[package]]
+name = "sniffio"
+version = "1.2.0"
+description = "Sniff out which async library your code is running under"
+category = "main"
+optional = false
+python-versions = ">=3.5"
+
+[[package]]
+name = "sqlalchemy"
+version = "1.3.23"
+description = "Database Abstraction Library"
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+
+[package.extras]
+mssql = ["pyodbc"]
+mssql_pymssql = ["pymssql"]
+mssql_pyodbc = ["pyodbc"]
+mysql = ["mysqlclient"]
+oracle = ["cx-oracle"]
+postgresql = ["psycopg2"]
+postgresql_pg8000 = ["pg8000 (<1.16.6)"]
+postgresql_psycopg2binary = ["psycopg2-binary"]
+postgresql_psycopg2cffi = ["psycopg2cffi"]
+pymysql = ["pymysql (<1)", "pymysql"]
+
+[[package]]
+name = "sqlalchemy-aio"
+version = "0.16.0"
+description = "Async support for SQLAlchemy."
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+outcome = "*"
+represent = ">=1.4"
+sqlalchemy = "*"
+
+[package.extras]
+test = ["pytest (>=5.4)", "pytest-asyncio (>=0.14)", "pytest-trio (>=0.6)"]
+test-noextras = ["pytest (>=5.4)", "pytest-asyncio (>=0.14)"]
+trio = ["trio (>=0.15)"]
+
+[[package]]
+name = "sse-starlette"
+version = "0.6.2"
+description = "SSE plugin for Starlette"
+category = "main"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "starlette"
+version = "0.19.1"
+description = "The little ASGI library that shines."
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+anyio = ">=3.4.0,<5"
+typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""}
+
+[package.extras]
+full = ["itsdangerous", "jinja2", "python-multipart", "pyyaml", "requests"]
+
+[[package]]
+name = "typing-extensions"
+version = "3.10.0.2"
+description = "Backported and Experimental Type Hints for Python 3.5+"
+category = "main"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "uvicorn"
+version = "0.18.1"
+description = "The lightning-fast ASGI server."
+category = "main"
+optional = false
+python-versions = ">=3.7"
+
+[package.dependencies]
+click = ">=7.0"
+h11 = ">=0.8"
+
+[package.extras]
+standard = ["websockets (>=10.0)", "httptools (>=0.4.0)", "watchfiles (>=0.13)", "python-dotenv (>=0.13)", "PyYAML (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "colorama (>=0.4)"]
+
+[[package]]
+name = "uvloop"
+version = "0.16.0"
+description = "Fast implementation of asyncio event loop on top of libuv"
+category = "main"
+optional = false
+python-versions = ">=3.7"
+
+[package.extras]
+dev = ["Cython (>=0.29.24,<0.30.0)", "pytest (>=3.6.0)", "Sphinx (>=4.1.2,<4.2.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)", "aiohttp", "flake8 (>=3.9.2,<3.10.0)", "psutil", "pycodestyle (>=2.7.0,<2.8.0)", "pyOpenSSL (>=19.0.0,<19.1.0)", "mypy (>=0.800)"]
+docs = ["Sphinx (>=4.1.2,<4.2.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)"]
+test = ["aiohttp", "flake8 (>=3.9.2,<3.10.0)", "psutil", "pycodestyle (>=2.7.0,<2.8.0)", "pyOpenSSL (>=19.0.0,<19.1.0)", "mypy (>=0.800)"]
+
+[[package]]
+name = "watchgod"
+version = "0.7"
+description = "Simple, modern file watching and code reload in python."
+category = "main"
+optional = false
+python-versions = ">=3.5"
+
+[[package]]
+name = "websockets"
+version = "10.0"
+description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)"
+category = "main"
+optional = false
+python-versions = ">=3.7"
+
+[[package]]
+name = "win32-setctime"
+version = "1.1.0"
+description = "A small Python utility to set file creation time on Windows"
+category = "main"
+optional = false
+python-versions = ">=3.5"
+
+[package.extras]
+dev = ["pytest (>=4.6.2)", "black (>=19.3b0)"]
+
+[[package]]
+name = "zipp"
+version = "3.5.0"
+description = "Backport of pathlib-compatible object wrapper for zip files"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.extras]
+docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"]
+testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"]
+
+[metadata]
+lock-version = "1.1"
+python-versions = "^3.9"
+content-hash = "921a5f4fe1a4d1a4c3b490f8631ed4bdd0d8af1f1992f1a4f74eaed986c4eb0b"
+
+[metadata.files]
+aiofiles = [
+ {file = "aiofiles-0.7.0-py3-none-any.whl", hash = "sha256:c67a6823b5f23fcab0a2595a289cec7d8c863ffcb4322fb8cd6b90400aedfdbc"},
+ {file = "aiofiles-0.7.0.tar.gz", hash = "sha256:a1c4fc9b2ff81568c83e21392a82f344ea9d23da906e4f6a52662764545e19d4"},
+]
+anyio = [
+ {file = "anyio-3.6.1-py3-none-any.whl", hash = "sha256:cb29b9c70620506a9a8f87a309591713446953302d7d995344d0d7c6c0c9a7be"},
+ {file = "anyio-3.6.1.tar.gz", hash = "sha256:413adf95f93886e442aea925f3ee43baa5a765a64a0f52c6081894f9992fdd0b"},
+]
+asgiref = [
+ {file = "asgiref-3.4.1-py3-none-any.whl", hash = "sha256:ffc141aa908e6f175673e7b1b3b7af4fdb0ecb738fc5c8b88f69f055c2415214"},
+ {file = "asgiref-3.4.1.tar.gz", hash = "sha256:4ef1ab46b484e3c706329cedeff284a5d40824200638503f5768edb6de7d58e9"},
+]
+attrs = [
+ {file = "attrs-21.2.0-py2.py3-none-any.whl", hash = "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1"},
+ {file = "attrs-21.2.0.tar.gz", hash = "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"},
+]
+bech32 = [
+ {file = "bech32-1.2.0-py3-none-any.whl", hash = "sha256:990dc8e5a5e4feabbdf55207b5315fdd9b73db40be294a19b3752cde9e79d981"},
+ {file = "bech32-1.2.0.tar.gz", hash = "sha256:7d6db8214603bd7871fcfa6c0826ef68b85b0abd90fa21c285a9c5e21d2bd899"},
+]
+bitstring = [
+ {file = "bitstring-3.1.9-py2-none-any.whl", hash = "sha256:e3e340e58900a948787a05e8c08772f1ccbe133f6f41fe3f0fa19a18a22bbf4f"},
+ {file = "bitstring-3.1.9-py3-none-any.whl", hash = "sha256:0de167daa6a00c9386255a7cac931b45e6e24e0ad7ea64f1f92a64ac23ad4578"},
+ {file = "bitstring-3.1.9.tar.gz", hash = "sha256:a5848a3f63111785224dca8bb4c0a75b62ecdef56a042c8d6be74b16f7e860e7"},
+]
+cerberus = [
+ {file = "Cerberus-1.3.4.tar.gz", hash = "sha256:d1b21b3954b2498d9a79edf16b3170a3ac1021df88d197dc2ce5928ba519237c"},
+]
+certifi = [
+ {file = "certifi-2021.5.30-py2.py3-none-any.whl", hash = "sha256:50b1e4f8446b06f41be7dd6338db18e0990601dce795c2b1686458aa7e8fa7d8"},
+ {file = "certifi-2021.5.30.tar.gz", hash = "sha256:2bbf76fd432960138b3ef6dda3dde0544f27cbf8546c458e60baf371917ba9ee"},
+]
+cffi = [
+ {file = "cffi-1.15.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:c2502a1a03b6312837279c8c1bd3ebedf6c12c4228ddbad40912d671ccc8a962"},
+ {file = "cffi-1.15.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:23cfe892bd5dd8941608f93348c0737e369e51c100d03718f108bf1add7bd6d0"},
+ {file = "cffi-1.15.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:41d45de54cd277a7878919867c0f08b0cf817605e4eb94093e7516505d3c8d14"},
+ {file = "cffi-1.15.0-cp27-cp27m-win32.whl", hash = "sha256:4a306fa632e8f0928956a41fa8e1d6243c71e7eb59ffbd165fc0b41e316b2474"},
+ {file = "cffi-1.15.0-cp27-cp27m-win_amd64.whl", hash = "sha256:e7022a66d9b55e93e1a845d8c9eba2a1bebd4966cd8bfc25d9cd07d515b33fa6"},
+ {file = "cffi-1.15.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:14cd121ea63ecdae71efa69c15c5543a4b5fbcd0bbe2aad864baca0063cecf27"},
+ {file = "cffi-1.15.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:d4d692a89c5cf08a8557fdeb329b82e7bf609aadfaed6c0d79f5a449a3c7c023"},
+ {file = "cffi-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0104fb5ae2391d46a4cb082abdd5c69ea4eab79d8d44eaaf79f1b1fd806ee4c2"},
+ {file = "cffi-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:91ec59c33514b7c7559a6acda53bbfe1b283949c34fe7440bcf917f96ac0723e"},
+ {file = "cffi-1.15.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f5c7150ad32ba43a07c4479f40241756145a1f03b43480e058cfd862bf5041c7"},
+ {file = "cffi-1.15.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:00c878c90cb53ccfaae6b8bc18ad05d2036553e6d9d1d9dbcf323bbe83854ca3"},
+ {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abb9a20a72ac4e0fdb50dae135ba5e77880518e742077ced47eb1499e29a443c"},
+ {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a5263e363c27b653a90078143adb3d076c1a748ec9ecc78ea2fb916f9b861962"},
+ {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f54a64f8b0c8ff0b64d18aa76675262e1700f3995182267998c31ae974fbc382"},
+ {file = "cffi-1.15.0-cp310-cp310-win32.whl", hash = "sha256:c21c9e3896c23007803a875460fb786118f0cdd4434359577ea25eb556e34c55"},
+ {file = "cffi-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:5e069f72d497312b24fcc02073d70cb989045d1c91cbd53979366077959933e0"},
+ {file = "cffi-1.15.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:64d4ec9f448dfe041705426000cc13e34e6e5bb13736e9fd62e34a0b0c41566e"},
+ {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2756c88cbb94231c7a147402476be2c4df2f6078099a6f4a480d239a8817ae39"},
+ {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b96a311ac60a3f6be21d2572e46ce67f09abcf4d09344c49274eb9e0bf345fc"},
+ {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75e4024375654472cc27e91cbe9eaa08567f7fbdf822638be2814ce059f58032"},
+ {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:59888172256cac5629e60e72e86598027aca6bf01fa2465bdb676d37636573e8"},
+ {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:27c219baf94952ae9d50ec19651a687b826792055353d07648a5695413e0c605"},
+ {file = "cffi-1.15.0-cp36-cp36m-win32.whl", hash = "sha256:4958391dbd6249d7ad855b9ca88fae690783a6be9e86df65865058ed81fc860e"},
+ {file = "cffi-1.15.0-cp36-cp36m-win_amd64.whl", hash = "sha256:f6f824dc3bce0edab5f427efcfb1d63ee75b6fcb7282900ccaf925be84efb0fc"},
+ {file = "cffi-1.15.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:06c48159c1abed75c2e721b1715c379fa3200c7784271b3c46df01383b593636"},
+ {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:c2051981a968d7de9dd2d7b87bcb9c939c74a34626a6e2f8181455dd49ed69e4"},
+ {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:fd8a250edc26254fe5b33be00402e6d287f562b6a5b2152dec302fa15bb3e997"},
+ {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91d77d2a782be4274da750752bb1650a97bfd8f291022b379bb8e01c66b4e96b"},
+ {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:45db3a33139e9c8f7c09234b5784a5e33d31fd6907800b316decad50af323ff2"},
+ {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:263cc3d821c4ab2213cbe8cd8b355a7f72a8324577dc865ef98487c1aeee2bc7"},
+ {file = "cffi-1.15.0-cp37-cp37m-win32.whl", hash = "sha256:17771976e82e9f94976180f76468546834d22a7cc404b17c22df2a2c81db0c66"},
+ {file = "cffi-1.15.0-cp37-cp37m-win_amd64.whl", hash = "sha256:3415c89f9204ee60cd09b235810be700e993e343a408693e80ce7f6a40108029"},
+ {file = "cffi-1.15.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4238e6dab5d6a8ba812de994bbb0a79bddbdf80994e4ce802b6f6f3142fcc880"},
+ {file = "cffi-1.15.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0808014eb713677ec1292301ea4c81ad277b6cdf2fdd90fd540af98c0b101d20"},
+ {file = "cffi-1.15.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:57e9ac9ccc3101fac9d6014fba037473e4358ef4e89f8e181f8951a2c0162024"},
+ {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b6c2ea03845c9f501ed1313e78de148cd3f6cad741a75d43a29b43da27f2e1e"},
+ {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:10dffb601ccfb65262a27233ac273d552ddc4d8ae1bf93b21c94b8511bffe728"},
+ {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:786902fb9ba7433aae840e0ed609f45c7bcd4e225ebb9c753aa39725bb3e6ad6"},
+ {file = "cffi-1.15.0-cp38-cp38-win32.whl", hash = "sha256:da5db4e883f1ce37f55c667e5c0de439df76ac4cb55964655906306918e7363c"},
+ {file = "cffi-1.15.0-cp38-cp38-win_amd64.whl", hash = "sha256:181dee03b1170ff1969489acf1c26533710231c58f95534e3edac87fff06c443"},
+ {file = "cffi-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:45e8636704eacc432a206ac7345a5d3d2c62d95a507ec70d62f23cd91770482a"},
+ {file = "cffi-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:31fb708d9d7c3f49a60f04cf5b119aeefe5644daba1cd2a0fe389b674fd1de37"},
+ {file = "cffi-1.15.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6dc2737a3674b3e344847c8686cf29e500584ccad76204efea14f451d4cc669a"},
+ {file = "cffi-1.15.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:74fdfdbfdc48d3f47148976f49fab3251e550a8720bebc99bf1483f5bfb5db3e"},
+ {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffaa5c925128e29efbde7301d8ecaf35c8c60ffbcd6a1ffd3a552177c8e5e796"},
+ {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f7d084648d77af029acb79a0ff49a0ad7e9d09057a9bf46596dac9514dc07df"},
+ {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ef1f279350da2c586a69d32fc8733092fd32cc8ac95139a00377841f59a3f8d8"},
+ {file = "cffi-1.15.0-cp39-cp39-win32.whl", hash = "sha256:2a23af14f408d53d5e6cd4e3d9a24ff9e05906ad574822a10563efcef137979a"},
+ {file = "cffi-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:3773c4d81e6e818df2efbc7dd77325ca0dcb688116050fb2b3011218eda36139"},
+ {file = "cffi-1.15.0.tar.gz", hash = "sha256:920f0d66a896c2d99f0adbb391f990a84091179542c205fa53ce5787aff87954"},
+]
+charset-normalizer = [
+ {file = "charset-normalizer-2.0.6.tar.gz", hash = "sha256:5ec46d183433dcbd0ab716f2d7f29d8dee50505b3fdb40c6b985c7c4f5a3591f"},
+ {file = "charset_normalizer-2.0.6-py3-none-any.whl", hash = "sha256:5d209c0a931f215cee683b6445e2d77677e7e75e159f78def0db09d68fafcaa6"},
+]
+click = [
+ {file = "click-8.0.1-py3-none-any.whl", hash = "sha256:fba402a4a47334742d782209a7c79bc448911afe1149d07bdabdf480b3e2f4b6"},
+ {file = "click-8.0.1.tar.gz", hash = "sha256:8c04c11192119b1ef78ea049e0a6f0463e4c48ef00a30160c704337586f3ad7a"},
+]
+colorama = [
+ {file = "colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"},
+ {file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"},
+]
+ecdsa = [
+ {file = "ecdsa-0.17.0-py2.py3-none-any.whl", hash = "sha256:5cf31d5b33743abe0dfc28999036c849a69d548f994b535e527ee3cb7f3ef676"},
+ {file = "ecdsa-0.17.0.tar.gz", hash = "sha256:b9f500bb439e4153d0330610f5d26baaf18d17b8ced1bc54410d189385ea68aa"},
+]
+embit = [
+ {file = "embit-0.4.9.tar.gz", hash = "sha256:992332bd89af6e2d027e26fe437eb14aa33997db08c882c49064d49c3e6f4ab9"},
+]
+environs = [
+ {file = "environs-9.3.3-py2.py3-none-any.whl", hash = "sha256:ee5466156b50fe03aa9fec6e720feea577b5bf515d7f21b2c46608272557ba26"},
+ {file = "environs-9.3.3.tar.gz", hash = "sha256:72b867ff7b553076cdd90f3ee01ecc1cf854987639c9c459f0ed0d3d44ae490c"},
+]
+fastapi = [
+ {file = "fastapi-0.78.0-py3-none-any.whl", hash = "sha256:15fcabd5c78c266fa7ae7d8de9b384bfc2375ee0503463a6febbe3bab69d6f65"},
+ {file = "fastapi-0.78.0.tar.gz", hash = "sha256:3233d4a789ba018578658e2af1a4bb5e38bdd122ff722b313666a9b2c6786a83"},
+]
+h11 = [
+ {file = "h11-0.12.0-py3-none-any.whl", hash = "sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6"},
+ {file = "h11-0.12.0.tar.gz", hash = "sha256:47222cb6067e4a307d535814917cd98fd0a57b6788ce715755fa2b6c28b56042"},
+]
+httpcore = [
+ {file = "httpcore-0.13.7-py3-none-any.whl", hash = "sha256:369aa481b014cf046f7067fddd67d00560f2f00426e79569d99cb11245134af0"},
+ {file = "httpcore-0.13.7.tar.gz", hash = "sha256:036f960468759e633574d7c121afba48af6419615d36ab8ede979f1ad6276fa3"},
+]
+httptools = [
+ {file = "httptools-0.2.0-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:79dbc21f3612a78b28384e989b21872e2e3cf3968532601544696e4ed0007ce5"},
+ {file = "httptools-0.2.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:78d03dd39b09c99ec917d50189e6743adbfd18c15d5944392d2eabda688bf149"},
+ {file = "httptools-0.2.0-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:a23166e5ae2775709cf4f7ad4c2048755ebfb272767d244e1a96d55ac775cca7"},
+ {file = "httptools-0.2.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:3ab1f390d8867f74b3b5ee2a7ecc9b8d7f53750bd45714bf1cb72a953d7dfa77"},
+ {file = "httptools-0.2.0-cp36-cp36m-win_amd64.whl", hash = "sha256:a7594f9a010cdf1e16a58b3bf26c9da39bbf663e3b8d46d39176999d71816658"},
+ {file = "httptools-0.2.0-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:01b392a166adcc8bc2f526a939a8aabf89fe079243e1543fd0e7dc1b58d737cb"},
+ {file = "httptools-0.2.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:80ffa04fe8c8dfacf6e4cef8277347d35b0442c581f5814f3b0cf41b65c43c6e"},
+ {file = "httptools-0.2.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d5682eeb10cca0606c4a8286a3391d4c3c5a36f0c448e71b8bd05be4e1694bfb"},
+ {file = "httptools-0.2.0-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:a289c27ccae399a70eacf32df9a44059ca2ba4ac444604b00a19a6c1f0809943"},
+ {file = "httptools-0.2.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:813871f961edea6cb2fe312f2d9b27d12a51ba92545380126f80d0de1917ea15"},
+ {file = "httptools-0.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:cc9be041e428c10f8b6ab358c6b393648f9457094e1dcc11b4906026d43cd380"},
+ {file = "httptools-0.2.0-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:b08d00d889a118f68f37f3c43e359aab24ee29eb2e3fe96d64c6a2ba8b9d6557"},
+ {file = "httptools-0.2.0-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:fd3b8905e21431ad306eeaf56644a68fdd621bf8f3097eff54d0f6bdf7262065"},
+ {file = "httptools-0.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:200fc1cdf733a9ff554c0bb97a4047785cfaad9875307d6087001db3eb2b417f"},
+ {file = "httptools-0.2.0.tar.gz", hash = "sha256:94505026be56652d7a530ab03d89474dc6021019d6b8682281977163b3471ea0"},
+]
+httpx = [
+ {file = "httpx-0.19.0-py3-none-any.whl", hash = "sha256:9bd728a6c5ec0a9e243932a9983d57d3cc4a87bb4f554e1360fce407f78f9435"},
+ {file = "httpx-0.19.0.tar.gz", hash = "sha256:92ecd2c00c688b529eda11cedb15161eaf02dee9116712f621c70d9a40b2cdd0"},
+]
+idna = [
+ {file = "idna-3.2-py3-none-any.whl", hash = "sha256:14475042e284991034cb48e06f6851428fb14c4dc953acd9be9a5e95c7b6dd7a"},
+ {file = "idna-3.2.tar.gz", hash = "sha256:467fbad99067910785144ce333826c71fb0e63a425657295239737f7ecd125f3"},
+]
+importlib-metadata = [
+ {file = "importlib_metadata-4.8.1-py3-none-any.whl", hash = "sha256:b618b6d2d5ffa2f16add5697cf57a46c76a56229b0ed1c438322e4e95645bd15"},
+ {file = "importlib_metadata-4.8.1.tar.gz", hash = "sha256:f284b3e11256ad1e5d03ab86bb2ccd6f5339688ff17a4d797a0fe7df326f23b1"},
+]
+jinja2 = [
+ {file = "Jinja2-3.0.1-py3-none-any.whl", hash = "sha256:1f06f2da51e7b56b8f238affdd6b4e2c61e39598a378cc49345bc1bd42a978a4"},
+ {file = "Jinja2-3.0.1.tar.gz", hash = "sha256:703f484b47a6af502e743c9122595cc812b0271f661722403114f71a79d0f5a4"},
+]
+lnurl = [
+ {file = "lnurl-0.3.6-py3-none-any.whl", hash = "sha256:579982fd8c4d25bc84c61c74ec45cb7999fa1fa2426f5d5aeb0160ba333b9c92"},
+ {file = "lnurl-0.3.6.tar.gz", hash = "sha256:8af07460115a48f3122a5a9c9a6062bee3897d5f6ab4c9a60f6561a83a8234f6"},
+]
+loguru = [
+ {file = "loguru-0.5.3-py3-none-any.whl", hash = "sha256:f8087ac396b5ee5f67c963b495d615ebbceac2796379599820e324419d53667c"},
+ {file = "loguru-0.5.3.tar.gz", hash = "sha256:b28e72ac7a98be3d28ad28570299a393dfcd32e5e3f6a353dec94675767b6319"},
+]
+markupsafe = [
+ {file = "MarkupSafe-2.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d8446c54dc28c01e5a2dbac5a25f071f6653e6e40f3a8818e8b45d790fe6ef53"},
+ {file = "MarkupSafe-2.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:36bc903cbb393720fad60fc28c10de6acf10dc6cc883f3e24ee4012371399a38"},
+ {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d7d807855b419fc2ed3e631034685db6079889a1f01d5d9dac950f764da3dad"},
+ {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:add36cb2dbb8b736611303cd3bfcee00afd96471b09cda130da3581cbdc56a6d"},
+ {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:168cd0a3642de83558a5153c8bd34f175a9a6e7f6dc6384b9655d2697312a646"},
+ {file = "MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4dc8f9fb58f7364b63fd9f85013b780ef83c11857ae79f2feda41e270468dd9b"},
+ {file = "MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:20dca64a3ef2d6e4d5d615a3fd418ad3bde77a47ec8a23d984a12b5b4c74491a"},
+ {file = "MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cdfba22ea2f0029c9261a4bd07e830a8da012291fbe44dc794e488b6c9bb353a"},
+ {file = "MarkupSafe-2.0.1-cp310-cp310-win32.whl", hash = "sha256:99df47edb6bda1249d3e80fdabb1dab8c08ef3975f69aed437cb69d0a5de1e28"},
+ {file = "MarkupSafe-2.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:e0f138900af21926a02425cf736db95be9f4af72ba1bb21453432a07f6082134"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f9081981fe268bd86831e5c75f7de206ef275defcb82bc70740ae6dc507aee51"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:0955295dd5eec6cb6cc2fe1698f4c6d84af2e92de33fbcac4111913cd100a6ff"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:0446679737af14f45767963a1a9ef7620189912317d095f2d9ffa183a4d25d2b"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:f826e31d18b516f653fe296d967d700fddad5901ae07c622bb3705955e1faa94"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:fa130dd50c57d53368c9d59395cb5526eda596d3ffe36666cd81a44d56e48872"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:905fec760bd2fa1388bb5b489ee8ee5f7291d692638ea5f67982d968366bef9f"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf5d821ffabf0ef3533c39c518f3357b171a1651c1ff6827325e4489b0e46c3c"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0d4b31cc67ab36e3392bbf3862cfbadac3db12bdd8b02a2731f509ed5b829724"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:baa1a4e8f868845af802979fcdbf0bb11f94f1cb7ced4c4b8a351bb60d108145"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:deb993cacb280823246a026e3b2d81c493c53de6acfd5e6bfe31ab3402bb37dd"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:63f3268ba69ace99cab4e3e3b5840b03340efed0948ab8f78d2fd87ee5442a4f"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:8d206346619592c6200148b01a2142798c989edcb9c896f9ac9722a99d4e77e6"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-win32.whl", hash = "sha256:6c4ca60fa24e85fe25b912b01e62cb969d69a23a5d5867682dd3e80b5b02581d"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b2f4bf27480f5e5e8ce285a8c8fd176c0b03e93dcc6646477d4630e83440c6a9"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0717a7390a68be14b8c793ba258e075c6f4ca819f15edfc2a3a027c823718567"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:6557b31b5e2c9ddf0de32a691f2312a32f77cd7681d8af66c2692efdbef84c18"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:49e3ceeabbfb9d66c3aef5af3a60cc43b85c33df25ce03d0031a608b0a8b2e3f"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:d7f9850398e85aba693bb640262d3611788b1f29a79f0c93c565694658f4071f"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:6a7fae0dd14cf60ad5ff42baa2e95727c3d81ded453457771d02b7d2b3f9c0c2"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:b7f2d075102dc8c794cbde1947378051c4e5180d52d276987b8d28a3bd58c17d"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9936f0b261d4df76ad22f8fee3ae83b60d7c3e871292cd42f40b81b70afae85"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2a7d351cbd8cfeb19ca00de495e224dea7e7d919659c2841bbb7f420ad03e2d6"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:60bf42e36abfaf9aff1f50f52644b336d4f0a3fd6d8a60ca0d054ac9f713a864"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d6c7ebd4e944c85e2c3421e612a7057a2f48d478d79e61800d81468a8d842207"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f0567c4dc99f264f49fe27da5f735f414c4e7e7dd850cfd8e69f0862d7c74ea9"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:89c687013cb1cd489a0f0ac24febe8c7a666e6e221b783e53ac50ebf68e45d86"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-win32.whl", hash = "sha256:a30e67a65b53ea0a5e62fe23682cfe22712e01f453b95233b25502f7c61cb415"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:611d1ad9a4288cf3e3c16014564df047fe08410e628f89805e475368bd304914"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5bb28c636d87e840583ee3adeb78172efc47c8b26127267f54a9c0ec251d41a9"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:be98f628055368795d818ebf93da628541e10b75b41c559fdf36d104c5787066"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1d609f577dc6e1aa17d746f8bd3c31aa4d258f4070d61b2aa5c4166c1539de35"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7d91275b0245b1da4d4cfa07e0faedd5b0812efc15b702576d103293e252af1b"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:01a9b8ea66f1658938f65b93a85ebe8bc016e6769611be228d797c9d998dd298"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:47ab1e7b91c098ab893b828deafa1203de86d0bc6ab587b160f78fe6c4011f75"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:97383d78eb34da7e1fa37dd273c20ad4320929af65d156e35a5e2d89566d9dfb"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fcf051089389abe060c9cd7caa212c707e58153afa2c649f00346ce6d260f1b"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5855f8438a7d1d458206a2466bf82b0f104a3724bf96a1c781ab731e4201731a"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3dd007d54ee88b46be476e293f48c85048603f5f516008bee124ddd891398ed6"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:aca6377c0cb8a8253e493c6b451565ac77e98c2951c45f913e0b52facdcff83f"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:04635854b943835a6ea959e948d19dcd311762c5c0c6e1f0e16ee57022669194"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6300b8454aa6930a24b9618fbb54b5a68135092bc666f7b06901f897fa5c2fee"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-win32.whl", hash = "sha256:023cb26ec21ece8dc3907c0e8320058b2e0cb3c55cf9564da612bc325bed5e64"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:984d76483eb32f1bcb536dc27e4ad56bba4baa70be32fa87152832cdd9db0833"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2ef54abee730b502252bcdf31b10dacb0a416229b72c18b19e24a4509f273d26"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3c112550557578c26af18a1ccc9e090bfe03832ae994343cfdacd287db6a6ae7"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:53edb4da6925ad13c07b6d26c2a852bd81e364f95301c66e930ab2aef5b5ddd8"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:f5653a225f31e113b152e56f154ccbe59eeb1c7487b39b9d9f9cdb58e6c79dc5"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:4efca8f86c54b22348a5467704e3fec767b2db12fc39c6d963168ab1d3fc9135"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:ab3ef638ace319fa26553db0624c4699e31a28bb2a835c5faca8f8acf6a5a902"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:f8ba0e8349a38d3001fae7eadded3f6606f0da5d748ee53cc1dab1d6527b9509"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c47adbc92fc1bb2b3274c4b3a43ae0e4573d9fbff4f54cd484555edbf030baf1"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:37205cac2a79194e3750b0af2a5720d95f786a55ce7df90c3af697bfa100eaac"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:1f2ade76b9903f39aa442b4aadd2177decb66525062db244b35d71d0ee8599b6"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4296f2b1ce8c86a6aea78613c34bb1a672ea0e3de9c6ba08a960efe0b0a09047"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f02365d4e99430a12647f09b6cc8bab61a6564363f313126f775eb4f6ef798e"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5b6d930f030f8ed98e3e6c98ffa0652bdb82601e7a016ec2ab5d7ff23baa78d1"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-win32.whl", hash = "sha256:10f82115e21dc0dfec9ab5c0223652f7197feb168c940f3ef61563fc2d6beb74"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8"},
+ {file = "MarkupSafe-2.0.1.tar.gz", hash = "sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a"},
+]
+marshmallow = [
+ {file = "marshmallow-3.13.0-py2.py3-none-any.whl", hash = "sha256:dd4724335d3c2b870b641ffe4a2f8728a1380cd2e7e2312756715ffeaa82b842"},
+ {file = "marshmallow-3.13.0.tar.gz", hash = "sha256:c67929438fd73a2be92128caa0325b1b5ed8b626d91a094d2f7f2771bf1f1c0e"},
+]
+outcome = [
+ {file = "outcome-1.1.0-py2.py3-none-any.whl", hash = "sha256:c7dd9375cfd3c12db9801d080a3b63d4b0a261aa996c4c13152380587288d958"},
+ {file = "outcome-1.1.0.tar.gz", hash = "sha256:e862f01d4e626e63e8f92c38d1f8d5546d3f9cce989263c521b2e7990d186967"},
+]
+psycopg2-binary = [
+ {file = "psycopg2-binary-2.9.1.tar.gz", hash = "sha256:b0221ca5a9837e040ebf61f48899926b5783668b7807419e4adae8175a31f773"},
+ {file = "psycopg2_binary-2.9.1-cp310-cp310-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:24b0b6688b9f31a911f2361fe818492650795c9e5d3a1bc647acbd7440142a4f"},
+ {file = "psycopg2_binary-2.9.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:542875f62bc56e91c6eac05a0deadeae20e1730be4c6334d8f04c944fcd99759"},
+ {file = "psycopg2_binary-2.9.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:661509f51531ec125e52357a489ea3806640d0ca37d9dada461ffc69ee1e7b6e"},
+ {file = "psycopg2_binary-2.9.1-cp310-cp310-manylinux_2_24_aarch64.whl", hash = "sha256:d92272c7c16e105788efe2cfa5d680f07e34e0c29b03c1908f8636f55d5f915a"},
+ {file = "psycopg2_binary-2.9.1-cp310-cp310-manylinux_2_24_ppc64le.whl", hash = "sha256:736b8797b58febabb85494142c627bd182b50d2a7ec65322983e71065ad3034c"},
+ {file = "psycopg2_binary-2.9.1-cp310-cp310-win32.whl", hash = "sha256:ebccf1123e7ef66efc615a68295bf6fdba875a75d5bba10a05073202598085fc"},
+ {file = "psycopg2_binary-2.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:1f6ca4a9068f5c5c57e744b4baa79f40e83e3746875cac3c45467b16326bab45"},
+ {file = "psycopg2_binary-2.9.1-cp36-cp36m-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:c250a7ec489b652c892e4f0a5d122cc14c3780f9f643e1a326754aedf82d9a76"},
+ {file = "psycopg2_binary-2.9.1-cp36-cp36m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aef9aee84ec78af51107181d02fe8773b100b01c5dfde351184ad9223eab3698"},
+ {file = "psycopg2_binary-2.9.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:123c3fb684e9abfc47218d3784c7b4c47c8587951ea4dd5bc38b6636ac57f616"},
+ {file = "psycopg2_binary-2.9.1-cp36-cp36m-manylinux_2_24_aarch64.whl", hash = "sha256:995fc41ebda5a7a663a254a1dcac52638c3e847f48307b5416ee373da15075d7"},
+ {file = "psycopg2_binary-2.9.1-cp36-cp36m-manylinux_2_24_ppc64le.whl", hash = "sha256:fbb42a541b1093385a2d8c7eec94d26d30437d0e77c1d25dae1dcc46741a385e"},
+ {file = "psycopg2_binary-2.9.1-cp36-cp36m-win32.whl", hash = "sha256:20f1ab44d8c352074e2d7ca67dc00843067788791be373e67a0911998787ce7d"},
+ {file = "psycopg2_binary-2.9.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f6fac64a38f6768e7bc7b035b9e10d8a538a9fadce06b983fb3e6fa55ac5f5ce"},
+ {file = "psycopg2_binary-2.9.1-cp37-cp37m-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:1e3a362790edc0a365385b1ac4cc0acc429a0c0d662d829a50b6ce743ae61b5a"},
+ {file = "psycopg2_binary-2.9.1-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f8559617b1fcf59a9aedba2c9838b5b6aa211ffedecabca412b92a1ff75aac1a"},
+ {file = "psycopg2_binary-2.9.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a36c7eb6152ba5467fb264d73844877be8b0847874d4822b7cf2d3c0cb8cdcb0"},
+ {file = "psycopg2_binary-2.9.1-cp37-cp37m-manylinux_2_24_aarch64.whl", hash = "sha256:2f62c207d1740b0bde5c4e949f857b044818f734a3d57f1d0d0edc65050532ed"},
+ {file = "psycopg2_binary-2.9.1-cp37-cp37m-manylinux_2_24_ppc64le.whl", hash = "sha256:cfc523edecddaef56f6740d7de1ce24a2fdf94fd5e704091856a201872e37f9f"},
+ {file = "psycopg2_binary-2.9.1-cp37-cp37m-win32.whl", hash = "sha256:1e85b74cbbb3056e3656f1cc4781294df03383127a8114cbc6531e8b8367bf1e"},
+ {file = "psycopg2_binary-2.9.1-cp37-cp37m-win_amd64.whl", hash = "sha256:1473c0215b0613dd938db54a653f68251a45a78b05f6fc21af4326f40e8360a2"},
+ {file = "psycopg2_binary-2.9.1-cp38-cp38-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:35c4310f8febe41f442d3c65066ca93cccefd75013df3d8c736c5b93ec288140"},
+ {file = "psycopg2_binary-2.9.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8c13d72ed6af7fd2c8acbd95661cf9477f94e381fce0792c04981a8283b52917"},
+ {file = "psycopg2_binary-2.9.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14db1752acdd2187d99cb2ca0a1a6dfe57fc65c3281e0f20e597aac8d2a5bd90"},
+ {file = "psycopg2_binary-2.9.1-cp38-cp38-manylinux_2_24_aarch64.whl", hash = "sha256:aed4a9a7e3221b3e252c39d0bf794c438dc5453bc2963e8befe9d4cd324dff72"},
+ {file = "psycopg2_binary-2.9.1-cp38-cp38-manylinux_2_24_ppc64le.whl", hash = "sha256:da113b70f6ec40e7d81b43d1b139b9db6a05727ab8be1ee559f3a69854a69d34"},
+ {file = "psycopg2_binary-2.9.1-cp38-cp38-win32.whl", hash = "sha256:4235f9d5ddcab0b8dbd723dca56ea2922b485ea00e1dafacf33b0c7e840b3d32"},
+ {file = "psycopg2_binary-2.9.1-cp38-cp38-win_amd64.whl", hash = "sha256:988b47ac70d204aed01589ed342303da7c4d84b56c2f4c4b8b00deda123372bf"},
+ {file = "psycopg2_binary-2.9.1-cp39-cp39-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:7360647ea04db2e7dff1648d1da825c8cf68dc5fbd80b8fb5b3ee9f068dcd21a"},
+ {file = "psycopg2_binary-2.9.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca86db5b561b894f9e5f115d6a159fff2a2570a652e07889d8a383b5fae66eb4"},
+ {file = "psycopg2_binary-2.9.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ced67f1e34e1a450cdb48eb53ca73b60aa0af21c46b9b35ac3e581cf9f00e31"},
+ {file = "psycopg2_binary-2.9.1-cp39-cp39-manylinux_2_24_aarch64.whl", hash = "sha256:0f2e04bd2a2ab54fa44ee67fe2d002bb90cee1c0f1cc0ebc3148af7b02034cbd"},
+ {file = "psycopg2_binary-2.9.1-cp39-cp39-manylinux_2_24_ppc64le.whl", hash = "sha256:3242b9619de955ab44581a03a64bdd7d5e470cc4183e8fcadd85ab9d3756ce7a"},
+ {file = "psycopg2_binary-2.9.1-cp39-cp39-win32.whl", hash = "sha256:0b7dae87f0b729922e06f85f667de7bf16455d411971b2043bbd9577af9d1975"},
+ {file = "psycopg2_binary-2.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:b4d7679a08fea64573c969f6994a2631908bb2c0e69a7235648642f3d2e39a68"},
+]
+pycparser = [
+ {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"},
+ {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"},
+]
+pycryptodomex = [
+ {file = "pycryptodomex-3.14.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:ca88f2f7020002638276439a01ffbb0355634907d1aa5ca91f3dc0c2e44e8f3b"},
+ {file = "pycryptodomex-3.14.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:8536bc08d130cae6dcba1ea689f2913dfd332d06113904d171f2f56da6228e89"},
+ {file = "pycryptodomex-3.14.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:406ec8cfe0c098fadb18d597dc2ee6de4428d640c0ccafa453f3d9b2e58d29e2"},
+ {file = "pycryptodomex-3.14.1-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:da8db8374295fb532b4b0c467e66800ef17d100e4d5faa2bbbd6df35502da125"},
+ {file = "pycryptodomex-3.14.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:d709572d64825d8d59ea112e11cc7faf6007f294e9951324b7574af4251e4de8"},
+ {file = "pycryptodomex-3.14.1-cp27-cp27m-win32.whl", hash = "sha256:3da13c2535b7aea94cc2a6d1b1b37746814c74b6e80790daddd55ca5c120a489"},
+ {file = "pycryptodomex-3.14.1-cp27-cp27m-win_amd64.whl", hash = "sha256:298c00ea41a81a491d5b244d295d18369e5aac4b61b77b2de5b249ca61cd6659"},
+ {file = "pycryptodomex-3.14.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:77931df40bb5ce5e13f4de2bfc982b2ddc0198971fbd947776c8bb5050896eb2"},
+ {file = "pycryptodomex-3.14.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:c5dd3ffa663c982d7f1be9eb494a8924f6d40e2e2f7d1d27384cfab1b2ac0662"},
+ {file = "pycryptodomex-3.14.1-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:2aa887683eee493e015545bd69d3d21ac8d5ad582674ec98f4af84511e353e45"},
+ {file = "pycryptodomex-3.14.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:8085bd0ad2034352eee4d4f3e2da985c2749cb7344b939f4d95ead38c2520859"},
+ {file = "pycryptodomex-3.14.1-cp35-abi3-macosx_10_9_x86_64.whl", hash = "sha256:e95a4a6c54d27a84a4624d2af8bb9ee178111604653194ca6880c98dcad92f48"},
+ {file = "pycryptodomex-3.14.1-cp35-abi3-manylinux1_i686.whl", hash = "sha256:a4d412eba5679ede84b41dbe48b1bed8f33131ab9db06c238a235334733acc5e"},
+ {file = "pycryptodomex-3.14.1-cp35-abi3-manylinux1_x86_64.whl", hash = "sha256:d2cce1c82a7845d7e2e8a0956c6b7ed3f1661c9acf18eb120fc71e098ab5c6fe"},
+ {file = "pycryptodomex-3.14.1-cp35-abi3-manylinux2010_i686.whl", hash = "sha256:f75009715dcf4a3d680c2338ab19dac5498f8121173a929872950f4fb3a48fbf"},
+ {file = "pycryptodomex-3.14.1-cp35-abi3-manylinux2010_x86_64.whl", hash = "sha256:1ca8e1b4c62038bb2da55451385246f51f412c5f5eabd64812c01766a5989b4a"},
+ {file = "pycryptodomex-3.14.1-cp35-abi3-win32.whl", hash = "sha256:ee835def05622e0c8b1435a906491760a43d0c462f065ec9143ec4b8d79f8bff"},
+ {file = "pycryptodomex-3.14.1-cp35-abi3-win_amd64.whl", hash = "sha256:b5a185ae79f899b01ca49f365bdf15a45d78d9856f09b0de1a41b92afce1a07f"},
+ {file = "pycryptodomex-3.14.1-pp27-pypy_73-macosx_10_9_x86_64.whl", hash = "sha256:797a36bd1f69df9e2798e33edb4bd04e5a30478efc08f9428c087f17f65a7045"},
+ {file = "pycryptodomex-3.14.1-pp27-pypy_73-manylinux1_x86_64.whl", hash = "sha256:aebecde2adc4a6847094d3bd6a8a9538ef3438a5ea84ac1983fcb167db614461"},
+ {file = "pycryptodomex-3.14.1-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:f8524b8bc89470cec7ac51734907818d3620fb1637f8f8b542d650ebec42a126"},
+ {file = "pycryptodomex-3.14.1-pp27-pypy_73-win32.whl", hash = "sha256:4d0db8df9ffae36f416897ad184608d9d7a8c2b46c4612c6bc759b26c073f750"},
+ {file = "pycryptodomex-3.14.1-pp36-pypy36_pp73-macosx_10_9_x86_64.whl", hash = "sha256:b276cc4deb4a80f9dfd47a41ebb464b1fe91efd8b1b8620cf5ccf8b824b850d6"},
+ {file = "pycryptodomex-3.14.1-pp36-pypy36_pp73-manylinux1_x86_64.whl", hash = "sha256:e36c7e3b5382cd5669cf199c4a04a0279a43b2a3bdd77627e9b89778ac9ec08c"},
+ {file = "pycryptodomex-3.14.1-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:c4d8977ccda886d88dc3ca789de2f1adc714df912ff3934b3d0a3f3d777deafb"},
+ {file = "pycryptodomex-3.14.1-pp36-pypy36_pp73-win32.whl", hash = "sha256:530756d2faa40af4c1f74123e1d889bd07feae45bac2fd32f259a35f7aa74151"},
+ {file = "pycryptodomex-3.14.1.tar.gz", hash = "sha256:2ce76ed0081fd6ac8c74edc75b9d14eca2064173af79843c24fa62573263c1f2"},
+]
+pydantic = [
+ {file = "pydantic-1.8.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:05ddfd37c1720c392f4e0d43c484217b7521558302e7069ce8d318438d297739"},
+ {file = "pydantic-1.8.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:a7c6002203fe2c5a1b5cbb141bb85060cbff88c2d78eccbc72d97eb7022c43e4"},
+ {file = "pydantic-1.8.2-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:589eb6cd6361e8ac341db97602eb7f354551482368a37f4fd086c0733548308e"},
+ {file = "pydantic-1.8.2-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:10e5622224245941efc193ad1d159887872776df7a8fd592ed746aa25d071840"},
+ {file = "pydantic-1.8.2-cp36-cp36m-win_amd64.whl", hash = "sha256:99a9fc39470010c45c161a1dc584997f1feb13f689ecf645f59bb4ba623e586b"},
+ {file = "pydantic-1.8.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a83db7205f60c6a86f2c44a61791d993dff4b73135df1973ecd9eed5ea0bda20"},
+ {file = "pydantic-1.8.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:41b542c0b3c42dc17da70554bc6f38cbc30d7066d2c2815a94499b5684582ecb"},
+ {file = "pydantic-1.8.2-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:ea5cb40a3b23b3265f6325727ddfc45141b08ed665458be8c6285e7b85bd73a1"},
+ {file = "pydantic-1.8.2-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:18b5ea242dd3e62dbf89b2b0ec9ba6c7b5abaf6af85b95a97b00279f65845a23"},
+ {file = "pydantic-1.8.2-cp37-cp37m-win_amd64.whl", hash = "sha256:234a6c19f1c14e25e362cb05c68afb7f183eb931dd3cd4605eafff055ebbf287"},
+ {file = "pydantic-1.8.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:021ea0e4133e8c824775a0cfe098677acf6fa5a3cbf9206a376eed3fc09302cd"},
+ {file = "pydantic-1.8.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:e710876437bc07bd414ff453ac8ec63d219e7690128d925c6e82889d674bb505"},
+ {file = "pydantic-1.8.2-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:ac8eed4ca3bd3aadc58a13c2aa93cd8a884bcf21cb019f8cfecaae3b6ce3746e"},
+ {file = "pydantic-1.8.2-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:4a03cbbe743e9c7247ceae6f0d8898f7a64bb65800a45cbdc52d65e370570820"},
+ {file = "pydantic-1.8.2-cp38-cp38-win_amd64.whl", hash = "sha256:8621559dcf5afacf0069ed194278f35c255dc1a1385c28b32dd6c110fd6531b3"},
+ {file = "pydantic-1.8.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8b223557f9510cf0bfd8b01316bf6dd281cf41826607eada99662f5e4963f316"},
+ {file = "pydantic-1.8.2-cp39-cp39-manylinux1_i686.whl", hash = "sha256:244ad78eeb388a43b0c927e74d3af78008e944074b7d0f4f696ddd5b2af43c62"},
+ {file = "pydantic-1.8.2-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:05ef5246a7ffd2ce12a619cbb29f3307b7c4509307b1b49f456657b43529dc6f"},
+ {file = "pydantic-1.8.2-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:54cd5121383f4a461ff7644c7ca20c0419d58052db70d8791eacbbe31528916b"},
+ {file = "pydantic-1.8.2-cp39-cp39-win_amd64.whl", hash = "sha256:4be75bebf676a5f0f87937c6ddb061fa39cbea067240d98e298508c1bda6f3f3"},
+ {file = "pydantic-1.8.2-py3-none-any.whl", hash = "sha256:fec866a0b59f372b7e776f2d7308511784dace622e0992a0b59ea3ccee0ae833"},
+ {file = "pydantic-1.8.2.tar.gz", hash = "sha256:26464e57ccaafe72b7ad156fdaa4e9b9ef051f69e175dbbb463283000c05ab7b"},
+]
+pypng = [
+ {file = "pypng-0.0.21-py3-none-any.whl", hash = "sha256:76f8a1539ec56451da7ab7121f12a361969fe0f2d48d703d198ce2a99d6c5afd"},
+]
+pyqrcode = [
+ {file = "PyQRCode-1.2.1.tar.gz", hash = "sha256:fdbf7634733e56b72e27f9bce46e4550b75a3a2c420414035cae9d9d26b234d5"},
+ {file = "PyQRCode-1.2.1.zip", hash = "sha256:1b2812775fa6ff5c527977c4cd2ccb07051ca7d0bc0aecf937a43864abe5eff6"},
+]
+pyscss = [
+ {file = "pyScss-1.3.7.tar.gz", hash = "sha256:f1df571569021a23941a538eb154405dde80bed35dc1ea7c5f3e18e0144746bf"},
+]
+python-dotenv = [
+ {file = "python-dotenv-0.19.0.tar.gz", hash = "sha256:f521bc2ac9a8e03c736f62911605c5d83970021e3fa95b37d769e2bbbe9b6172"},
+ {file = "python_dotenv-0.19.0-py2.py3-none-any.whl", hash = "sha256:aae25dc1ebe97c420f50b81fb0e5c949659af713f31fdb63c749ca68748f34b1"},
+]
+pyyaml = [
+ {file = "PyYAML-5.4.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922"},
+ {file = "PyYAML-5.4.1-cp27-cp27m-win32.whl", hash = "sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393"},
+ {file = "PyYAML-5.4.1-cp27-cp27m-win_amd64.whl", hash = "sha256:4465124ef1b18d9ace298060f4eccc64b0850899ac4ac53294547536533800c8"},
+ {file = "PyYAML-5.4.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185"},
+ {file = "PyYAML-5.4.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253"},
+ {file = "PyYAML-5.4.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc"},
+ {file = "PyYAML-5.4.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:72a01f726a9c7851ca9bfad6fd09ca4e090a023c00945ea05ba1638c09dc3347"},
+ {file = "PyYAML-5.4.1-cp36-cp36m-manylinux2014_s390x.whl", hash = "sha256:895f61ef02e8fed38159bb70f7e100e00f471eae2bc838cd0f4ebb21e28f8541"},
+ {file = "PyYAML-5.4.1-cp36-cp36m-win32.whl", hash = "sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5"},
+ {file = "PyYAML-5.4.1-cp36-cp36m-win_amd64.whl", hash = "sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df"},
+ {file = "PyYAML-5.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018"},
+ {file = "PyYAML-5.4.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63"},
+ {file = "PyYAML-5.4.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:cb333c16912324fd5f769fff6bc5de372e9e7a202247b48870bc251ed40239aa"},
+ {file = "PyYAML-5.4.1-cp37-cp37m-manylinux2014_s390x.whl", hash = "sha256:fe69978f3f768926cfa37b867e3843918e012cf83f680806599ddce33c2c68b0"},
+ {file = "PyYAML-5.4.1-cp37-cp37m-win32.whl", hash = "sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b"},
+ {file = "PyYAML-5.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf"},
+ {file = "PyYAML-5.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46"},
+ {file = "PyYAML-5.4.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb"},
+ {file = "PyYAML-5.4.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:fd7f6999a8070df521b6384004ef42833b9bd62cfee11a09bda1079b4b704247"},
+ {file = "PyYAML-5.4.1-cp38-cp38-manylinux2014_s390x.whl", hash = "sha256:bfb51918d4ff3d77c1c856a9699f8492c612cde32fd3bcd344af9be34999bfdc"},
+ {file = "PyYAML-5.4.1-cp38-cp38-win32.whl", hash = "sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc"},
+ {file = "PyYAML-5.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696"},
+ {file = "PyYAML-5.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77"},
+ {file = "PyYAML-5.4.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183"},
+ {file = "PyYAML-5.4.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:d483ad4e639292c90170eb6f7783ad19490e7a8defb3e46f97dfe4bacae89122"},
+ {file = "PyYAML-5.4.1-cp39-cp39-manylinux2014_s390x.whl", hash = "sha256:fdc842473cd33f45ff6bce46aea678a54e3d21f1b61a7750ce3c498eedfe25d6"},
+ {file = "PyYAML-5.4.1-cp39-cp39-win32.whl", hash = "sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10"},
+ {file = "PyYAML-5.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db"},
+ {file = "PyYAML-5.4.1.tar.gz", hash = "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e"},
+]
+represent = [
+ {file = "Represent-1.6.0.post0-py2.py3-none-any.whl", hash = "sha256:99142650756ef1998ce0661568f54a47dac8c638fb27e3816c02536575dbba8c"},
+ {file = "Represent-1.6.0.post0.tar.gz", hash = "sha256:026c0de2ee8385d1255b9c2426cd4f03fe9177ac94c09979bc601946c8493aa0"},
+]
+rfc3986 = [
+ {file = "rfc3986-1.5.0-py2.py3-none-any.whl", hash = "sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97"},
+ {file = "rfc3986-1.5.0.tar.gz", hash = "sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835"},
+]
+secp256k1 = [
+ {file = "secp256k1-0.14.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f666c67dcf1dc69e1448b2ede5e12aaf382b600204a61dbc65e4f82cea444405"},
+ {file = "secp256k1-0.14.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:fcabb3c3497a902fb61eec72d1b69bf72747d7bcc2a732d56d9319a1e8322262"},
+ {file = "secp256k1-0.14.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7a27c479ab60571502516a1506a562d0a9df062de8ad645313fabfcc97252816"},
+ {file = "secp256k1-0.14.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f4b9306bff6dde020444dfee9ca9b9f5b20ca53a2c0b04898361a3f43d5daf2e"},
+ {file = "secp256k1-0.14.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:72735da6cb28273e924431cd40aa607e7f80ef09608c8c9300be2e0e1d2417b4"},
+ {file = "secp256k1-0.14.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:87f4ad42a370f768910585989a301d1d65de17dcd86f6e8def9b021364b34d5c"},
+ {file = "secp256k1-0.14.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:130f119b06142e597c10eb4470b5a38eae865362d01aaef06b113478d77f728d"},
+ {file = "secp256k1-0.14.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:3aedcfe6eb1c5fa7c6be25b7cc91c76d8eb984271920ba0f7a934ae41ed56f51"},
+ {file = "secp256k1-0.14.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:c91dd3154f6c46ac798d9a41166120e1751222587f54516cc3f378f56ce4ac82"},
+ {file = "secp256k1-0.14.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:fec790cb6d0d37129ca0ce5b3f8e85692d5fb618d1c440f189453d18694035df"},
+ {file = "secp256k1-0.14.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:63eb148196b8f646922d4be6739b17fbbf50ebb3a020078c823e2445d88b7a81"},
+ {file = "secp256k1-0.14.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:adc23a4c5d24c95191638eb2ca313097827f07db102e77b59faed15d50c98cae"},
+ {file = "secp256k1-0.14.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:ce0314788d3248b275426501228969fd32f6501c9d1837902ee0e7bd8264a36f"},
+ {file = "secp256k1-0.14.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:bc761894b3634021686714278fc62b73395fa3eded33453eadfd8a00a6c44ef3"},
+ {file = "secp256k1-0.14.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:373dc8bca735f3c2d73259aa2711a9ecea2f3c7edbb663555fe3422e3dd76102"},
+ {file = "secp256k1-0.14.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fe3f503c9dfdf663b500d3e0688ad842e116c2907ad3f1e1d685812df3f56290"},
+ {file = "secp256k1-0.14.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:4b1bf09953cde181132cf5e9033065615e5c2694e803165e2db763efa47695e5"},
+ {file = "secp256k1-0.14.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6af07be5f8612628c3638dc7b208f6cc78d0abae3e25797eadb13890c7d5da81"},
+ {file = "secp256k1-0.14.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:a8dbd75a9fb6f42de307f3c5e24573fe59c3374637cbf39136edc66c200a4029"},
+ {file = "secp256k1-0.14.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:97a30c8dae633cb18135c76b6517ae99dc59106818e8985be70dbc05dcc06c0d"},
+ {file = "secp256k1-0.14.0-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f4062d8c101aa63b9ecb3709f1f075ad9c01b6672869bbaa1bd77271816936a7"},
+ {file = "secp256k1-0.14.0-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c9e7c024ff17e9b9d7c392bb2a917da231d6cb40ab119389ff1f51dca10339a4"},
+ {file = "secp256k1-0.14.0.tar.gz", hash = "sha256:82c06712d69ef945220c8b53c1a0d424c2ff6a1f64aee609030df79ad8383397"},
+]
+shortuuid = [
+ {file = "shortuuid-1.0.1-py3-none-any.whl", hash = "sha256:492c7402ff91beb1342a5898bd61ea953985bf24a41cd9f247409aa2e03c8f77"},
+ {file = "shortuuid-1.0.1.tar.gz", hash = "sha256:3c11d2007b915c43bee3e10625f068d8a349e04f0d81f08f5fa08507427ebf1f"},
+]
+six = [
+ {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
+ {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
+]
+sniffio = [
+ {file = "sniffio-1.2.0-py3-none-any.whl", hash = "sha256:471b71698eac1c2112a40ce2752bb2f4a4814c22a54a3eed3676bc0f5ca9f663"},
+ {file = "sniffio-1.2.0.tar.gz", hash = "sha256:c4666eecec1d3f50960c6bdf61ab7bc350648da6c126e3cf6898d8cd4ddcd3de"},
+]
+sqlalchemy = [
+ {file = "SQLAlchemy-1.3.23-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:fd3b96f8c705af8e938eaa99cbd8fd1450f632d38cad55e7367c33b263bf98ec"},
+ {file = "SQLAlchemy-1.3.23-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:29cccc9606750fe10c5d0e8bd847f17a97f3850b8682aef1f56f5d5e1a5a64b1"},
+ {file = "SQLAlchemy-1.3.23-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:927ce09e49bff3104459e1451ce82983b0a3062437a07d883a4c66f0b344c9b5"},
+ {file = "SQLAlchemy-1.3.23-cp27-cp27m-win32.whl", hash = "sha256:b4b0e44d586cd64b65b507fa116a3814a1a53d55dce4836d7c1a6eb2823ff8d1"},
+ {file = "SQLAlchemy-1.3.23-cp27-cp27m-win_amd64.whl", hash = "sha256:6b8b8c80c7f384f06825612dd078e4a31f0185e8f1f6b8c19e188ff246334205"},
+ {file = "SQLAlchemy-1.3.23-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:9e9c25522933e569e8b53ccc644dc993cab87e922fb7e142894653880fdd419d"},
+ {file = "SQLAlchemy-1.3.23-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:a0e306e9bb76fd93b29ae3a5155298e4c1b504c7cbc620c09c20858d32d16234"},
+ {file = "SQLAlchemy-1.3.23-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:6c9e6cc9237de5660bcddea63f332428bb83c8e2015c26777281f7ffbd2efb84"},
+ {file = "SQLAlchemy-1.3.23-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:94f667d86be82dd4cb17d08de0c3622e77ca865320e0b95eae6153faa7b4ecaf"},
+ {file = "SQLAlchemy-1.3.23-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:751934967f5336a3e26fc5993ccad1e4fee982029f9317eb6153bc0bc3d2d2da"},
+ {file = "SQLAlchemy-1.3.23-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:63677d0c08524af4c5893c18dbe42141de7178001360b3de0b86217502ed3601"},
+ {file = "SQLAlchemy-1.3.23-cp35-cp35m-win32.whl", hash = "sha256:ddfb511e76d016c3a160910642d57f4587dc542ce5ee823b0d415134790eeeb9"},
+ {file = "SQLAlchemy-1.3.23-cp35-cp35m-win_amd64.whl", hash = "sha256:040bdfc1d76a9074717a3f43455685f781c581f94472b010cd6c4754754e1862"},
+ {file = "SQLAlchemy-1.3.23-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:d1a85dfc5dee741bf49cb9b6b6b8d2725a268e4992507cf151cba26b17d97c37"},
+ {file = "SQLAlchemy-1.3.23-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:639940bbe1108ac667dcffc79925db2966826c270112e9159439ab6bb14f8d80"},
+ {file = "SQLAlchemy-1.3.23-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:e8a1750b44ad6422ace82bf3466638f1aa0862dbb9689690d5f2f48cce3476c8"},
+ {file = "SQLAlchemy-1.3.23-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:e5bb3463df697279e5459a7316ad5a60b04b0107f9392e88674d0ece70e9cf70"},
+ {file = "SQLAlchemy-1.3.23-cp36-cp36m-win32.whl", hash = "sha256:e273367f4076bd7b9a8dc2e771978ef2bfd6b82526e80775a7db52bff8ca01dd"},
+ {file = "SQLAlchemy-1.3.23-cp36-cp36m-win_amd64.whl", hash = "sha256:ac2244e64485c3778f012951fdc869969a736cd61375fde6096d08850d8be729"},
+ {file = "SQLAlchemy-1.3.23-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:23927c3981d1ec6b4ea71eb99d28424b874d9c696a21e5fbd9fa322718be3708"},
+ {file = "SQLAlchemy-1.3.23-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:d90010304abb4102123d10cbad2cdf2c25a9f2e66a50974199b24b468509bad5"},
+ {file = "SQLAlchemy-1.3.23-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:a8bfc1e1afe523e94974132d7230b82ca7fa2511aedde1f537ec54db0399541a"},
+ {file = "SQLAlchemy-1.3.23-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:269990b3ab53cb035d662dcde51df0943c1417bdab707dc4a7e4114a710504b4"},
+ {file = "SQLAlchemy-1.3.23-cp37-cp37m-win32.whl", hash = "sha256:fdd2ed7395df8ac2dbb10cefc44737b66c6a5cd7755c92524733d7a443e5b7e2"},
+ {file = "SQLAlchemy-1.3.23-cp37-cp37m-win_amd64.whl", hash = "sha256:6a939a868fdaa4b504e8b9d4a61f21aac11e3fecc8a8214455e144939e3d2aea"},
+ {file = "SQLAlchemy-1.3.23-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:24f9569e82a009a09ce2d263559acb3466eba2617203170e4a0af91e75b4f075"},
+ {file = "SQLAlchemy-1.3.23-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:2578dbdbe4dbb0e5126fb37ffcd9793a25dcad769a95f171a2161030bea850ff"},
+ {file = "SQLAlchemy-1.3.23-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:1fe5d8d39118c2b018c215c37b73fd6893c3e1d4895be745ca8ff6eb83333ed3"},
+ {file = "SQLAlchemy-1.3.23-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:c7dc052432cd5d060d7437e217dd33c97025287f99a69a50e2dc1478dd610d64"},
+ {file = "SQLAlchemy-1.3.23-cp38-cp38-win32.whl", hash = "sha256:ecce8c021894a77d89808222b1ff9687ad84db54d18e4bd0500ca766737faaf6"},
+ {file = "SQLAlchemy-1.3.23-cp38-cp38-win_amd64.whl", hash = "sha256:37b83bf81b4b85dda273aaaed5f35ea20ad80606f672d94d2218afc565fb0173"},
+ {file = "SQLAlchemy-1.3.23-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:8be835aac18ec85351385e17b8665bd4d63083a7160a017bef3d640e8e65cadb"},
+ {file = "SQLAlchemy-1.3.23-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:6ec1044908414013ebfe363450c22f14698803ce97fbb47e53284d55c5165848"},
+ {file = "SQLAlchemy-1.3.23-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:eab063a70cca4a587c28824e18be41d8ecc4457f8f15b2933584c6c6cccd30f0"},
+ {file = "SQLAlchemy-1.3.23-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:baeb451ee23e264de3f577fee5283c73d9bbaa8cb921d0305c0bbf700094b65b"},
+ {file = "SQLAlchemy-1.3.23-cp39-cp39-win32.whl", hash = "sha256:94208867f34e60f54a33a37f1c117251be91a47e3bfdb9ab8a7847f20886ad06"},
+ {file = "SQLAlchemy-1.3.23-cp39-cp39-win_amd64.whl", hash = "sha256:f4d972139d5000105fcda9539a76452039434013570d6059993120dc2a65e447"},
+ {file = "SQLAlchemy-1.3.23.tar.gz", hash = "sha256:6fca33672578666f657c131552c4ef8979c1606e494f78cd5199742dfb26918b"},
+]
+sqlalchemy-aio = [
+ {file = "sqlalchemy_aio-0.16.0-py2.py3-none-any.whl", hash = "sha256:f767320427c22c66fa5840a1f17f3261110a8ddc8560558f4fbf12d31a66b17b"},
+ {file = "sqlalchemy_aio-0.16.0.tar.gz", hash = "sha256:7f77366f55d34891c87386dd0962a28b948b684e8ea5edb7daae4187c0b291bf"},
+]
+sse-starlette = [
+ {file = "sse-starlette-0.6.2.tar.gz", hash = "sha256:1c0cc62cc7d021a386dc06a16a9ddc3e2861d19da6bc2e654e65cc111e820456"},
+]
+starlette = [
+ {file = "starlette-0.19.1-py3-none-any.whl", hash = "sha256:5a60c5c2d051f3a8eb546136aa0c9399773a689595e099e0877704d5888279bf"},
+ {file = "starlette-0.19.1.tar.gz", hash = "sha256:c6d21096774ecb9639acad41b86b7706e52ba3bf1dc13ea4ed9ad593d47e24c7"},
+]
+typing-extensions = [
+ {file = "typing_extensions-3.10.0.2-py2-none-any.whl", hash = "sha256:d8226d10bc02a29bcc81df19a26e56a9647f8b0a6d4a83924139f4a8b01f17b7"},
+ {file = "typing_extensions-3.10.0.2-py3-none-any.whl", hash = "sha256:f1d25edafde516b146ecd0613dabcc61409817af4766fbbcfb8d1ad4ec441a34"},
+ {file = "typing_extensions-3.10.0.2.tar.gz", hash = "sha256:49f75d16ff11f1cd258e1b988ccff82a3ca5570217d7ad8c5f48205dd99a677e"},
+]
+uvicorn = [
+ {file = "uvicorn-0.18.1-py3-none-any.whl", hash = "sha256:013c4ea0787cc2dc456ef4368e18c01982e6be57903e4d3183218e543eb889b7"},
+ {file = "uvicorn-0.18.1.tar.gz", hash = "sha256:35703e6518105cfe53f16a5a9435db3e2e227d0784f1fd8fbc1214b1fdc108df"},
+]
+uvloop = [
+ {file = "uvloop-0.16.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6224f1401025b748ffecb7a6e2652b17768f30b1a6a3f7b44660e5b5b690b12d"},
+ {file = "uvloop-0.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:30ba9dcbd0965f5c812b7c2112a1ddf60cf904c1c160f398e7eed3a6b82dcd9c"},
+ {file = "uvloop-0.16.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:bd53f7f5db562f37cd64a3af5012df8cac2c464c97e732ed556800129505bd64"},
+ {file = "uvloop-0.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:772206116b9b57cd625c8a88f2413df2fcfd0b496eb188b82a43bed7af2c2ec9"},
+ {file = "uvloop-0.16.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b572256409f194521a9895aef274cea88731d14732343da3ecdb175228881638"},
+ {file = "uvloop-0.16.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:04ff57aa137230d8cc968f03481176041ae789308b4d5079118331ab01112450"},
+ {file = "uvloop-0.16.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a19828c4f15687675ea912cc28bbcb48e9bb907c801873bd1519b96b04fb805"},
+ {file = "uvloop-0.16.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e814ac2c6f9daf4c36eb8e85266859f42174a4ff0d71b99405ed559257750382"},
+ {file = "uvloop-0.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bd8f42ea1ea8f4e84d265769089964ddda95eb2bb38b5cbe26712b0616c3edee"},
+ {file = "uvloop-0.16.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:647e481940379eebd314c00440314c81ea547aa636056f554d491e40503c8464"},
+ {file = "uvloop-0.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e0d26fa5875d43ddbb0d9d79a447d2ace4180d9e3239788208527c4784f7cab"},
+ {file = "uvloop-0.16.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:6ccd57ae8db17d677e9e06192e9c9ec4bd2066b77790f9aa7dede2cc4008ee8f"},
+ {file = "uvloop-0.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:089b4834fd299d82d83a25e3335372f12117a7d38525217c2258e9b9f4578897"},
+ {file = "uvloop-0.16.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98d117332cc9e5ea8dfdc2b28b0a23f60370d02e1395f88f40d1effd2cb86c4f"},
+ {file = "uvloop-0.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e5f2e2ff51aefe6c19ee98af12b4ae61f5be456cd24396953244a30880ad861"},
+ {file = "uvloop-0.16.0.tar.gz", hash = "sha256:f74bc20c7b67d1c27c72601c78cf95be99d5c2cdd4514502b4f3eb0933ff1228"},
+]
+watchgod = [
+ {file = "watchgod-0.7-py3-none-any.whl", hash = "sha256:d6c1ea21df37847ac0537ca0d6c2f4cdf513562e95f77bb93abbcf05573407b7"},
+ {file = "watchgod-0.7.tar.gz", hash = "sha256:48140d62b0ebe9dd9cf8381337f06351e1f2e70b2203fa9c6eff4e572ca84f29"},
+]
+websockets = [
+ {file = "websockets-10.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cd8c6f2ec24aedace251017bc7a414525171d4e6578f914acab9349362def4da"},
+ {file = "websockets-10.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:1f6b814cff6aadc4288297cb3a248614829c6e4ff5556593c44a115e9dd49939"},
+ {file = "websockets-10.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:01db0ecd1a0ca6702d02a5ed40413e18b7d22f94afb3bbe0d323bac86c42c1c8"},
+ {file = "websockets-10.0-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:82b17524b1ce6ae7f7dd93e4d18e9b9474071e28b65dbf1dfe9b5767778db379"},
+ {file = "websockets-10.0-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:8bbf8660c3f833ddc8b1afab90213f2e672a9ddac6eecb3cde968e6b2807c1c7"},
+ {file = "websockets-10.0-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:b8176deb6be540a46695960a765a77c28ac8b2e3ef2ec95d50a4f5df901edb1c"},
+ {file = "websockets-10.0-cp37-cp37m-win32.whl", hash = "sha256:706e200fc7f03bed99ad0574cd1ea8b0951477dd18cc978ccb190683c69dba76"},
+ {file = "websockets-10.0-cp37-cp37m-win_amd64.whl", hash = "sha256:5b2600e01c7ca6f840c42c747ffbe0254f319594ed108db847eb3d75f4aacb80"},
+ {file = "websockets-10.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:085bb8a6e780d30eaa1ba48ac7f3a6707f925edea787cfb761ce5a39e77ac09b"},
+ {file = "websockets-10.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:9a4d889162bd48588e80950e07fa5e039eee9deb76a58092e8c3ece96d7ef537"},
+ {file = "websockets-10.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:b4ade7569b6fd17912452f9c3757d96f8e4044016b6d22b3b8391e641ca50456"},
+ {file = "websockets-10.0-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:2a43072e434c041a99f2e1eb9b692df0232a38c37c61d00e9f24db79474329e4"},
+ {file = "websockets-10.0-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:7f79f02c7f9a8320aff7d3321cd1c7e3a7dbc15d922ac996cca827301ee75238"},
+ {file = "websockets-10.0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:1ac35426fe3e7d3d0fac3d63c8965c76ed67a8fd713937be072bf0ce22808539"},
+ {file = "websockets-10.0-cp38-cp38-win32.whl", hash = "sha256:ff59c6bdb87b31f7e2d596f09353d5a38c8c8ff571b0e2238e8ee2d55ad68465"},
+ {file = "websockets-10.0-cp38-cp38-win_amd64.whl", hash = "sha256:d67646ddd17a86117ae21c27005d83c1895c0cef5d7be548b7549646372f868a"},
+ {file = "websockets-10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:82bd921885231f4a30d9bc550552495b3fc36b1235add6d374e7c65c3babd805"},
+ {file = "websockets-10.0-cp39-cp39-manylinux1_i686.whl", hash = "sha256:7d2e12e4f901f1bc062dfdf91831712c4106ed18a9a4cdb65e2e5f502124ca37"},
+ {file = "websockets-10.0-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:71358c7816e2762f3e4af3adf0040f268e219f5a38cb3487a9d0fc2e554fef6a"},
+ {file = "websockets-10.0-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:fe83b3ec9ef34063d86dfe1029160a85f24a5a94271036e5714a57acfdd089a1"},
+ {file = "websockets-10.0-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:eb282127e9c136f860c6068a4fba5756eb25e755baffb5940b6f1eae071928b2"},
+ {file = "websockets-10.0-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:62160772314920397f9d219147f958b33fa27a12c662d4455c9ccbba9a07e474"},
+ {file = "websockets-10.0-cp39-cp39-win32.whl", hash = "sha256:e42a1f1e03437b017af341e9bbfdc09252cd48ef32a8c3c3ead769eab3b17368"},
+ {file = "websockets-10.0-cp39-cp39-win_amd64.whl", hash = "sha256:c5880442f5fc268f1ef6d37b2c152c114deccca73f48e3a8c48004d2f16f4567"},
+ {file = "websockets-10.0.tar.gz", hash = "sha256:c4fc9a1d242317892590abe5b61a9127f1a61740477bfb121743f290b8054002"},
+]
+win32-setctime = []
+zipp = [
+ {file = "zipp-3.5.0-py3-none-any.whl", hash = "sha256:957cfda87797e389580cb8b9e3870841ca991e2125350677b2ca83a0e99390a3"},
+ {file = "zipp-3.5.0.tar.gz", hash = "sha256:f5812b1e007e48cff63449a5e9f4e7ebea716b4111f9c4f9a645f91d579bf0c4"},
+]
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 000000000..5c4bc7a05
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,70 @@
+[tool.poetry]
+name = "lnbits"
+version = "0.1.0"
+description = ""
+authors = ["matthewcroughan "]
+
+[tool.poetry.build]
+generate-setup-file = false
+script = "build.py"
+
+[tool.poetry.dependencies]
+python = "^3.9"
+aiofiles = "0.7.0"
+asgiref = "3.4.1"
+attrs = "21.2.0"
+bech32 = "1.2.0"
+bitstring = "3.1.9"
+cerberus = "1.3.4"
+certifi = "2021.5.30"
+charset-normalizer = "2.0.6"
+click = "8.0.1"
+ecdsa = "0.17.0"
+embit = "0.4.9"
+environs = "9.3.3"
+fastapi = "0.78.0"
+h11 = "0.12.0"
+httpcore = "0.13.7"
+httptools = "0.2.0"
+httpx = "0.19.0"
+idna = "3.2"
+importlib-metadata = "4.8.1"
+jinja2 = "3.0.1"
+lnurl = "0.3.6"
+markupsafe = "2.0.1"
+marshmallow = "3.13.0"
+outcome = "1.1.0"
+psycopg2-binary = "2.9.1"
+pycryptodomex = "3.14.1"
+pydantic = "1.8.2"
+pypng = "0.0.21"
+pyqrcode = "1.2.1"
+pyscss = "1.3.7"
+python-dotenv = "0.19.0"
+pyyaml = "5.4.1"
+represent = "1.6.0.post0"
+rfc3986 = "1.5.0"
+secp256k1 = "0.14.0"
+shortuuid = "1.0.1"
+six = "1.16.0"
+sniffio = "1.2.0"
+sqlalchemy = "1.3.23"
+sqlalchemy-aio = "0.16.0"
+sse-starlette = "0.6.2"
+typing-extensions = "3.10.0.2"
+uvicorn = "0.18.1"
+uvloop = "0.16.0"
+watchgod = "0.7"
+websockets = "10.0"
+zipp = "3.5.0"
+loguru = "0.5.3"
+cffi = "1.15.0"
+
+[tool.poetry.dev-dependencies]
+
+[build-system]
+requires = ["poetry-core>=1.0.0"]
+build-backend = "poetry.core.masonry.api"
+
+[tool.poetry.scripts]
+lnbits = "lnbits.server:main"
diff --git a/requirements.txt b/requirements.txt
index f8ccf47cb..23d428e54 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,52 +1,52 @@
-aiofiles==0.7.0
-anyio==3.3.1
-asgiref==3.4.1
+aiofiles==0.8.0
+anyio==3.6.1
asyncio==3.4.3
-attrs==21.2.0
+attrs==21.4.0
bech32==1.2.0
bitstring==3.1.9
cerberus==1.3.4
-certifi==2021.5.30
-charset-normalizer==2.0.6
-click==8.0.1
-ecdsa==0.17.0
-embit==0.4.9
-environs==9.3.3
-fastapi==0.68.1
+certifi==2022.6.15
+cffi==1.15.0
+click==8.1.3
+ecdsa==0.18.0
+embit==0.5.0
+environs==9.5.0
+fastapi==0.79.0
h11==0.12.0
httpcore==0.15.0
-httptools==0.2.0
+httptools==0.4.0
httpx==0.23.0
-idna==3.2
-importlib-metadata==4.8.1
+idna==3.3
jinja2==3.0.1
lnurl==0.3.6
loguru==0.6.0
-markupsafe==2.0.1
-marshmallow==3.13.0
-outcome==1.1.0
-psycopg2-binary==2.9.1
-pycryptodomex==3.14.1
-pydantic==1.8.2
-pypng==0.0.21
+markupsafe==2.1.1
+marshmallow==3.17.0
+outcome==1.2.0
+packaging==21.3
+psycopg2-binary==2.9.3
+pycparser==2.21
+pycryptodomex==3.15.0
+pydantic==1.9.1
+pyngrok==5.1.0
+pyparsing==3.0.9
+pypng==0.20220715.0
pyqrcode==1.2.1
-pyscss==1.3.7
-python-dotenv==0.19.0
-pyyaml==5.4.1
+pyscss==1.4.0
+python-dotenv==0.20.0
+pyyaml==6.0
represent==1.6.0.post0
rfc3986==1.5.0
secp256k1==0.14.0
-cffi==1.15.0
-shortuuid==1.0.1
+shortuuid==1.0.9
six==1.16.0
sniffio==1.2.0
+sqlalchemy-aio==0.17.0
sqlalchemy==1.3.23
-sqlalchemy-aio==0.16.0
-sse-starlette==0.6.2
-starlette==0.14.2
-typing-extensions==3.10.0.2
-uvicorn==0.15.0
+sse-starlette==0.10.3
+starlette==0.19.1
+typing-extensions==4.3.0
+uvicorn==0.18.2
uvloop==0.16.0
-watchgod==0.7
-websockets==10.0
-zipp==3.5.0
+watchfiles==0.16.0
+websockets==10.3
diff --git a/result b/result
new file mode 120000
index 000000000..b0acd55c0
--- /dev/null
+++ b/result
@@ -0,0 +1 @@
+/nix/store/ds9c48q7hnkdmpzy3aq14kc1x9wrrszd-python3.9-lnbits-0.1.0
\ No newline at end of file
diff --git a/tests/core/views/test_api.py b/tests/core/views/test_api.py
index 6a5f82ecb..dfd2b32ae 100644
--- a/tests/core/views/test_api.py
+++ b/tests/core/views/test_api.py
@@ -1,6 +1,7 @@
import pytest
import pytest_asyncio
from lnbits.core.crud import get_wallet
+from lnbits.core.views.api import api_payment
from ...helpers import get_random_invoice_data
@@ -155,3 +156,26 @@ async def test_decode_invoice(client, invoice):
)
assert response.status_code < 300
assert response.json()["payment_hash"] == invoice["payment_hash"]
+
+
+# check api_payment() internal function call (NOT API): payment status
+@pytest.mark.asyncio
+async def test_api_payment_without_key(invoice):
+ # check the payment status
+ response = await api_payment(invoice["payment_hash"])
+ assert type(response) == dict
+ assert response["paid"] == True
+ # no key, that's why no "details"
+ assert "details" not in response
+
+
+# check api_payment() internal function call (NOT API): payment status
+@pytest.mark.asyncio
+async def test_api_payment_with_key(invoice, inkey_headers_from):
+ # check the payment status
+ response = await api_payment(
+ invoice["payment_hash"], inkey_headers_from["X-Api-Key"]
+ )
+ assert type(response) == dict
+ assert response["paid"] == True
+ assert "details" in response