package handler import ( "database/sql" "fmt" "net/http" "strconv" "git.ekzyis.com/ekzyis/delphi.market/db" "git.ekzyis.com/ekzyis/delphi.market/env" "git.ekzyis.com/ekzyis/delphi.market/lib" "git.ekzyis.com/ekzyis/delphi.market/server/router/context" "github.com/labstack/echo/v4" "github.com/lightningnetwork/lnd/lntypes" ) func HandleMarket(sc context.ServerContext) echo.HandlerFunc { return func(c echo.Context) error { var ( marketId int64 market db.Market shares []db.Share orders []db.Order err error data map[string]any ) if marketId, err = strconv.ParseInt(c.Param("id"), 10, 64); err != nil { return echo.NewHTTPError(http.StatusBadRequest, "Bad Request") } if err = sc.Db.FetchMarket(int(marketId), &market); err == sql.ErrNoRows { return echo.NewHTTPError(http.StatusNotFound, "Not Found") } else if err != nil { return err } if err = sc.Db.FetchShares(market.Id, &shares); err != nil { return err } if err = sc.Db.FetchOrders(&db.FetchOrdersWhere{MarketId: market.Id, Confirmed: true}, &orders); err != nil { return err } data = map[string]any{ "session": c.Get("session"), "Id": market.Id, "Description": market.Description, // shares are sorted by description in descending order // that's how we know that YES must be the first share "YesShare": shares[0], "NoShare": shares[1], "Orders": orders, } return sc.Render(c, http.StatusOK, "market.html", data) } } func HandlePostOrder(sc context.ServerContext) echo.HandlerFunc { return func(c echo.Context) error { var ( marketId string u db.User o db.Order invoice *db.Invoice msats int64 data map[string]any qr string hash lntypes.Hash err error ) marketId = c.Param("id") // TODO: // [ ] Step 0: If SELL order, check share balance of user // [x] Create HODL invoice // [x] Create (unconfirmed) order // [ ] Find matching orders // [ ] Settle invoice when matching order was found, // else cancel invoice if expired // parse body if err := c.Bind(&o); err != nil { return echo.NewHTTPError(http.StatusBadRequest) } u = c.Get("session").(db.User) o.Pubkey = u.Pubkey msats = o.Quantity * o.Price * 1000 // TODO: if SELL order, check share balance of user // Create HODL invoice if invoice, err = sc.Lnd.CreateInvoice(sc.Db, o.Pubkey, msats); err != nil { return err } // Create QR code to pay HODL invoice if qr, err = lib.ToQR(invoice.PaymentRequest); err != nil { return err } if hash, err = lntypes.MakeHashFromStr(invoice.Hash); err != nil { return err } // Start goroutine to poll status and update invoice in background go sc.Lnd.CheckInvoice(sc.Db, hash) // Create (unconfirmed) order o.InvoiceId = invoice.Id if err := sc.Db.CreateOrder(&o); err != nil { return err } // TODO: find matching orders data = map[string]any{ "session": c.Get("session"), "lnurl": invoice.PaymentRequest, "qr": qr, "invoice": *invoice, "redirectURL": fmt.Sprintf("https://%s/market/%s", env.PublicURL, marketId), } return sc.Render(c, http.StatusPaymentRequired, "invoice.html", data) } }