diff --git a/go.mod b/go.mod index 2b1d04ee..8c018086 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ toolchain go1.23.3 require ( github.com/adrg/xdg v0.5.3 github.com/breez/breez-sdk-go v0.5.2 - github.com/elnosh/gonuts v0.2.0 + github.com/elnosh/gonuts v0.3.1-0.20250107155549-ae74d5ac3a23 github.com/getAlby/glalby-go v0.0.0-20240621192717-95673c864d59 github.com/getAlby/ldk-node-go v0.0.0-20250106052504-d4191410486f github.com/go-gormigrate/gormigrate/v2 v2.1.3 @@ -18,7 +18,7 @@ require ( github.com/stretchr/testify v1.10.0 github.com/tyler-smith/go-bip39 v1.1.0 github.com/wailsapp/wails/v2 v2.9.2 - golang.org/x/crypto v0.30.0 + golang.org/x/crypto v0.31.0 golang.org/x/oauth2 v0.24.0 google.golang.org/grpc v1.68.0 gopkg.in/DataDog/dd-trace-go.v1 v1.69.1 @@ -68,6 +68,7 @@ require ( github.com/dustin/go-humanize v1.0.1 // indirect github.com/fergusstrange/embedded-postgres v1.29.0 // indirect github.com/frankban/quicktest v1.13.0 // indirect + github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/go-errors/errors v1.5.1 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect @@ -81,7 +82,7 @@ require ( github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt v3.2.2+incompatible // indirect - github.com/golang-jwt/jwt/v4 v4.5.0 // indirect + github.com/golang-jwt/jwt/v4 v4.5.1 // indirect github.com/golang-migrate/migrate/v4 v4.18.1 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/golang/snappy v0.0.4 // indirect @@ -145,7 +146,6 @@ require ( github.com/mattn/go-sqlite3 v1.14.22 // indirect github.com/miekg/dns v1.1.62 // indirect github.com/moby/docker-image-spec v1.3.1 // indirect - github.com/moby/sys/userns v0.1.0 // indirect github.com/moby/term v0.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect @@ -182,6 +182,7 @@ require ( github.com/valyala/fasttemplate v1.2.2 // indirect github.com/wailsapp/go-webview2 v1.0.16 // indirect github.com/wailsapp/mimetype v1.4.1 // indirect + github.com/x448/float16 v0.8.4 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonschema v1.2.0 // indirect @@ -207,7 +208,7 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect golang.org/x/mod v0.21.0 // indirect - golang.org/x/net v0.29.0 // indirect + golang.org/x/net v0.33.0 // indirect golang.org/x/sync v0.10.0 // indirect golang.org/x/sys v0.28.0 // indirect golang.org/x/term v0.27.0 // indirect diff --git a/go.sum b/go.sum index c7133946..30804845 100644 --- a/go.sum +++ b/go.sum @@ -33,8 +33,6 @@ github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0 github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= -github.com/Microsoft/hcsshim v0.11.4 h1:68vKo2VN8DE9AdN4tnkWnmdhqdbpUFM8OF3Airm7fz8= -github.com/Microsoft/hcsshim v0.11.4/go.mod h1:smjE4dvqPX9Zldna+t5FG3rnoHhaB7QYxPRqGcpAD9w= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= github.com/adrg/xdg v0.5.3 h1:xRnxJXne7+oWDatRhR1JLnvuccuIeCoBu2rtuLqQB78= @@ -113,12 +111,12 @@ github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= github.com/cockroachdb/datadriven v1.0.2 h1:H9MtNqVoVhvd9nCBwOyDjUEdZCREqbIdCJD93PBm/jA= github.com/cockroachdb/datadriven v1.0.2/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= -github.com/containerd/containerd v1.7.15 h1:afEHXdil9iAm03BmhjzKyXnnEBtjaLJefdU7DV0IFes= -github.com/containerd/containerd v1.7.15/go.mod h1:ISzRRTMF8EXNpJlTzyr2XMhN+j9K302C21/+cr3kUnY= github.com/containerd/continuity v0.4.3 h1:6HVkalIp+2u1ZLH1J/pYX2oBVXlJZvh1X1A7bEZ9Su8= github.com/containerd/continuity v0.4.3/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= +github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= +github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw= github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4= github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= @@ -164,10 +162,10 @@ github.com/eapache/queue/v2 v2.0.0-20230407133247-75960ed334e4 h1:8EXxF+tCLqaVk8 github.com/eapache/queue/v2 v2.0.0-20230407133247-75960ed334e4/go.mod h1:I5sHm0Y0T1u5YjlyqC5GVArM7aNZRUYtTjmJ8mPJFds= github.com/ebitengine/purego v0.6.0-alpha.5 h1:EYID3JOAdmQ4SNZYJHu9V6IqOeRQDBYxqKAg9PyoHFY= github.com/ebitengine/purego v0.6.0-alpha.5/go.mod h1:ah1In8AOtksoNK6yk5z1HTJeUkC1Ez4Wk2idgGslMwQ= -github.com/elnosh/btc-docker-test v0.0.0-20240730150514-6d94d76b8881 h1:iHr0CRNKU9ilxf+LGUon9XB39lRvLlbbm9C9dx2Y/u0= -github.com/elnosh/btc-docker-test v0.0.0-20240730150514-6d94d76b8881/go.mod h1:W2G5BhKocXfbC61N4Jy8Z+0rSPGAbDcZsKIr+4B5v9Y= -github.com/elnosh/gonuts v0.2.0 h1:EP0gDmsD3oUuTLoEvo21rLSWHAu1f46DN/xpivi7skc= -github.com/elnosh/gonuts v0.2.0/go.mod h1:vIrJdoCw2Qy9zHQhed6izBgHKtB8mzMoYBJPPyGkF4Q= +github.com/elnosh/btc-docker-test v0.0.0-20241223164556-146e52a0433b h1:JbZVAqKBVRkvHuZZJsf8MvO+I7HGaVNCMQvp7WMFGqs= +github.com/elnosh/btc-docker-test v0.0.0-20241223164556-146e52a0433b/go.mod h1:4PlP53czOHN+XvjyQZh+zgrzkI7BYFvJajxKK2zquyE= +github.com/elnosh/gonuts v0.3.1-0.20250107155549-ae74d5ac3a23 h1:L74EHKPpS5PDMHc/yhP24S7KaxHQBfCHudYpNXFmPI8= +github.com/elnosh/gonuts v0.3.1-0.20250107155549-ae74d5ac3a23/go.mod h1:s1lwjLf5HjrkgiMvdYPCz7HNAUjYS94C2WQcYaFbijc= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= @@ -184,8 +182,11 @@ github.com/frankban/quicktest v1.13.0 h1:yNZif1OkDfNoDfb9zZa9aXIpejNR4F23Wely0c+ github.com/frankban/quicktest v1.13.0/go.mod h1:qLE0fzW0VuyUAJgPU19zByoIr0HtCHN/r/VLSOOIySU= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= +github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= +github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= github.com/getAlby/glalby-go v0.0.0-20240621192717-95673c864d59 h1:fSqdXE9uKhLcOOQaLtzN+D8RN3oEcZQkGX5E8PyiKy0= github.com/getAlby/glalby-go v0.0.0-20240621192717-95673c864d59/go.mod h1:ViyJvjlvv0GCesTJ7mb3fBo4G+/qsujDAFN90xZ7a9U= github.com/getAlby/ldk-node-go v0.0.0-20250106052504-d4191410486f h1:L9PHEhYgD4tO66KOoTPxYo/+unZC8zTTO5fS32jKG2E= @@ -229,8 +230,8 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= -github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= -github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo= +github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang-migrate/migrate/v4 v4.18.1 h1:JML/k+t4tpHCpQTCAD62Nu43NUFzHY4CV3uAuvHGC+Y= @@ -642,8 +643,8 @@ github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDdvS342BElfbETmL1Aiz3i2t0zfRj16Hs= github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= -github.com/testcontainers/testcontainers-go v0.31.0 h1:W0VwIhcEVhRflwL9as3dhY6jXjVCA27AkmbnZ+UTh3U= -github.com/testcontainers/testcontainers-go v0.31.0/go.mod h1:D2lAoA0zUFiSY+eAflqK5mcUx/A5hrrORaEQrd0SefI= +github.com/testcontainers/testcontainers-go v0.33.0 h1:zJS9PfXYT5O0ZFXM2xxXfk4J5UMw/kRiISng037Gxdw= +github.com/testcontainers/testcontainers-go v0.33.0/go.mod h1:W80YpTa8D5C3Yy16icheD01UTDu+LmXIA2Keo+jWtT8= github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= @@ -675,6 +676,8 @@ github.com/wailsapp/mimetype v1.4.1 h1:pQN9ycO7uo4vsUUuPeHEYoUkLVkaRntMnHJxVwYhw github.com/wailsapp/mimetype v1.4.1/go.mod h1:9aV5k31bBOv5z6u+QP8TltzvNGJPmNJD4XlAL3U+j3o= github.com/wailsapp/wails/v2 v2.9.2 h1:Xb5YRTos1w5N7DTMyYegWaGukCP2fIaX9WF21kPPF2k= github.com/wailsapp/wails/v2 v2.9.2/go.mod h1:uehvlCwJSFcBq7rMCGfk4rxca67QQGsbg5Nm4m9UnBs= +github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= @@ -766,8 +769,8 @@ golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.20.0/go.mod h1:Xwo95rrVNIoSMx9wa1JroENMToLWn3RNVrTBpLHgZPQ= -golang.org/x/crypto v0.30.0 h1:RwoQn3GkWiMkzlX562cLB7OxWvjH1L8xutO2WoJcRoY= -golang.org/x/crypto v0.30.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk= golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY= @@ -812,8 +815,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= -golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= -golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= +golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= +golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE= diff --git a/lnclient/cashu/cashu.go b/lnclient/cashu/cashu.go index 3b510746..96e63518 100644 --- a/lnclient/cashu/cashu.go +++ b/lnclient/cashu/cashu.go @@ -9,8 +9,10 @@ import ( "sort" "time" + "github.com/elnosh/gonuts/cashu/nuts/nut04" "github.com/elnosh/gonuts/wallet" "github.com/elnosh/gonuts/wallet/storage" + "github.com/getAlby/hub/constants" "github.com/getAlby/hub/lnclient" "github.com/getAlby/hub/logger" decodepay "github.com/nbd-wtf/ln-decodepay" @@ -31,9 +33,6 @@ func NewCashuService(workDir string, mintUrl string) (result lnclient.LNClient, //create dir if not exists newpath := filepath.Join(workDir) - _, err = os.Stat(newpath) - isFirstSetup := err != nil && errors.Is(err, os.ErrNotExist) - err = os.MkdirAll(newpath, os.ModePerm) if err != nil { log.Printf("Failed to create cashu working dir: %v", err) @@ -53,22 +52,6 @@ func NewCashuService(workDir string, mintUrl string) (result lnclient.LNClient, wallet: wallet, } - // try to make an invoice to ensure the mint is running - // TODO: remove once LoadWallet is improved - see https://github.com/elnosh/gonuts/issues/49 - _, err = cs.MakeInvoice(context.Background(), 10000, "", "", 0) - if err != nil { - logger.Logger.WithError(err).Error("Failed to load cashu wallet") - if isFirstSetup { - // delete wallet to ensure user gets a fresh start when they try a different mint - // otherwise the wallet freezes on next startup - fileErr := os.RemoveAll(newpath) - if fileErr != nil { - logger.Logger.WithError(fileErr).Error("Failed to remove broken wallet directory") - } - } - return nil, err - } - return &cs, nil } @@ -82,30 +65,24 @@ func (cs *CashuService) SendPaymentSync(ctx context.Context, invoice string, amo return nil, errors.New("0-amount invoices not supported") } - meltResponse, err := cs.wallet.Melt(invoice, cs.wallet.CurrentMint()) + meltQuoteResponse, err := cs.wallet.RequestMeltQuote(invoice, cs.wallet.CurrentMint()) if err != nil { - logger.Logger.WithError(err).Error("Failed to melt invoice") + logger.Logger.WithError(err).Error("Failed to request melt quote") return nil, err } - if meltResponse == nil || meltResponse.Preimage == "" { - return nil, errors.New("no preimage in melt response") - } - - // TODO: get fee from melt response - cashuInvoice, err := cs.wallet.GetInvoiceByPaymentRequest(invoice) + balanceBeforeMelt := cs.wallet.GetBalance() + meltResponse, err := cs.wallet.Melt(meltQuoteResponse.Quote) if err != nil { - logger.Logger.WithField("invoice", invoice).WithError(err).Error("Failed to get invoice after melting") + logger.Logger.WithError(err).Error("Failed to melt invoice") return nil, err } - transaction, err := cs.cashuInvoiceToTransaction(cashuInvoice) - if err != nil { - logger.Logger.WithField("invoice", invoice).WithError(err).Error("Failed to convert invoice to transaction") - return nil, err + if meltResponse == nil || meltResponse.Preimage == "" { + return nil, errors.New("no preimage in melt response") } - - fee := uint64(transaction.FeesPaid) + totalPaid := balanceBeforeMelt - cs.wallet.GetBalance() + fee := totalPaid - meltQuoteResponse.Amount return &lnclient.PayInvoiceResponse{ Preimage: meltResponse.Preimage, @@ -122,54 +99,48 @@ func (cs *CashuService) MakeInvoice(ctx context.Context, amount int64, descripti if expiry == 0 { expiry = lnclient.DEFAULT_INVOICE_EXPIRY } - mintResponse, err := cs.wallet.RequestMint(uint64(amount / 1000)) + mintResponse, err := cs.wallet.RequestMint(uint64(amount/1000), cs.wallet.CurrentMint()) if err != nil { logger.Logger.WithError(err).Error("Failed to mint") return nil, err } - paymentRequest, err := decodepay.Decodepay(mintResponse.Request) - if err != nil { - logger.Logger.WithFields(logrus.Fields{ - "invoice": mintResponse.Request, - }).WithError(err).Error("Failed to decode bolt11 invoice") - return nil, err - } - - return cs.LookupInvoice(ctx, paymentRequest.PaymentHash) + mintQuote := cs.wallet.GetMintQuoteById(mintResponse.Quote) + return cs.cashuMintQuoteToTransaction(mintQuote), nil } func (cs *CashuService) LookupInvoice(ctx context.Context, paymentHash string) (transaction *lnclient.Transaction, err error) { - cashuInvoice := cs.wallet.GetInvoiceByPaymentHash(paymentHash) - - if cashuInvoice == nil { + mintQuote := cs.getMintQuoteByPaymentHash(paymentHash) + if mintQuote == nil { logger.Logger.WithField("paymentHash", paymentHash).Error("Failed to lookup payment request by payment hash") return nil, errors.New("failed to lookup payment request by payment hash") } - cs.checkInvoice(cashuInvoice) - - transaction, err = cs.cashuInvoiceToTransaction(cashuInvoice) - + cs.checkInvoice(mintQuote) + transaction = cs.cashuMintQuoteToTransaction(mintQuote) return transaction, nil } func (cs *CashuService) ListTransactions(ctx context.Context, from, until, limit, offset uint64, unpaid bool, invoiceType string) (transactions []lnclient.Transaction, err error) { - transactions = []lnclient.Transaction{} - - invoices := cs.wallet.GetAllInvoices() - - for _, invoice := range invoices { - invoiceCreated := time.UnixMilli(invoice.CreatedAt * 1000) - - if time.Since(invoiceCreated) < 24*time.Hour { - cs.checkInvoice(&invoice) + mintQuotes := cs.wallet.GetMintQuotes() + meltQuotes := cs.wallet.GetMeltQuotes() + transactions = make([]lnclient.Transaction, 0, len(mintQuotes)+len(meltQuotes)) + + for _, mintQuote := range mintQuotes { + invoiceCreated := time.UnixMilli(mintQuote.CreatedAt * 1000) + if time.Since(invoiceCreated) < 24*time.Hour && mintQuote.State != nut04.Paid { + cs.checkInvoice(&mintQuote) } - transaction, err := cs.cashuInvoiceToTransaction(&invoice) - if err != nil { + transaction := cs.cashuMintQuoteToTransaction(&mintQuote) + if transaction.SettledAt == nil { continue } + transactions = append(transactions, *transaction) + } + + for _, meltQuote := range meltQuotes { + transaction := cs.cashuMeltQuoteToTransaction(&meltQuote) if transaction.SettledAt == nil { continue } @@ -274,14 +245,8 @@ func (cs *CashuService) UpdateChannel(ctx context.Context, updateChannelRequest } func (cs *CashuService) GetBalances(ctx context.Context) (*lnclient.BalancesResponse, error) { - balanceByMints := cs.wallet.GetBalanceByMints() - totalBalance := uint64(0) - - for _, balance := range balanceByMints { - totalBalance += balance - } - - balance := int64(totalBalance * 1000) + cashuBalance := cs.wallet.GetBalance() + balance := int64(cashuBalance * 1000) return &lnclient.BalancesResponse{ Onchain: lnclient.OnchainBalanceResponse{ @@ -299,18 +264,12 @@ func (cs *CashuService) GetBalances(ctx context.Context) (*lnclient.BalancesResp }, nil } -func (cs *CashuService) cashuInvoiceToTransaction(cashuInvoice *storage.Invoice) (*lnclient.Transaction, error) { - paymentRequest, err := decodepay.Decodepay(cashuInvoice.PaymentRequest) - if err != nil { - logger.Logger.WithFields(logrus.Fields{ - "invoice": cashuInvoice.PaymentRequest, - }).WithError(err).Error("Failed to decode bolt11 invoice") - return nil, err - } - +func (cs *CashuService) cashuMintQuoteToTransaction(mintQuote *storage.MintQuote) *lnclient.Transaction { + // note: if a mint quote exists, then the payment request is already valid + paymentRequest, _ := decodepay.Decodepay(mintQuote.PaymentRequest) var settledAt *int64 - if cashuInvoice.SettledAt > 0 { - settledAt = &cashuInvoice.SettledAt + if mintQuote.SettledAt > 0 { + settledAt = &mintQuote.SettledAt } var expiresAt *int64 @@ -320,44 +279,94 @@ func (cs *CashuService) cashuInvoiceToTransaction(cashuInvoice *storage.Invoice) description := paymentRequest.Description descriptionHash := paymentRequest.DescriptionHash - invoiceType := "outgoing" - if cashuInvoice.TransactionType == storage.Mint { - invoiceType = "incoming" + return &lnclient.Transaction{ + Type: constants.TRANSACTION_TYPE_INCOMING, + Invoice: mintQuote.PaymentRequest, + PaymentHash: paymentRequest.PaymentHash, + Amount: paymentRequest.MSatoshi, + CreatedAt: int64(paymentRequest.CreatedAt), + ExpiresAt: expiresAt, + Description: description, + DescriptionHash: descriptionHash, + SettledAt: settledAt, + } +} + +func (cs *CashuService) cashuMeltQuoteToTransaction(meltQuote *storage.MeltQuote) *lnclient.Transaction { + // note: if a melt quote exists, then the payment request is already valid + paymentRequest, _ := decodepay.Decodepay(meltQuote.PaymentRequest) + + var settledAt *int64 + if meltQuote.SettledAt > 0 { + settledAt = &meltQuote.SettledAt } + var expiresAt *int64 + + expiresAtUnix := time.UnixMilli(int64(paymentRequest.CreatedAt) * 1000).Add(time.Duration(paymentRequest.Expiry) * time.Second).Unix() + expiresAt = &expiresAtUnix + description := paymentRequest.Description + descriptionHash := paymentRequest.DescriptionHash + return &lnclient.Transaction{ - Type: invoiceType, - Invoice: cashuInvoice.PaymentRequest, + Type: constants.TRANSACTION_TYPE_OUTGOING, + Invoice: meltQuote.PaymentRequest, PaymentHash: paymentRequest.PaymentHash, Amount: paymentRequest.MSatoshi, CreatedAt: int64(paymentRequest.CreatedAt), ExpiresAt: expiresAt, Description: description, DescriptionHash: descriptionHash, - Preimage: cashuInvoice.Preimage, + Preimage: meltQuote.Preimage, SettledAt: settledAt, - FeesPaid: int64(cashuInvoice.QuoteAmount*1000) - paymentRequest.MSatoshi, - }, nil + FeesPaid: int64(meltQuote.FeeReserve), + } } -func (cs *CashuService) checkInvoice(cashuInvoice *storage.Invoice) { - if cashuInvoice.TransactionType == storage.Mint && !cashuInvoice.Paid { +func (cs *CashuService) getMintQuoteByPaymentHash(paymentHash string) *storage.MintQuote { + mintQuotes := cs.wallet.GetMintQuotes() + + for _, mintQuote := range mintQuotes { + bolt11, err := decodepay.Decodepay(mintQuote.PaymentRequest) + if err != nil { + return nil + } + if bolt11.PaymentHash == paymentHash { + return &mintQuote + } + } + + return nil +} + +func (cs *CashuService) checkInvoice(mintQuote *storage.MintQuote) { + bolt11, _ := decodepay.Decodepay(mintQuote.PaymentRequest) + + if mintQuote.State != nut04.Paid { logger.Logger.WithFields(logrus.Fields{ - "paymentHash": cashuInvoice.PaymentHash, + "paymentHash": bolt11.PaymentHash, }).Debug("Checking unpaid invoice") - proofs, err := cs.wallet.MintTokens(cashuInvoice.Id) + mintQuoteState, err := cs.wallet.MintQuoteState(mintQuote.QuoteId) if err != nil { logger.Logger.WithFields(logrus.Fields{ - "paymentHash": cashuInvoice.PaymentHash, - }).WithError(err).Warn("failed to mint") + "paymentHash": bolt11.PaymentHash, + }).WithError(err).Warn("failed to check invoice state") } - if proofs != nil { - logger.Logger.WithFields(logrus.Fields{ - "paymentHash": cashuInvoice.PaymentHash, - "amount": proofs.Amount(), - }).Info("sats successfully minted") + if mintQuoteState.State == nut04.Paid { + amountMinted, err := cs.wallet.MintTokens(mintQuote.QuoteId) + if err != nil { + logger.Logger.WithFields(logrus.Fields{ + "paymentHash": bolt11.PaymentHash, + }).WithError(err).Warn("failed to mint") + } + if amountMinted > 0 { + logger.Logger.WithFields(logrus.Fields{ + "paymentHash": bolt11.PaymentHash, + "amount": amountMinted, + }).Info("sats successfully minted") + } } } }