diff --git a/src/custom-nip19.ts b/src/custom-nip19.ts index 64de7ba8..70a222cb 100644 --- a/src/custom-nip19.ts +++ b/src/custom-nip19.ts @@ -20,9 +20,9 @@ export type CustomProfilePointer = { export type OfferPointer = { pubkey: string, - relays?: string[], + relay: string, offer: string - priceType: 'fixed' | 'spontaneous' | 'variable', + priceType: PriceType, price?: number } enum PriceType { @@ -68,17 +68,16 @@ export const encodeNprofile = (profile: CustomProfilePointer): string => { } export const encodeNoffer = (offer: OfferPointer): string => { - let relays = offer.relays - if (!relays) { + let relay = offer.relay + if (!relay) { const settings = LoadNosrtSettingsFromEnv() - relays = settings.relays + relay = settings.relays[0] } - const typeAsNum = Number(PriceType[offer.priceType]) const o: TLV = { 0: [hexToBytes(offer.pubkey)], - 1: (relays).map(url => utf8Encoder.encode(url)), + 1: [utf8Encoder.encode(relay)], 2: [utf8Encoder.encode(offer.offer)], - 3: [new Uint8Array([typeAsNum])], + 3: [new Uint8Array([Number(offer.priceType)])], } if (offer.price) { o[4] = [new Uint8Array(new BigUint64Array([BigInt(offer.price)]).buffer)] @@ -113,11 +112,15 @@ export const decodeNoffer = (noffer: string): OfferPointer => { const tlv = parseTLV(data); if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for noffer') if (tlv[0][0].length !== 32) throw new Error('TLV 0 should be 32 bytes') - + if (!tlv[1]?.[0]) throw new Error('missing TLV 1 for noffer') + if (!tlv[2]?.[0]) throw new Error('missing TLV 2 for noffer') + if (!tlv[3]?.[0]) throw new Error('missing TLV 3 for noffer') return { pubkey: bytesToHex(tlv[0][0]), - relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [], - bridge: tlv[2] ? tlv[2].map(d => utf8Decoder.decode(d)) : [] + relay: utf8Decoder.decode(tlv[1][0]), + offer: utf8Decoder.decode(tlv[2][0]), + priceType: tlv[3][0][0], + price: tlv[4] ? Number(new BigUint64Array(tlv[4][0])[0]) : undefined } } diff --git a/src/nostrMiddleware.ts b/src/nostrMiddleware.ts index 2bf563ff..90579908 100644 --- a/src/nostrMiddleware.ts +++ b/src/nostrMiddleware.ts @@ -1,4 +1,4 @@ -import Main from "./services/main/index.js" +import Main, { NofferData } from "./services/main/index.js" import Nostr from "./services/nostr/index.js" import { NostrEvent, NostrSend, NostrSettings } from "./services/nostr/handler.js" import * as Types from '../proto/autogenerated/ts/types.js' @@ -7,7 +7,7 @@ import { ERROR, getLogger } from "./services/helpers/logger.js"; import { UnsignedEvent } from "./services/nostr/tools/event.js"; import { defaultInvoiceExpiry } from "./services/storage/paymentStorage.js"; import { Application } from "./services/storage/entity/Application.js"; -type NofferData = { offer: string, amount?: number } + export default (serverMethods: Types.ServerMethods, mainHandler: Main, nostrSettings: NostrSettings, onClientEvent: (e: { requestId: string }, fromPub: string) => void): { Stop: () => void, Send: NostrSend } => { const log = getLogger({}) const nostrTransport = NewNostrTransport(serverMethods, { diff --git a/src/services/main/index.ts b/src/services/main/index.ts index efcffa76..aba85c05 100644 --- a/src/services/main/index.ts +++ b/src/services/main/index.ts @@ -31,7 +31,7 @@ type UserOperationsSub = { newOutgoingTx: (operation: Types.UserOperation) => void } const appTag = "Lightning.Pub" - +export type NofferData = { offer: string, amount?: number } export default class { storage: Storage lnd: LND @@ -275,14 +275,14 @@ export default class { this.nostrSend({ type: 'app', appId: invoice.linkedApplication.app_id }, { type: 'event', event }) } - async getNofferInvoice(offerReq: { offer: string, amount?: number }, appId: string): Promise<{ success: true, invoice: string } | { success: false, code: number }> { + async getNofferInvoice(offerReq: NofferData, appId: string): Promise<{ success: true, invoice: string } | { success: false, code: number, max: number }> { try { const { remote } = await this.lnd.ChannelBalance() const { offer, amount } = offerReq const split = offer.split(':') if (split.length === 1) { if (!amount || isNaN(amount) || amount < 10 || amount > remote) { - return { success: false, code: 5 } + return { success: false, code: 5, max: remote } } const res = await this.applicationManager.AddAppUserInvoice(appId, { http_callback_url: "", payer_identifier: split[0], receiver_identifier: split[0], @@ -293,19 +293,19 @@ export default class { const product = await this.productManager.NewProductInvoice(split[1]) return { success: true, invoice: product.invoice } } else { - return { success: false, code: 1 } + return { success: false, code: 1, max: remote } } } catch (e: any) { getLogger({ component: "noffer" })(ERROR, e.message || e) - return { success: false, code: 1 } + return { success: false, code: 1, max: 0 } } } - async handleNip69Noffer(offerReq: { offer: string }, event: NostrEvent) { + async handleNip69Noffer(offerReq: NofferData, event: NostrEvent) { const offerInvoice = await this.getNofferInvoice(offerReq, event.appId) if (!offerInvoice.success) { const code = offerInvoice.code - const e = newNofferResponse(JSON.stringify({ code, message: codeToMessage(code) }), event) + const e = newNofferResponse(JSON.stringify({ code, error: codeToMessage(code), range: { min: 10, max: offerInvoice.max } }), event) this.nostrSend({ type: 'app', appId: event.appId }, { type: 'event', event: e, encrypt: { toPub: event.pub } }) return }