From 4b0d017b98cc7c032bf76a24133d404fc0739b03 Mon Sep 17 00:00:00 2001 From: ekzyis Date: Thu, 9 Nov 2023 03:47:24 +0100 Subject: [PATCH] Add invoice description --- db/init.sql | 3 ++- db/invoice.go | 10 +++++----- db/market.go | 4 ++++ db/types.go | 1 + lnd/invoice.go | 3 ++- server/router/handler/invoice.go | 1 + server/router/handler/market.go | 26 +++++++++++++++++--------- vue/src/components/Invoice.vue | 27 +++++++++++++++++++++++++-- vue/src/index.css | 2 +- 9 files changed, 58 insertions(+), 19 deletions(-) diff --git a/db/init.sql b/db/init.sql index 033d0ab..9072d29 100644 --- a/db/init.sql +++ b/db/init.sql @@ -35,7 +35,8 @@ CREATE TABLE invoices( created_at TIMESTAMP WITH TIME ZONE NOT NULL, expires_at TIMESTAMP WITH TIME ZONE NOT NULL, confirmed_at TIMESTAMP WITH TIME ZONE, - held_since TIMESTAMP WITH TIME ZONE + held_since TIMESTAMP WITH TIME ZONE, + description TEXT ); CREATE TYPE order_side AS ENUM ('BUY', 'SELL'); CREATE TABLE orders( diff --git a/db/invoice.go b/db/invoice.go index c4b5de3..94b6512 100644 --- a/db/invoice.go +++ b/db/invoice.go @@ -4,10 +4,10 @@ import "time" func (db *DB) CreateInvoice(invoice *Invoice) error { if err := db.QueryRow(""+ - "INSERT INTO invoices(pubkey, msats, preimage, hash, bolt11, created_at, expires_at) "+ - "VALUES($1, $2, $3, $4, $5, $6, $7) "+ + "INSERT INTO invoices(pubkey, msats, preimage, hash, bolt11, created_at, expires_at, description) "+ + "VALUES($1, $2, $3, $4, $5, $6, $7, $8) "+ "RETURNING id", - invoice.Pubkey, invoice.Msats, invoice.Preimage, invoice.Hash, invoice.PaymentRequest, invoice.CreatedAt, invoice.ExpiresAt).Scan(&invoice.Id); err != nil { + invoice.Pubkey, invoice.Msats, invoice.Preimage, invoice.Hash, invoice.PaymentRequest, invoice.CreatedAt, invoice.ExpiresAt, invoice.Description).Scan(&invoice.Id); err != nil { return err } return nil @@ -20,7 +20,7 @@ type FetchInvoiceWhere struct { func (db *DB) FetchInvoice(where *FetchInvoiceWhere, invoice *Invoice) error { var ( - query = "SELECT id, pubkey, msats, preimage, hash, bolt11, created_at, expires_at, confirmed_at, held_since FROM invoices " + query = "SELECT id, pubkey, msats, preimage, hash, bolt11, created_at, expires_at, confirmed_at, held_since, COALESCE(description, '') FROM invoices " args []any ) if where.Id != "" { @@ -32,7 +32,7 @@ func (db *DB) FetchInvoice(where *FetchInvoiceWhere, invoice *Invoice) error { } if err := db.QueryRow(query, args...).Scan( &invoice.Id, &invoice.Pubkey, &invoice.Msats, &invoice.Preimage, &invoice.Hash, - &invoice.PaymentRequest, &invoice.CreatedAt, &invoice.ExpiresAt, &invoice.ConfirmedAt, &invoice.HeldSince); err != nil { + &invoice.PaymentRequest, &invoice.CreatedAt, &invoice.ExpiresAt, &invoice.ConfirmedAt, &invoice.HeldSince, &invoice.Description); err != nil { return err } return nil diff --git a/db/market.go b/db/market.go index c182328..2e4e23c 100644 --- a/db/market.go +++ b/db/market.go @@ -46,6 +46,10 @@ func (db *DB) FetchShares(marketId int, shares *[]Share) error { return nil } +func (db *DB) FetchShare(shareId string, share *Share) error { + return db.QueryRow("SELECT id, market_id, description FROM shares WHERE id = $1", shareId).Scan(&share.Id, &share.MarketId, &share.Description) +} + func (db *DB) FetchOrders(where *FetchOrdersWhere, orders *[]Order) error { query := "" + "SELECT o.id, share_id, o.pubkey, o.side, o.quantity, o.price, o.invoice_id, o.created_at, s.description, s.market_id, i.confirmed_at " + diff --git a/db/types.go b/db/types.go index be7a625..89e380c 100644 --- a/db/types.go +++ b/db/types.go @@ -45,6 +45,7 @@ type ( ExpiresAt time.Time ConfirmedAt null.Time HeldSince null.Time + Description string } Order struct { Id UUID diff --git a/lnd/invoice.go b/lnd/invoice.go index 7ae861f..7e5d9b1 100644 --- a/lnd/invoice.go +++ b/lnd/invoice.go @@ -12,7 +12,7 @@ import ( "github.com/lightningnetwork/lnd/lnwire" ) -func (lnd *LNDClient) CreateInvoice(d *db.DB, pubkey string, msats int64) (*db.Invoice, error) { +func (lnd *LNDClient) CreateInvoice(d *db.DB, pubkey string, msats int64, description string) (*db.Invoice, error) { var ( expiry time.Duration = time.Hour preimage lntypes.Preimage @@ -44,6 +44,7 @@ func (lnd *LNDClient) CreateInvoice(d *db.DB, pubkey string, msats int64) (*db.I Hash: hash.String(), CreatedAt: lnInvoice.CreationDate, ExpiresAt: lnInvoice.CreationDate.Add(expiry), + Description: description, } if err := d.CreateInvoice(dbInvoice); err != nil { return nil, err diff --git a/server/router/handler/invoice.go b/server/router/handler/invoice.go index acc8ffd..45e888d 100644 --- a/server/router/handler/invoice.go +++ b/server/router/handler/invoice.go @@ -44,6 +44,7 @@ func HandleInvoiceStatus(sc context.ServerContext) echo.HandlerFunc { "ExpiresAt": invoice.ExpiresAt, "ConfirmedAt": invoice.ConfirmedAt, "HeldSince": invoice.HeldSince, + "Description": invoice.Description, "Qr": qr, } return c.JSON(http.StatusOK, data) diff --git a/server/router/handler/market.go b/server/router/handler/market.go index d20fd8e..419628f 100644 --- a/server/router/handler/market.go +++ b/server/router/handler/market.go @@ -2,8 +2,10 @@ package handler import ( "database/sql" + "fmt" "net/http" "strconv" + "strings" "git.ekzyis.com/ekzyis/delphi.market/db" "git.ekzyis.com/ekzyis/delphi.market/lib" @@ -48,14 +50,16 @@ func HandleMarket(sc context.ServerContext) echo.HandlerFunc { func HandleOrder(sc context.ServerContext) echo.HandlerFunc { return func(c echo.Context) error { var ( - u db.User - o db.Order - invoice *db.Invoice - msats int64 - data map[string]any - qr string - hash lntypes.Hash - err error + u db.User + o db.Order + s db.Share + invoice *db.Invoice + msats int64 + description string + data map[string]any + qr string + hash lntypes.Hash + err error ) // TODO: // [ ] Step 0: If SELL order, check share balance of user @@ -72,11 +76,15 @@ func HandleOrder(sc context.ServerContext) echo.HandlerFunc { u = c.Get("session").(db.User) o.Pubkey = u.Pubkey msats = o.Quantity * o.Price * 1000 + if err = sc.Db.FetchShare(o.ShareId, &s); err != nil { + return err + } + description = fmt.Sprintf("%s %d %s shares @ %d [market:%d]", strings.ToUpper(o.Side), o.Quantity, s.Description, o.Price, s.MarketId) // 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 { + if invoice, err = sc.Lnd.CreateInvoice(sc.Db, o.Pubkey, msats, description); err != nil { return err } // Create QR code to pay HODL invoice diff --git a/vue/src/components/Invoice.vue b/vue/src/components/Invoice.vue index 8dd485d..74245ea 100644 --- a/vue/src/components/Invoice.vue +++ b/vue/src/components/Invoice.vue @@ -31,8 +31,22 @@ expires at {{ invoice.ExpiresAt }} - msats - {{ invoice.Msats }} + sats + {{ invoice.Msats / 1000 }} + + description + + + + {{ invoice.Description }} + [market] + + <empty> + + + {{ invoice.Description }} + <empty> + @@ -83,6 +97,15 @@ await (async () => { const url = window.origin + '/api/invoice/' + route.params.id const res = await fetch(url) const body = await res.json() + if (body.Description) { + const regexp = /\[market:(?[0-9]+)\]/ + const m = body.Description.match(regexp) + const marketId = m.groups?.id + if (marketId) { + body.DescriptionMarketId = marketId + body.Description = body.Description.replace(regexp, '') + } + } invoice.value = body })() diff --git a/vue/src/index.css b/vue/src/index.css index 2ef490e..233907b 100644 --- a/vue/src/index.css +++ b/vue/src/index.css @@ -62,5 +62,5 @@ a.selected { } .text-muted { - opacity: 0.5 + opacity: 0.67 } \ No newline at end of file