From 8b0e56f215ba85d423b536ea3279cb992f8309c3 Mon Sep 17 00:00:00 2001 From: boufni95 Date: Tue, 16 Sep 2025 18:16:35 +0000 Subject: [PATCH 1/7] clink exp and desc --- package-lock.json | 8 ++++---- package.json | 2 +- proto/autogenerated/client.md | 1 + proto/autogenerated/go/types.go | 1 + proto/autogenerated/ts/types.ts | 9 +++++++-- proto/service/structs.proto | 2 +- src/services/main/applicationManager.ts | 3 ++- src/services/main/offerManager.ts | 7 ++++++- 8 files changed, 23 insertions(+), 10 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8105f829..4435ce90 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ "@protobuf-ts/grpc-transport": "^2.9.4", "@protobuf-ts/plugin": "^2.5.0", "@protobuf-ts/runtime": "^2.5.0", - "@shocknet/clink-sdk": "^1.3.1", + "@shocknet/clink-sdk": "^1.4.0", "@stablelib/xchacha20": "^1.0.1", "@types/express": "^4.17.21", "@types/node": "^17.0.31", @@ -686,9 +686,9 @@ } }, "node_modules/@shocknet/clink-sdk": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@shocknet/clink-sdk/-/clink-sdk-1.3.1.tgz", - "integrity": "sha512-jQtiB6jHN8ApuyTjmf6xB1b+53O2tSkAUsIQtctEopJL3xq4xLgmIKce+tTefr6BYQJtzSd2lAiumiT4KLxSrw==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@shocknet/clink-sdk/-/clink-sdk-1.4.0.tgz", + "integrity": "sha512-J0PWE8CVRJrFF1Zi/UhChhvOrlmDj7LRJTpR6rbHlFPmjC5TGIW6891tVWWv+JmUR0jzez9QHFrHnc8DgIJYCQ==", "license": "MIT", "dependencies": { "@noble/hashes": "^1.8.0", diff --git a/package.json b/package.json index f09b5c61..a71062ba 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "@protobuf-ts/grpc-transport": "^2.9.4", "@protobuf-ts/plugin": "^2.5.0", "@protobuf-ts/runtime": "^2.5.0", - "@shocknet/clink-sdk": "^1.3.1", + "@shocknet/clink-sdk": "^1.4.0", "@stablelib/xchacha20": "^1.0.1", "@types/express": "^4.17.21", "@types/node": "^17.0.31", diff --git a/proto/autogenerated/client.md b/proto/autogenerated/client.md index 785c7108..761f65fc 100644 --- a/proto/autogenerated/client.md +++ b/proto/autogenerated/client.md @@ -1393,6 +1393,7 @@ The nostr server will send back a message response, and inside the body there wi ### NewInvoiceRequest - __amountSats__: _number_ + - __expiry__: _number_ *this field is optional - __memo__: _string_ - __zap__: _string_ *this field is optional diff --git a/proto/autogenerated/go/types.go b/proto/autogenerated/go/types.go index 5f050e18..16d12d70 100644 --- a/proto/autogenerated/go/types.go +++ b/proto/autogenerated/go/types.go @@ -466,6 +466,7 @@ type NewAddressResponse struct { } type NewInvoiceRequest struct { Amountsats int64 `json:"amountSats"` + Expiry int64 `json:"expiry"` Memo string `json:"memo"` Zap string `json:"zap"` } diff --git a/proto/autogenerated/ts/types.ts b/proto/autogenerated/ts/types.ts index e25f6225..dbd626de 100644 --- a/proto/autogenerated/ts/types.ts +++ b/proto/autogenerated/ts/types.ts @@ -2743,14 +2743,16 @@ export const NewAddressResponseValidate = (o?: NewAddressResponse, opts: NewAddr export type NewInvoiceRequest = { amountSats: number + expiry?: number memo: string zap?: string } -export type NewInvoiceRequestOptionalField = 'zap' -export const NewInvoiceRequestOptionalFields: NewInvoiceRequestOptionalField[] = ['zap'] +export type NewInvoiceRequestOptionalField = 'expiry' | 'zap' +export const NewInvoiceRequestOptionalFields: NewInvoiceRequestOptionalField[] = ['expiry', 'zap'] export type NewInvoiceRequestOptions = OptionsBaseMessage & { checkOptionalsAreSet?: NewInvoiceRequestOptionalField[] amountSats_CustomCheck?: (v: number) => boolean + expiry_CustomCheck?: (v?: number) => boolean memo_CustomCheck?: (v: string) => boolean zap_CustomCheck?: (v?: string) => boolean } @@ -2761,6 +2763,9 @@ export const NewInvoiceRequestValidate = (o?: NewInvoiceRequest, opts: NewInvoic if (typeof o.amountSats !== 'number') return new Error(`${path}.amountSats: is not a number`) if (opts.amountSats_CustomCheck && !opts.amountSats_CustomCheck(o.amountSats)) return new Error(`${path}.amountSats: custom check failed`) + if ((o.expiry || opts.allOptionalsAreSet || opts.checkOptionalsAreSet?.includes('expiry')) && typeof o.expiry !== 'number') return new Error(`${path}.expiry: is not a number`) + if (opts.expiry_CustomCheck && !opts.expiry_CustomCheck(o.expiry)) return new Error(`${path}.expiry: custom check failed`) + if (typeof o.memo !== 'string') return new Error(`${path}.memo: is not a string`) if (opts.memo_CustomCheck && !opts.memo_CustomCheck(o.memo)) return new Error(`${path}.memo: custom check failed`) diff --git a/proto/service/structs.proto b/proto/service/structs.proto index 15cd49ba..49b01868 100644 --- a/proto/service/structs.proto +++ b/proto/service/structs.proto @@ -379,7 +379,6 @@ message AddAppUserInvoiceRequest { optional string offer_string = 6; optional bool rejectUnauthorized = 7; optional string token = 8; - } message GetAppUserRequest { @@ -450,6 +449,7 @@ message NewInvoiceRequest{ int64 amountSats = 1; string memo = 2; optional string zap = 3; + optional int64 expiry = 4; } message NewInvoiceResponse{ diff --git a/src/services/main/applicationManager.ts b/src/services/main/applicationManager.ts index 5bf3c5fc..3aa33f52 100644 --- a/src/services/main/applicationManager.ts +++ b/src/services/main/applicationManager.ts @@ -195,8 +195,9 @@ export default class { if (req.invoice_req.zap) { zapInfo = this.paymentManager.validateZapEvent(req.invoice_req.zap, req.invoice_req.amountSats) } + const expiry = req.invoice_req.expiry ? Math.min(req.invoice_req.expiry, defaultInvoiceExpiry) : defaultInvoiceExpiry const opts: InboundOptionals = { - callbackUrl: cbUrl, expiry: defaultInvoiceExpiry, expectedPayer: payer.user, linkedApplication: app, zapInfo, + callbackUrl: cbUrl, expiry: expiry, expectedPayer: payer.user, linkedApplication: app, zapInfo, offerId: req.offer_string, payerData: req.payer_data?.data, rejectUnauthorized: req.rejectUnauthorized, token: req.token } diff --git a/src/services/main/offerManager.ts b/src/services/main/offerManager.ts index 65b44e93..e7325de7 100644 --- a/src/services/main/offerManager.ts +++ b/src/services/main/offerManager.ts @@ -234,9 +234,14 @@ export class OfferManager { this.logger("Invalid expected data", validated || {}) return { success: false, code: 1, max: remote } } + if (offerReq.description && (typeof offerReq.description !== 'string' || offerReq.description.length > 100)) { + return { success: false, code: 1, max: remote } + } + const memo = offerReq.description || userOffer.label + const expiry = offerReq.expires_in_seconds ? Date.now()/1000 + offerReq.expires_in_seconds : undefined const res = await this.applicationManager.AddAppUserInvoice(appId, { http_callback_url: userOffer.callback_url, payer_identifier: userOffer.app_user_id, receiver_identifier: userOffer.app_user_id, - invoice_req: { amountSats: amt, memo: userOffer.label, zap: offerReq.zap }, + invoice_req: { amountSats: amt, memo, zap: offerReq.zap, expiry }, payer_data: validated ? { data: validated } : undefined, offer_string: offer, rejectUnauthorized: userOffer.rejectUnauthorized, From 1082465db5adb1798de1c28aee8aced76312406b Mon Sep 17 00:00:00 2001 From: boufni95 Date: Tue, 16 Sep 2025 19:39:41 +0000 Subject: [PATCH 2/7] fix default offer --- src/services/main/offerManager.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/services/main/offerManager.ts b/src/services/main/offerManager.ts index e7325de7..ccfb9dc3 100644 --- a/src/services/main/offerManager.ts +++ b/src/services/main/offerManager.ts @@ -195,14 +195,14 @@ export class OfferManager { return } - async HandleDefaultUserOffer(offerReq: NofferData, appId: string, remote: number): Promise<{ success: true, invoice: string } | { success: false, code: number, max: number }> { + async HandleDefaultUserOffer(offerReq: NofferData, appId: string, remote: number, { memo, expiry }: { memo?: string, expiry?: number }): Promise<{ success: true, invoice: string } | { success: false, code: number, max: number }> { const { amount_sats: amount, offer } = offerReq if (!amount || isNaN(amount) || amount < 10 || amount > remote) { return { success: false, code: 5, max: remote } } const res = await this.applicationManager.AddAppUserInvoice(appId, { http_callback_url: "", payer_identifier: offer, receiver_identifier: offer, - invoice_req: { amountSats: amount, memo: "Default NIP-69 Offer", zap: offerReq.zap }, + invoice_req: { amountSats: amount, memo: memo ||"Default NIP-69 Offer", zap: offerReq.zap, expiry }, offer_string: 'offer' }) return { success: true, invoice: res.invoice } @@ -211,8 +211,10 @@ export class OfferManager { async HandleUserOffer(offerReq: NofferData, appId: string, remote: number): Promise<{ success: true, invoice: string } | { success: false, code: number, max: number }> { const { amount_sats: amount, offer } = offerReq const userOffer = await this.storage.offerStorage.GetOffer(offer) + const expiry = offerReq.expires_in_seconds ? Date.now()/1000 + offerReq.expires_in_seconds : undefined + if (!userOffer) { - return this.HandleDefaultUserOffer(offerReq, appId, remote) + return this.HandleDefaultUserOffer(offerReq, appId, remote, { memo: offerReq.description, expiry }) } if (userOffer.app_user_id === userOffer.offer_id) { if (userOffer.price_sats !== 0 || userOffer.payer_data) { @@ -238,7 +240,6 @@ export class OfferManager { return { success: false, code: 1, max: remote } } const memo = offerReq.description || userOffer.label - const expiry = offerReq.expires_in_seconds ? Date.now()/1000 + offerReq.expires_in_seconds : undefined const res = await this.applicationManager.AddAppUserInvoice(appId, { http_callback_url: userOffer.callback_url, payer_identifier: userOffer.app_user_id, receiver_identifier: userOffer.app_user_id, invoice_req: { amountSats: amt, memo, zap: offerReq.zap, expiry }, From 4ee66a7778643473316a726dd0730239ff954fed Mon Sep 17 00:00:00 2001 From: boufni95 Date: Tue, 16 Sep 2025 19:42:27 +0000 Subject: [PATCH 3/7] fix expiry --- src/services/main/offerManager.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/main/offerManager.ts b/src/services/main/offerManager.ts index ccfb9dc3..e8dd21b2 100644 --- a/src/services/main/offerManager.ts +++ b/src/services/main/offerManager.ts @@ -211,7 +211,7 @@ export class OfferManager { async HandleUserOffer(offerReq: NofferData, appId: string, remote: number): Promise<{ success: true, invoice: string } | { success: false, code: number, max: number }> { const { amount_sats: amount, offer } = offerReq const userOffer = await this.storage.offerStorage.GetOffer(offer) - const expiry = offerReq.expires_in_seconds ? Date.now()/1000 + offerReq.expires_in_seconds : undefined + const expiry = offerReq.expires_in_seconds ? offerReq.expires_in_seconds : undefined if (!userOffer) { return this.HandleDefaultUserOffer(offerReq, appId, remote, { memo: offerReq.description, expiry }) From 83ff54b775b25728815995a1bf566f105ab253db Mon Sep 17 00:00:00 2001 From: boufni95 Date: Tue, 16 Sep 2025 19:44:45 +0000 Subject: [PATCH 4/7] deb --- src/services/main/applicationManager.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/services/main/applicationManager.ts b/src/services/main/applicationManager.ts index 3aa33f52..035e547a 100644 --- a/src/services/main/applicationManager.ts +++ b/src/services/main/applicationManager.ts @@ -195,6 +195,8 @@ export default class { if (req.invoice_req.zap) { zapInfo = this.paymentManager.validateZapEvent(req.invoice_req.zap, req.invoice_req.amountSats) } + console.log("req.invoice_req.expiry", req.invoice_req.expiry) + console.log("defaultInvoiceExpiry", defaultInvoiceExpiry) const expiry = req.invoice_req.expiry ? Math.min(req.invoice_req.expiry, defaultInvoiceExpiry) : defaultInvoiceExpiry const opts: InboundOptionals = { callbackUrl: cbUrl, expiry: expiry, expectedPayer: payer.user, linkedApplication: app, zapInfo, From 358249daba348af2b7e3542d62c88551c25459e0 Mon Sep 17 00:00:00 2001 From: boufni95 Date: Tue, 16 Sep 2025 19:46:45 +0000 Subject: [PATCH 5/7] deb --- src/services/main/applicationManager.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/services/main/applicationManager.ts b/src/services/main/applicationManager.ts index 035e547a..426bdc2b 100644 --- a/src/services/main/applicationManager.ts +++ b/src/services/main/applicationManager.ts @@ -198,6 +198,7 @@ export default class { console.log("req.invoice_req.expiry", req.invoice_req.expiry) console.log("defaultInvoiceExpiry", defaultInvoiceExpiry) const expiry = req.invoice_req.expiry ? Math.min(req.invoice_req.expiry, defaultInvoiceExpiry) : defaultInvoiceExpiry + console.log("expiry", expiry) const opts: InboundOptionals = { callbackUrl: cbUrl, expiry: expiry, expectedPayer: payer.user, linkedApplication: app, zapInfo, offerId: req.offer_string, payerData: req.payer_data?.data, rejectUnauthorized: req.rejectUnauthorized, From c6249764f083431b98f290559fd1746212161bab Mon Sep 17 00:00:00 2001 From: boufni95 Date: Tue, 16 Sep 2025 19:50:32 +0000 Subject: [PATCH 6/7] fix --- src/services/lnd/lnd.ts | 3 ++- src/services/main/liquidityProvider.ts | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/services/lnd/lnd.ts b/src/services/lnd/lnd.ts index 6c5112de..d1b65b50 100644 --- a/src/services/lnd/lnd.ts +++ b/src/services/lnd/lnd.ts @@ -290,7 +290,8 @@ export default class { async NewInvoice(value: number, memo: string, expiry: number, { useProvider, from }: TxActionOptions): Promise { if (useProvider) { - const invoice = await this.liquidProvider.AddInvoice(value, memo, from) + console.log("using provider") + const invoice = await this.liquidProvider.AddInvoice(value, memo, from, expiry) const providerDst = this.liquidProvider.GetProviderDestination() return { payRequest: invoice, providerDst } } diff --git a/src/services/main/liquidityProvider.ts b/src/services/main/liquidityProvider.ts index 01d60b94..725aa61e 100644 --- a/src/services/main/liquidityProvider.ts +++ b/src/services/main/liquidityProvider.ts @@ -165,12 +165,12 @@ export class LiquidityProvider { return true } - AddInvoice = async (amount: number, memo: string, from: 'user' | 'system') => { + AddInvoice = async (amount: number, memo: string, from: 'user' | 'system', expiry: number) => { try { if (!this.ready) { throw new Error("liquidity provider is not ready yet") } - const res = await this.client.NewInvoice({ amountSats: amount, memo }) + const res = await this.client.NewInvoice({ amountSats: amount, memo, expiry }) if (res.status === 'ERROR') { this.log("error creating invoice", res.reason) throw new Error(res.reason) From 8d8e5562ad281f33003a7c8192e4f641213b8968 Mon Sep 17 00:00:00 2001 From: boufni95 Date: Tue, 16 Sep 2025 19:52:30 +0000 Subject: [PATCH 7/7] clean --- src/services/main/applicationManager.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/services/main/applicationManager.ts b/src/services/main/applicationManager.ts index 426bdc2b..3aa33f52 100644 --- a/src/services/main/applicationManager.ts +++ b/src/services/main/applicationManager.ts @@ -195,10 +195,7 @@ export default class { if (req.invoice_req.zap) { zapInfo = this.paymentManager.validateZapEvent(req.invoice_req.zap, req.invoice_req.amountSats) } - console.log("req.invoice_req.expiry", req.invoice_req.expiry) - console.log("defaultInvoiceExpiry", defaultInvoiceExpiry) const expiry = req.invoice_req.expiry ? Math.min(req.invoice_req.expiry, defaultInvoiceExpiry) : defaultInvoiceExpiry - console.log("expiry", expiry) const opts: InboundOptionals = { callbackUrl: cbUrl, expiry: expiry, expectedPayer: payer.user, linkedApplication: app, zapInfo, offerId: req.offer_string, payerData: req.payer_data?.data, rejectUnauthorized: req.rejectUnauthorized,