This commit is contained in:
boufni95 2024-09-04 15:23:32 +00:00
parent ee5f3c2743
commit 0b27757f72
3 changed files with 23 additions and 20 deletions

View file

@ -20,9 +20,9 @@ export type CustomProfilePointer = {
export type OfferPointer = { export type OfferPointer = {
pubkey: string, pubkey: string,
relays?: string[], relay: string,
offer: string offer: string
priceType: 'fixed' | 'spontaneous' | 'variable', priceType: PriceType,
price?: number price?: number
} }
enum PriceType { enum PriceType {
@ -68,17 +68,16 @@ export const encodeNprofile = (profile: CustomProfilePointer): string => {
} }
export const encodeNoffer = (offer: OfferPointer): string => { export const encodeNoffer = (offer: OfferPointer): string => {
let relays = offer.relays let relay = offer.relay
if (!relays) { if (!relay) {
const settings = LoadNosrtSettingsFromEnv() const settings = LoadNosrtSettingsFromEnv()
relays = settings.relays relay = settings.relays[0]
} }
const typeAsNum = Number(PriceType[offer.priceType])
const o: TLV = { const o: TLV = {
0: [hexToBytes(offer.pubkey)], 0: [hexToBytes(offer.pubkey)],
1: (relays).map(url => utf8Encoder.encode(url)), 1: [utf8Encoder.encode(relay)],
2: [utf8Encoder.encode(offer.offer)], 2: [utf8Encoder.encode(offer.offer)],
3: [new Uint8Array([typeAsNum])], 3: [new Uint8Array([Number(offer.priceType)])],
} }
if (offer.price) { if (offer.price) {
o[4] = [new Uint8Array(new BigUint64Array([BigInt(offer.price)]).buffer)] o[4] = [new Uint8Array(new BigUint64Array([BigInt(offer.price)]).buffer)]
@ -113,11 +112,15 @@ export const decodeNoffer = (noffer: string): OfferPointer => {
const tlv = parseTLV(data); const tlv = parseTLV(data);
if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for noffer') 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[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 { return {
pubkey: bytesToHex(tlv[0][0]), pubkey: bytesToHex(tlv[0][0]),
relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [], relay: utf8Decoder.decode(tlv[1][0]),
bridge: tlv[2] ? tlv[2].map(d => utf8Decoder.decode(d)) : [] offer: utf8Decoder.decode(tlv[2][0]),
priceType: tlv[3][0][0],
price: tlv[4] ? Number(new BigUint64Array(tlv[4][0])[0]) : undefined
} }
} }

View file

@ -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 Nostr from "./services/nostr/index.js"
import { NostrEvent, NostrSend, NostrSettings } from "./services/nostr/handler.js" import { NostrEvent, NostrSend, NostrSettings } from "./services/nostr/handler.js"
import * as Types from '../proto/autogenerated/ts/types.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 { UnsignedEvent } from "./services/nostr/tools/event.js";
import { defaultInvoiceExpiry } from "./services/storage/paymentStorage.js"; import { defaultInvoiceExpiry } from "./services/storage/paymentStorage.js";
import { Application } from "./services/storage/entity/Application.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 } => { export default (serverMethods: Types.ServerMethods, mainHandler: Main, nostrSettings: NostrSettings, onClientEvent: (e: { requestId: string }, fromPub: string) => void): { Stop: () => void, Send: NostrSend } => {
const log = getLogger({}) const log = getLogger({})
const nostrTransport = NewNostrTransport(serverMethods, { const nostrTransport = NewNostrTransport(serverMethods, {

View file

@ -31,7 +31,7 @@ type UserOperationsSub = {
newOutgoingTx: (operation: Types.UserOperation) => void newOutgoingTx: (operation: Types.UserOperation) => void
} }
const appTag = "Lightning.Pub" const appTag = "Lightning.Pub"
export type NofferData = { offer: string, amount?: number }
export default class { export default class {
storage: Storage storage: Storage
lnd: LND lnd: LND
@ -275,14 +275,14 @@ export default class {
this.nostrSend({ type: 'app', appId: invoice.linkedApplication.app_id }, { type: 'event', event }) 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 { try {
const { remote } = await this.lnd.ChannelBalance() const { remote } = await this.lnd.ChannelBalance()
const { offer, amount } = offerReq const { offer, amount } = offerReq
const split = offer.split(':') const split = offer.split(':')
if (split.length === 1) { if (split.length === 1) {
if (!amount || isNaN(amount) || amount < 10 || amount > remote) { 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, { const res = await this.applicationManager.AddAppUserInvoice(appId, {
http_callback_url: "", payer_identifier: split[0], receiver_identifier: split[0], 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]) const product = await this.productManager.NewProductInvoice(split[1])
return { success: true, invoice: product.invoice } return { success: true, invoice: product.invoice }
} else { } else {
return { success: false, code: 1 } return { success: false, code: 1, max: remote }
} }
} catch (e: any) { } catch (e: any) {
getLogger({ component: "noffer" })(ERROR, e.message || e) 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) const offerInvoice = await this.getNofferInvoice(offerReq, event.appId)
if (!offerInvoice.success) { if (!offerInvoice.success) {
const code = offerInvoice.code 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 } }) this.nostrSend({ type: 'app', appId: event.appId }, { type: 'event', event: e, encrypt: { toPub: event.pub } })
return return
} }