N swap services support

This commit is contained in:
boufni95 2026-01-14 19:39:27 +00:00
parent 9d082ea0a0
commit f6e425aed5
19 changed files with 557 additions and 426 deletions

View file

@ -44,6 +44,9 @@ import { AddBlindToUserOffer1760000000000 } from './build/src/services/storage/m
import { ApplicationAvatarUrl1761000001000 } from './build/src/services/storage/migrations/1761000001000-application_avatar_url.js' import { ApplicationAvatarUrl1761000001000 } from './build/src/services/storage/migrations/1761000001000-application_avatar_url.js'
import { AdminSettings1761683639419 } from './build/src/services/storage/migrations/1761683639419-admin_settings.js' import { AdminSettings1761683639419 } from './build/src/services/storage/migrations/1761683639419-admin_settings.js'
import { TxSwap1762890527098 } from './build/src/services/storage/migrations/1762890527098-tx_swap.js' import { TxSwap1762890527098 } from './build/src/services/storage/migrations/1762890527098-tx_swap.js'
import { TxSwapAddress1764779178945 } from './build/src/services/storage/migrations/1764779178945-tx_swap_address.js'
import { ClinkRequester1765497600000 } from './build/src/services/storage/migrations/1765497600000-clink_requester.js'
import { TrackedProviderHeight1766504040000 } from './build/src/services/storage/migrations/1766504040000-tracked_provider_height.js'
export default new DataSource({ export default new DataSource({
type: "better-sqlite3", type: "better-sqlite3",
@ -52,11 +55,12 @@ export default new DataSource({
migrations: [Initial1703170309875, LspOrder1718387847693, LiquidityProvider1719335699480, LndNodeInfo1720187506189, CreateInviteTokenTable1721751414878, migrations: [Initial1703170309875, LspOrder1718387847693, LiquidityProvider1719335699480, LndNodeInfo1720187506189, CreateInviteTokenTable1721751414878,
PaymentIndex1721760297610, DebitAccess1726496225078, DebitAccessFixes1726685229264, DebitToPub1727105758354, UserCbUrl1727112281043, PaymentIndex1721760297610, DebitAccess1726496225078, DebitAccessFixes1726685229264, DebitToPub1727105758354, UserCbUrl1727112281043,
UserOffer1733502626042, ManagementGrant1751307732346, InvoiceCallbackUrls1752425992291, OldSomethingLeftover1753106599604, UserReceivingInvoiceIdx1753109184611, UserOffer1733502626042, ManagementGrant1751307732346, InvoiceCallbackUrls1752425992291, OldSomethingLeftover1753106599604, UserReceivingInvoiceIdx1753109184611,
AppUserDevice1753285173175, UserAccess1759426050669, AddBlindToUserOffer1760000000000, ApplicationAvatarUrl1761000001000, AdminSettings1761683639419, TxSwap1762890527098], AppUserDevice1753285173175, UserAccess1759426050669, AddBlindToUserOffer1760000000000, ApplicationAvatarUrl1761000001000, AdminSettings1761683639419, TxSwap1762890527098,
TxSwapAddress1764779178945, ClinkRequester1765497600000, TrackedProviderHeight1766504040000],
entities: [User, UserReceivingInvoice, UserReceivingAddress, AddressReceivingTransaction, UserInvoicePayment, UserTransactionPayment, entities: [User, UserReceivingInvoice, UserReceivingAddress, AddressReceivingTransaction, UserInvoicePayment, UserTransactionPayment,
UserBasicAuth, UserEphemeralKey, Product, UserToUserPayment, Application, ApplicationUser, UserToUserPayment, LspOrder, LndNodeInfo, UserBasicAuth, UserEphemeralKey, Product, UserToUserPayment, Application, ApplicationUser, UserToUserPayment, LspOrder, LndNodeInfo,
TrackedProvider, InviteToken, DebitAccess, UserOffer, ManagementGrant, AppUserDevice, UserAccess, AdminSettings, TransactionSwap], TrackedProvider, InviteToken, DebitAccess, UserOffer, ManagementGrant, AppUserDevice, UserAccess, AdminSettings, TransactionSwap],
// synchronize: true, // synchronize: true,
}) })
//npx typeorm migration:generate ./src/services/storage/migrations/tx_swap_address -d ./datasource.js //npx typeorm migration:generate ./src/services/storage/migrations/swaps_service_url -d ./datasource.js

View file

@ -93,10 +93,10 @@ The nostr server will send back a message response, and inside the body there wi
- input: [MessagingToken](#MessagingToken) - input: [MessagingToken](#MessagingToken)
- This methods has an __empty__ __response__ body - This methods has an __empty__ __response__ body
- GetAdminTransactionSwapQuote - GetAdminTransactionSwapQuotes
- auth type: __Admin__ - auth type: __Admin__
- input: [TransactionSwapRequest](#TransactionSwapRequest) - input: [TransactionSwapRequest](#TransactionSwapRequest)
- output: [TransactionSwapQuote](#TransactionSwapQuote) - output: [TransactionSwapQuoteList](#TransactionSwapQuoteList)
- GetAppsMetrics - GetAppsMetrics
- auth type: __Metrics__ - auth type: __Metrics__
@ -203,10 +203,10 @@ The nostr server will send back a message response, and inside the body there wi
- input: [SingleMetricReq](#SingleMetricReq) - input: [SingleMetricReq](#SingleMetricReq)
- output: [UsageMetricTlv](#UsageMetricTlv) - output: [UsageMetricTlv](#UsageMetricTlv)
- GetTransactionSwapQuote - GetTransactionSwapQuotes
- auth type: __User__ - auth type: __User__
- input: [TransactionSwapRequest](#TransactionSwapRequest) - input: [TransactionSwapRequest](#TransactionSwapRequest)
- output: [TransactionSwapQuote](#TransactionSwapQuote) - output: [TransactionSwapQuoteList](#TransactionSwapQuoteList)
- GetUsageMetrics - GetUsageMetrics
- auth type: __Metrics__ - auth type: __Metrics__
@ -540,12 +540,12 @@ The nostr server will send back a message response, and inside the body there wi
- input: [MessagingToken](#MessagingToken) - input: [MessagingToken](#MessagingToken)
- This methods has an __empty__ __response__ body - This methods has an __empty__ __response__ body
- GetAdminTransactionSwapQuote - GetAdminTransactionSwapQuotes
- auth type: __Admin__ - auth type: __Admin__
- http method: __post__ - http method: __post__
- http route: __/api/admin/swap/transaction/quote__ - http route: __/api/admin/swap/transaction/quote__
- input: [TransactionSwapRequest](#TransactionSwapRequest) - input: [TransactionSwapRequest](#TransactionSwapRequest)
- output: [TransactionSwapQuote](#TransactionSwapQuote) - output: [TransactionSwapQuoteList](#TransactionSwapQuoteList)
- GetApp - GetApp
- auth type: __App__ - auth type: __App__
@ -740,12 +740,12 @@ The nostr server will send back a message response, and inside the body there wi
- input: [SingleMetricReq](#SingleMetricReq) - input: [SingleMetricReq](#SingleMetricReq)
- output: [UsageMetricTlv](#UsageMetricTlv) - output: [UsageMetricTlv](#UsageMetricTlv)
- GetTransactionSwapQuote - GetTransactionSwapQuotes
- auth type: __User__ - auth type: __User__
- http method: __post__ - http method: __post__
- http route: __/api/user/swap/quote__ - http route: __/api/user/swap/quote__
- input: [TransactionSwapRequest](#TransactionSwapRequest) - input: [TransactionSwapRequest](#TransactionSwapRequest)
- output: [TransactionSwapQuote](#TransactionSwapQuote) - output: [TransactionSwapQuoteList](#TransactionSwapQuoteList)
- GetUsageMetrics - GetUsageMetrics
- auth type: __Metrics__ - auth type: __Metrics__
@ -1654,10 +1654,14 @@ The nostr server will send back a message response, and inside the body there wi
- __chain_fee_sats__: _number_ - __chain_fee_sats__: _number_
- __invoice_amount_sats__: _number_ - __invoice_amount_sats__: _number_
- __service_fee_sats__: _number_ - __service_fee_sats__: _number_
- __service_url__: _string_
- __swap_fee_sats__: _number_ - __swap_fee_sats__: _number_
- __swap_operation_id__: _string_ - __swap_operation_id__: _string_
- __transaction_amount_sats__: _number_ - __transaction_amount_sats__: _number_
### TransactionSwapQuoteList
- __quotes__: ARRAY of: _[TransactionSwapQuote](#TransactionSwapQuote)_
### TransactionSwapRequest ### TransactionSwapRequest
- __transaction_amount_sats__: _number_ - __transaction_amount_sats__: _number_

View file

@ -66,86 +66,86 @@ type Client struct {
BanDebit func(req DebitOperation) error BanDebit func(req DebitOperation) error
BanUser func(req BanUserRequest) (*BanUserResponse, error) BanUser func(req BanUserRequest) (*BanUserResponse, error)
// batching method: BatchUser not implemented // batching method: BatchUser not implemented
CloseChannel func(req CloseChannelRequest) (*CloseChannelResponse, error) CloseChannel func(req CloseChannelRequest) (*CloseChannelResponse, error)
CreateOneTimeInviteLink func(req CreateOneTimeInviteLinkRequest) (*CreateOneTimeInviteLinkResponse, error) CreateOneTimeInviteLink func(req CreateOneTimeInviteLinkRequest) (*CreateOneTimeInviteLinkResponse, error)
DecodeInvoice func(req DecodeInvoiceRequest) (*DecodeInvoiceResponse, error) DecodeInvoice func(req DecodeInvoiceRequest) (*DecodeInvoiceResponse, error)
DeleteUserOffer func(req OfferId) error DeleteUserOffer func(req OfferId) error
EditDebit func(req DebitAuthorizationRequest) error EditDebit func(req DebitAuthorizationRequest) error
EncryptionExchange func(req EncryptionExchangeRequest) error EncryptionExchange func(req EncryptionExchangeRequest) error
EnrollAdminToken func(req EnrollAdminTokenRequest) error EnrollAdminToken func(req EnrollAdminTokenRequest) error
EnrollMessagingToken func(req MessagingToken) error EnrollMessagingToken func(req MessagingToken) error
GetAdminTransactionSwapQuote func(req TransactionSwapRequest) (*TransactionSwapQuote, error) GetAdminTransactionSwapQuotes func(req TransactionSwapRequest) (*TransactionSwapQuoteList, error)
GetApp func() (*Application, error) GetApp func() (*Application, error)
GetAppUser func(req GetAppUserRequest) (*AppUser, error) GetAppUser func(req GetAppUserRequest) (*AppUser, error)
GetAppUserLNURLInfo func(req GetAppUserLNURLInfoRequest) (*LnurlPayInfoResponse, error) GetAppUserLNURLInfo func(req GetAppUserLNURLInfoRequest) (*LnurlPayInfoResponse, error)
GetAppsMetrics func(req AppsMetricsRequest) (*AppsMetrics, error) GetAppsMetrics func(req AppsMetricsRequest) (*AppsMetrics, error)
GetBundleMetrics func(req LatestBundleMetricReq) (*BundleMetrics, error) GetBundleMetrics func(req LatestBundleMetricReq) (*BundleMetrics, error)
GetDebitAuthorizations func() (*DebitAuthorizations, error) GetDebitAuthorizations func() (*DebitAuthorizations, error)
GetErrorStats func() (*ErrorStats, error) GetErrorStats func() (*ErrorStats, error)
GetHttpCreds func() (*HttpCreds, error) GetHttpCreds func() (*HttpCreds, error)
GetInviteLinkState func(req GetInviteTokenStateRequest) (*GetInviteTokenStateResponse, error) GetInviteLinkState func(req GetInviteTokenStateRequest) (*GetInviteTokenStateResponse, error)
GetLNURLChannelLink func() (*LnurlLinkResponse, error) GetLNURLChannelLink func() (*LnurlLinkResponse, error)
GetLiveDebitRequests func() (*LiveDebitRequest, error) GetLiveDebitRequests func() (*LiveDebitRequest, error)
GetLiveManageRequests func() (*LiveManageRequest, error) GetLiveManageRequests func() (*LiveManageRequest, error)
GetLiveUserOperations func() (*LiveUserOperation, error) GetLiveUserOperations func() (*LiveUserOperation, error)
GetLndForwardingMetrics func(req LndMetricsRequest) (*LndForwardingMetrics, error) GetLndForwardingMetrics func(req LndMetricsRequest) (*LndForwardingMetrics, error)
GetLndMetrics func(req LndMetricsRequest) (*LndMetrics, error) GetLndMetrics func(req LndMetricsRequest) (*LndMetrics, error)
GetLnurlPayInfo func(query GetLnurlPayInfo_Query) (*LnurlPayInfoResponse, error) GetLnurlPayInfo func(query GetLnurlPayInfo_Query) (*LnurlPayInfoResponse, error)
GetLnurlPayLink func() (*LnurlLinkResponse, error) GetLnurlPayLink func() (*LnurlLinkResponse, error)
GetLnurlWithdrawInfo func(query GetLnurlWithdrawInfo_Query) (*LnurlWithdrawInfoResponse, error) GetLnurlWithdrawInfo func(query GetLnurlWithdrawInfo_Query) (*LnurlWithdrawInfoResponse, error)
GetLnurlWithdrawLink func() (*LnurlLinkResponse, error) GetLnurlWithdrawLink func() (*LnurlLinkResponse, error)
GetManageAuthorizations func() (*ManageAuthorizations, error) GetManageAuthorizations func() (*ManageAuthorizations, error)
GetMigrationUpdate func() (*MigrationUpdate, error) GetMigrationUpdate func() (*MigrationUpdate, error)
GetNPubLinkingState func(req GetNPubLinking) (*NPubLinking, error) GetNPubLinkingState func(req GetNPubLinking) (*NPubLinking, error)
GetPaymentState func(req GetPaymentStateRequest) (*PaymentState, error) GetPaymentState func(req GetPaymentStateRequest) (*PaymentState, error)
GetProvidersDisruption func() (*ProvidersDisruption, error) GetProvidersDisruption func() (*ProvidersDisruption, error)
GetSeed func() (*LndSeed, error) GetSeed func() (*LndSeed, error)
GetSingleBundleMetrics func(req SingleMetricReq) (*BundleData, error) GetSingleBundleMetrics func(req SingleMetricReq) (*BundleData, error)
GetSingleUsageMetrics func(req SingleMetricReq) (*UsageMetricTlv, error) GetSingleUsageMetrics func(req SingleMetricReq) (*UsageMetricTlv, error)
GetTransactionSwapQuote func(req TransactionSwapRequest) (*TransactionSwapQuote, error) GetTransactionSwapQuotes func(req TransactionSwapRequest) (*TransactionSwapQuoteList, error)
GetUsageMetrics func(req LatestUsageMetricReq) (*UsageMetrics, error) GetUsageMetrics func(req LatestUsageMetricReq) (*UsageMetrics, error)
GetUserInfo func() (*UserInfo, error) GetUserInfo func() (*UserInfo, error)
GetUserOffer func(req OfferId) (*OfferConfig, error) GetUserOffer func(req OfferId) (*OfferConfig, error)
GetUserOfferInvoices func(req GetUserOfferInvoicesReq) (*OfferInvoices, error) GetUserOfferInvoices func(req GetUserOfferInvoicesReq) (*OfferInvoices, error)
GetUserOffers func() (*UserOffers, error) GetUserOffers func() (*UserOffers, error)
GetUserOperations func(req GetUserOperationsRequest) (*GetUserOperationsResponse, error) GetUserOperations func(req GetUserOperationsRequest) (*GetUserOperationsResponse, error)
HandleLnurlAddress func(routeParams HandleLnurlAddress_RouteParams) (*LnurlPayInfoResponse, error) HandleLnurlAddress func(routeParams HandleLnurlAddress_RouteParams) (*LnurlPayInfoResponse, error)
HandleLnurlPay func(query HandleLnurlPay_Query) (*HandleLnurlPayResponse, error) HandleLnurlPay func(query HandleLnurlPay_Query) (*HandleLnurlPayResponse, error)
HandleLnurlWithdraw func(query HandleLnurlWithdraw_Query) error HandleLnurlWithdraw func(query HandleLnurlWithdraw_Query) error
Health func() error Health func() error
LinkNPubThroughToken func(req LinkNPubThroughTokenRequest) error LinkNPubThroughToken func(req LinkNPubThroughTokenRequest) error
ListAdminSwaps func() (*SwapsList, error) ListAdminSwaps func() (*SwapsList, error)
ListChannels func() (*LndChannels, error) ListChannels func() (*LndChannels, error)
ListSwaps func() (*SwapsList, error) ListSwaps func() (*SwapsList, error)
LndGetInfo func(req LndGetInfoRequest) (*LndGetInfoResponse, error) LndGetInfo func(req LndGetInfoRequest) (*LndGetInfoResponse, error)
NewAddress func(req NewAddressRequest) (*NewAddressResponse, error) NewAddress func(req NewAddressRequest) (*NewAddressResponse, error)
NewInvoice func(req NewInvoiceRequest) (*NewInvoiceResponse, error) NewInvoice func(req NewInvoiceRequest) (*NewInvoiceResponse, error)
NewProductInvoice func(query NewProductInvoice_Query) (*NewInvoiceResponse, error) NewProductInvoice func(query NewProductInvoice_Query) (*NewInvoiceResponse, error)
OpenChannel func(req OpenChannelRequest) (*OpenChannelResponse, error) OpenChannel func(req OpenChannelRequest) (*OpenChannelResponse, error)
PayAddress func(req PayAddressRequest) (*PayAddressResponse, error) PayAddress func(req PayAddressRequest) (*PayAddressResponse, error)
PayAdminTransactionSwap func(req PayAdminTransactionSwapRequest) (*AdminSwapResponse, error) PayAdminTransactionSwap func(req PayAdminTransactionSwapRequest) (*AdminSwapResponse, error)
PayAppUserInvoice func(req PayAppUserInvoiceRequest) (*PayInvoiceResponse, error) PayAppUserInvoice func(req PayAppUserInvoiceRequest) (*PayInvoiceResponse, error)
PayInvoice func(req PayInvoiceRequest) (*PayInvoiceResponse, error) PayInvoice func(req PayInvoiceRequest) (*PayInvoiceResponse, error)
PingSubProcesses func() error PingSubProcesses func() error
RequestNPubLinkingToken func(req RequestNPubLinkingTokenRequest) (*RequestNPubLinkingTokenResponse, error) RequestNPubLinkingToken func(req RequestNPubLinkingTokenRequest) (*RequestNPubLinkingTokenResponse, error)
ResetDebit func(req DebitOperation) error ResetDebit func(req DebitOperation) error
ResetManage func(req ManageOperation) error ResetManage func(req ManageOperation) error
ResetMetricsStorages func() error ResetMetricsStorages func() error
ResetNPubLinkingToken func(req RequestNPubLinkingTokenRequest) (*RequestNPubLinkingTokenResponse, error) ResetNPubLinkingToken func(req RequestNPubLinkingTokenRequest) (*RequestNPubLinkingTokenResponse, error)
RespondToDebit func(req DebitResponse) error RespondToDebit func(req DebitResponse) error
SendAppUserToAppPayment func(req SendAppUserToAppPaymentRequest) error SendAppUserToAppPayment func(req SendAppUserToAppPaymentRequest) error
SendAppUserToAppUserPayment func(req SendAppUserToAppUserPaymentRequest) error SendAppUserToAppUserPayment func(req SendAppUserToAppUserPaymentRequest) error
SetMockAppBalance func(req SetMockAppBalanceRequest) error SetMockAppBalance func(req SetMockAppBalanceRequest) error
SetMockAppUserBalance func(req SetMockAppUserBalanceRequest) error SetMockAppUserBalance func(req SetMockAppUserBalanceRequest) error
SetMockInvoiceAsPaid func(req SetMockInvoiceAsPaidRequest) error SetMockInvoiceAsPaid func(req SetMockInvoiceAsPaidRequest) error
SubToWebRtcCandidates func() (*WebRtcCandidate, error) SubToWebRtcCandidates func() (*WebRtcCandidate, error)
SubmitWebRtcMessage func(req WebRtcMessage) (*WebRtcAnswer, error) SubmitWebRtcMessage func(req WebRtcMessage) (*WebRtcAnswer, error)
UpdateCallbackUrl func(req CallbackUrl) (*CallbackUrl, error) UpdateCallbackUrl func(req CallbackUrl) (*CallbackUrl, error)
UpdateChannelPolicy func(req UpdateChannelPolicyRequest) error UpdateChannelPolicy func(req UpdateChannelPolicyRequest) error
UpdateUserOffer func(req OfferConfig) error UpdateUserOffer func(req OfferConfig) error
UseInviteLink func(req UseInviteLinkRequest) error UseInviteLink func(req UseInviteLinkRequest) error
UserHealth func() (*UserHealthState, error) UserHealth func() (*UserHealthState, error)
ZipMetricsStorages func() (*ZippedMetrics, error) ZipMetricsStorages func() (*ZippedMetrics, error)
} }
func NewClient(params ClientParams) *Client { func NewClient(params ClientParams) *Client {
@ -667,7 +667,7 @@ func NewClient(params ClientParams) *Client {
} }
return nil return nil
}, },
GetAdminTransactionSwapQuote: func(req TransactionSwapRequest) (*TransactionSwapQuote, error) { GetAdminTransactionSwapQuotes: func(req TransactionSwapRequest) (*TransactionSwapQuoteList, error) {
auth, err := params.RetrieveAdminAuth() auth, err := params.RetrieveAdminAuth()
if err != nil { if err != nil {
return nil, err return nil, err
@ -689,7 +689,7 @@ func NewClient(params ClientParams) *Client {
if result.Status == "ERROR" { if result.Status == "ERROR" {
return nil, fmt.Errorf(result.Reason) return nil, fmt.Errorf(result.Reason)
} }
res := TransactionSwapQuote{} res := TransactionSwapQuoteList{}
err = json.Unmarshal(resBody, &res) err = json.Unmarshal(resBody, &res)
if err != nil { if err != nil {
return nil, err return nil, err
@ -1319,7 +1319,7 @@ func NewClient(params ClientParams) *Client {
} }
return &res, nil return &res, nil
}, },
GetTransactionSwapQuote: func(req TransactionSwapRequest) (*TransactionSwapQuote, error) { GetTransactionSwapQuotes: func(req TransactionSwapRequest) (*TransactionSwapQuoteList, error) {
auth, err := params.RetrieveUserAuth() auth, err := params.RetrieveUserAuth()
if err != nil { if err != nil {
return nil, err return nil, err
@ -1341,7 +1341,7 @@ func NewClient(params ClientParams) *Client {
if result.Status == "ERROR" { if result.Status == "ERROR" {
return nil, fmt.Errorf(result.Reason) return nil, fmt.Errorf(result.Reason)
} }
res := TransactionSwapQuote{} res := TransactionSwapQuoteList{}
err = json.Unmarshal(resBody, &res) err = json.Unmarshal(resBody, &res)
if err != nil { if err != nil {
return nil, err return nil, err

View file

@ -679,10 +679,14 @@ type TransactionSwapQuote struct {
Chain_fee_sats int64 `json:"chain_fee_sats"` Chain_fee_sats int64 `json:"chain_fee_sats"`
Invoice_amount_sats int64 `json:"invoice_amount_sats"` Invoice_amount_sats int64 `json:"invoice_amount_sats"`
Service_fee_sats int64 `json:"service_fee_sats"` Service_fee_sats int64 `json:"service_fee_sats"`
Service_url string `json:"service_url"`
Swap_fee_sats int64 `json:"swap_fee_sats"` Swap_fee_sats int64 `json:"swap_fee_sats"`
Swap_operation_id string `json:"swap_operation_id"` Swap_operation_id string `json:"swap_operation_id"`
Transaction_amount_sats int64 `json:"transaction_amount_sats"` Transaction_amount_sats int64 `json:"transaction_amount_sats"`
} }
type TransactionSwapQuoteList struct {
Quotes []TransactionSwapQuote `json:"quotes"`
}
type TransactionSwapRequest struct { type TransactionSwapRequest struct {
Transaction_amount_sats int64 `json:"transaction_amount_sats"` Transaction_amount_sats int64 `json:"transaction_amount_sats"`
} }

View file

@ -477,14 +477,14 @@ export default (methods: Types.ServerMethods, opts: ServerOptions) => {
callsMetrics.push({ ...opInfo, ...opStats, ...ctx }) callsMetrics.push({ ...opInfo, ...opStats, ...ctx })
} }
break break
case 'GetTransactionSwapQuote': case 'GetTransactionSwapQuotes':
if (!methods.GetTransactionSwapQuote) { if (!methods.GetTransactionSwapQuotes) {
throw new Error('method GetTransactionSwapQuote not found' ) throw new Error('method GetTransactionSwapQuotes not found' )
} else { } else {
const error = Types.TransactionSwapRequestValidate(operation.req) const error = Types.TransactionSwapRequestValidate(operation.req)
opStats.validate = process.hrtime.bigint() opStats.validate = process.hrtime.bigint()
if (error !== null) throw error if (error !== null) throw error
const res = await methods.GetTransactionSwapQuote({...operation, ctx}); responses.push({ status: 'OK', ...res }) const res = await methods.GetTransactionSwapQuotes({...operation, ctx}); responses.push({ status: 'OK', ...res })
opStats.handle = process.hrtime.bigint() opStats.handle = process.hrtime.bigint()
callsMetrics.push({ ...opInfo, ...opStats, ...ctx }) callsMetrics.push({ ...opInfo, ...opStats, ...ctx })
} }
@ -869,13 +869,13 @@ export default (methods: Types.ServerMethods, opts: ServerOptions) => {
opts.metricsCallback([{ ...info, ...stats, ...authContext }]) opts.metricsCallback([{ ...info, ...stats, ...authContext }])
} catch (ex) { const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback); if (opts.throwErrors) throw e } } catch (ex) { const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback); if (opts.throwErrors) throw e }
}) })
if (!opts.allowNotImplementedMethods && !methods.GetAdminTransactionSwapQuote) throw new Error('method: GetAdminTransactionSwapQuote is not implemented') if (!opts.allowNotImplementedMethods && !methods.GetAdminTransactionSwapQuotes) throw new Error('method: GetAdminTransactionSwapQuotes is not implemented')
app.post('/api/admin/swap/transaction/quote', async (req, res) => { app.post('/api/admin/swap/transaction/quote', async (req, res) => {
const info: Types.RequestInfo = { rpcName: 'GetAdminTransactionSwapQuote', batch: false, nostr: false, batchSize: 0} const info: Types.RequestInfo = { rpcName: 'GetAdminTransactionSwapQuotes', batch: false, nostr: false, batchSize: 0}
const stats: Types.RequestStats = { startMs:req.startTimeMs || 0, start:req.startTime || 0n, parse: process.hrtime.bigint(), guard: 0n, validate: 0n, handle: 0n } const stats: Types.RequestStats = { startMs:req.startTimeMs || 0, start:req.startTime || 0n, parse: process.hrtime.bigint(), guard: 0n, validate: 0n, handle: 0n }
let authCtx: Types.AuthContext = {} let authCtx: Types.AuthContext = {}
try { try {
if (!methods.GetAdminTransactionSwapQuote) throw new Error('method: GetAdminTransactionSwapQuote is not implemented') if (!methods.GetAdminTransactionSwapQuotes) throw new Error('method: GetAdminTransactionSwapQuotes is not implemented')
const authContext = await opts.AdminAuthGuard(req.headers['authorization']) const authContext = await opts.AdminAuthGuard(req.headers['authorization'])
authCtx = authContext authCtx = authContext
stats.guard = process.hrtime.bigint() stats.guard = process.hrtime.bigint()
@ -885,7 +885,7 @@ export default (methods: Types.ServerMethods, opts: ServerOptions) => {
if (error !== null) return logErrorAndReturnResponse(error, 'invalid request body', res, logger, { ...info, ...stats, ...authContext }, opts.metricsCallback) if (error !== null) return logErrorAndReturnResponse(error, 'invalid request body', res, logger, { ...info, ...stats, ...authContext }, opts.metricsCallback)
const query = req.query const query = req.query
const params = req.params const params = req.params
const response = await methods.GetAdminTransactionSwapQuote({rpcName:'GetAdminTransactionSwapQuote', ctx:authContext , req: request}) const response = await methods.GetAdminTransactionSwapQuotes({rpcName:'GetAdminTransactionSwapQuotes', ctx:authContext , req: request})
stats.handle = process.hrtime.bigint() stats.handle = process.hrtime.bigint()
res.json({status: 'OK', ...response}) res.json({status: 'OK', ...response})
opts.metricsCallback([{ ...info, ...stats, ...authContext }]) opts.metricsCallback([{ ...info, ...stats, ...authContext }])
@ -1361,13 +1361,13 @@ export default (methods: Types.ServerMethods, opts: ServerOptions) => {
opts.metricsCallback([{ ...info, ...stats, ...authContext }]) opts.metricsCallback([{ ...info, ...stats, ...authContext }])
} catch (ex) { const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback); if (opts.throwErrors) throw e } } catch (ex) { const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback); if (opts.throwErrors) throw e }
}) })
if (!opts.allowNotImplementedMethods && !methods.GetTransactionSwapQuote) throw new Error('method: GetTransactionSwapQuote is not implemented') if (!opts.allowNotImplementedMethods && !methods.GetTransactionSwapQuotes) throw new Error('method: GetTransactionSwapQuotes is not implemented')
app.post('/api/user/swap/quote', async (req, res) => { app.post('/api/user/swap/quote', async (req, res) => {
const info: Types.RequestInfo = { rpcName: 'GetTransactionSwapQuote', batch: false, nostr: false, batchSize: 0} const info: Types.RequestInfo = { rpcName: 'GetTransactionSwapQuotes', batch: false, nostr: false, batchSize: 0}
const stats: Types.RequestStats = { startMs:req.startTimeMs || 0, start:req.startTime || 0n, parse: process.hrtime.bigint(), guard: 0n, validate: 0n, handle: 0n } const stats: Types.RequestStats = { startMs:req.startTimeMs || 0, start:req.startTime || 0n, parse: process.hrtime.bigint(), guard: 0n, validate: 0n, handle: 0n }
let authCtx: Types.AuthContext = {} let authCtx: Types.AuthContext = {}
try { try {
if (!methods.GetTransactionSwapQuote) throw new Error('method: GetTransactionSwapQuote is not implemented') if (!methods.GetTransactionSwapQuotes) throw new Error('method: GetTransactionSwapQuotes is not implemented')
const authContext = await opts.UserAuthGuard(req.headers['authorization']) const authContext = await opts.UserAuthGuard(req.headers['authorization'])
authCtx = authContext authCtx = authContext
stats.guard = process.hrtime.bigint() stats.guard = process.hrtime.bigint()
@ -1377,7 +1377,7 @@ export default (methods: Types.ServerMethods, opts: ServerOptions) => {
if (error !== null) return logErrorAndReturnResponse(error, 'invalid request body', res, logger, { ...info, ...stats, ...authContext }, opts.metricsCallback) if (error !== null) return logErrorAndReturnResponse(error, 'invalid request body', res, logger, { ...info, ...stats, ...authContext }, opts.metricsCallback)
const query = req.query const query = req.query
const params = req.params const params = req.params
const response = await methods.GetTransactionSwapQuote({rpcName:'GetTransactionSwapQuote', ctx:authContext , req: request}) const response = await methods.GetTransactionSwapQuotes({rpcName:'GetTransactionSwapQuotes', ctx:authContext , req: request})
stats.handle = process.hrtime.bigint() stats.handle = process.hrtime.bigint()
res.json({status: 'OK', ...response}) res.json({status: 'OK', ...response})
opts.metricsCallback([{ ...info, ...stats, ...authContext }]) opts.metricsCallback([{ ...info, ...stats, ...authContext }])

View file

@ -273,7 +273,7 @@ export default (params: ClientParams) => ({
} }
return { status: 'ERROR', reason: 'invalid response' } return { status: 'ERROR', reason: 'invalid response' }
}, },
GetAdminTransactionSwapQuote: async (request: Types.TransactionSwapRequest): Promise<ResultError | ({ status: 'OK' }& Types.TransactionSwapQuote)> => { GetAdminTransactionSwapQuotes: async (request: Types.TransactionSwapRequest): Promise<ResultError | ({ status: 'OK' }& Types.TransactionSwapQuoteList)> => {
const auth = await params.retrieveAdminAuth() const auth = await params.retrieveAdminAuth()
if (auth === null) throw new Error('retrieveAdminAuth() returned null') if (auth === null) throw new Error('retrieveAdminAuth() returned null')
let finalRoute = '/api/admin/swap/transaction/quote' let finalRoute = '/api/admin/swap/transaction/quote'
@ -282,7 +282,7 @@ export default (params: ClientParams) => ({
if (data.status === 'OK') { if (data.status === 'OK') {
const result = data const result = data
if(!params.checkResult) return { status: 'OK', ...result } if(!params.checkResult) return { status: 'OK', ...result }
const error = Types.TransactionSwapQuoteValidate(result) const error = Types.TransactionSwapQuoteListValidate(result)
if (error === null) { return { status: 'OK', ...result } } else return { status: 'ERROR', reason: error.message } if (error === null) { return { status: 'OK', ...result } } else return { status: 'ERROR', reason: error.message }
} }
return { status: 'ERROR', reason: 'invalid response' } return { status: 'ERROR', reason: 'invalid response' }
@ -617,7 +617,7 @@ export default (params: ClientParams) => ({
} }
return { status: 'ERROR', reason: 'invalid response' } return { status: 'ERROR', reason: 'invalid response' }
}, },
GetTransactionSwapQuote: async (request: Types.TransactionSwapRequest): Promise<ResultError | ({ status: 'OK' }& Types.TransactionSwapQuote)> => { GetTransactionSwapQuotes: async (request: Types.TransactionSwapRequest): Promise<ResultError | ({ status: 'OK' }& Types.TransactionSwapQuoteList)> => {
const auth = await params.retrieveUserAuth() const auth = await params.retrieveUserAuth()
if (auth === null) throw new Error('retrieveUserAuth() returned null') if (auth === null) throw new Error('retrieveUserAuth() returned null')
let finalRoute = '/api/user/swap/quote' let finalRoute = '/api/user/swap/quote'
@ -626,7 +626,7 @@ export default (params: ClientParams) => ({
if (data.status === 'OK') { if (data.status === 'OK') {
const result = data const result = data
if(!params.checkResult) return { status: 'OK', ...result } if(!params.checkResult) return { status: 'OK', ...result }
const error = Types.TransactionSwapQuoteValidate(result) const error = Types.TransactionSwapQuoteListValidate(result)
if (error === null) { return { status: 'OK', ...result } } else return { status: 'ERROR', reason: error.message } if (error === null) { return { status: 'OK', ...result } } else return { status: 'ERROR', reason: error.message }
} }
return { status: 'ERROR', reason: 'invalid response' } return { status: 'ERROR', reason: 'invalid response' }

View file

@ -230,17 +230,17 @@ export default (params: NostrClientParams, send: (to:string, message: NostrRequ
} }
return { status: 'ERROR', reason: 'invalid response' } return { status: 'ERROR', reason: 'invalid response' }
}, },
GetAdminTransactionSwapQuote: async (request: Types.TransactionSwapRequest): Promise<ResultError | ({ status: 'OK' }& Types.TransactionSwapQuote)> => { GetAdminTransactionSwapQuotes: async (request: Types.TransactionSwapRequest): Promise<ResultError | ({ status: 'OK' }& Types.TransactionSwapQuoteList)> => {
const auth = await params.retrieveNostrAdminAuth() const auth = await params.retrieveNostrAdminAuth()
if (auth === null) throw new Error('retrieveNostrAdminAuth() returned null') if (auth === null) throw new Error('retrieveNostrAdminAuth() returned null')
const nostrRequest: NostrRequest = {} const nostrRequest: NostrRequest = {}
nostrRequest.body = request nostrRequest.body = request
const data = await send(params.pubDestination, {rpcName:'GetAdminTransactionSwapQuote',authIdentifier:auth, ...nostrRequest }) const data = await send(params.pubDestination, {rpcName:'GetAdminTransactionSwapQuotes',authIdentifier:auth, ...nostrRequest })
if (data.status === 'ERROR' && typeof data.reason === 'string') return data if (data.status === 'ERROR' && typeof data.reason === 'string') return data
if (data.status === 'OK') { if (data.status === 'OK') {
const result = data const result = data
if(!params.checkResult) return { status: 'OK', ...result } if(!params.checkResult) return { status: 'OK', ...result }
const error = Types.TransactionSwapQuoteValidate(result) const error = Types.TransactionSwapQuoteListValidate(result)
if (error === null) { return { status: 'OK', ...result } } else return { status: 'ERROR', reason: error.message } if (error === null) { return { status: 'OK', ...result } } else return { status: 'ERROR', reason: error.message }
} }
return { status: 'ERROR', reason: 'invalid response' } return { status: 'ERROR', reason: 'invalid response' }
@ -551,17 +551,17 @@ export default (params: NostrClientParams, send: (to:string, message: NostrRequ
} }
return { status: 'ERROR', reason: 'invalid response' } return { status: 'ERROR', reason: 'invalid response' }
}, },
GetTransactionSwapQuote: async (request: Types.TransactionSwapRequest): Promise<ResultError | ({ status: 'OK' }& Types.TransactionSwapQuote)> => { GetTransactionSwapQuotes: async (request: Types.TransactionSwapRequest): Promise<ResultError | ({ status: 'OK' }& Types.TransactionSwapQuoteList)> => {
const auth = await params.retrieveNostrUserAuth() const auth = await params.retrieveNostrUserAuth()
if (auth === null) throw new Error('retrieveNostrUserAuth() returned null') if (auth === null) throw new Error('retrieveNostrUserAuth() returned null')
const nostrRequest: NostrRequest = {} const nostrRequest: NostrRequest = {}
nostrRequest.body = request nostrRequest.body = request
const data = await send(params.pubDestination, {rpcName:'GetTransactionSwapQuote',authIdentifier:auth, ...nostrRequest }) const data = await send(params.pubDestination, {rpcName:'GetTransactionSwapQuotes',authIdentifier:auth, ...nostrRequest })
if (data.status === 'ERROR' && typeof data.reason === 'string') return data if (data.status === 'ERROR' && typeof data.reason === 'string') return data
if (data.status === 'OK') { if (data.status === 'OK') {
const result = data const result = data
if(!params.checkResult) return { status: 'OK', ...result } if(!params.checkResult) return { status: 'OK', ...result }
const error = Types.TransactionSwapQuoteValidate(result) const error = Types.TransactionSwapQuoteListValidate(result)
if (error === null) { return { status: 'OK', ...result } } else return { status: 'ERROR', reason: error.message } if (error === null) { return { status: 'OK', ...result } } else return { status: 'ERROR', reason: error.message }
} }
return { status: 'ERROR', reason: 'invalid response' } return { status: 'ERROR', reason: 'invalid response' }

View file

@ -359,14 +359,14 @@ export default (methods: Types.ServerMethods, opts: NostrOptions) => {
callsMetrics.push({ ...opInfo, ...opStats, ...ctx }) callsMetrics.push({ ...opInfo, ...opStats, ...ctx })
} }
break break
case 'GetTransactionSwapQuote': case 'GetTransactionSwapQuotes':
if (!methods.GetTransactionSwapQuote) { if (!methods.GetTransactionSwapQuotes) {
throw new Error('method not defined: GetTransactionSwapQuote') throw new Error('method not defined: GetTransactionSwapQuotes')
} else { } else {
const error = Types.TransactionSwapRequestValidate(operation.req) const error = Types.TransactionSwapRequestValidate(operation.req)
opStats.validate = process.hrtime.bigint() opStats.validate = process.hrtime.bigint()
if (error !== null) throw error if (error !== null) throw error
const res = await methods.GetTransactionSwapQuote({...operation, ctx}); responses.push({ status: 'OK', ...res }) const res = await methods.GetTransactionSwapQuotes({...operation, ctx}); responses.push({ status: 'OK', ...res })
opStats.handle = process.hrtime.bigint() opStats.handle = process.hrtime.bigint()
callsMetrics.push({ ...opInfo, ...opStats, ...ctx }) callsMetrics.push({ ...opInfo, ...opStats, ...ctx })
} }
@ -687,9 +687,9 @@ export default (methods: Types.ServerMethods, opts: NostrOptions) => {
opts.metricsCallback([{ ...info, ...stats, ...authContext }]) opts.metricsCallback([{ ...info, ...stats, ...authContext }])
}catch(ex){ const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback); if (opts.throwErrors) throw e } }catch(ex){ const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback); if (opts.throwErrors) throw e }
break break
case 'GetAdminTransactionSwapQuote': case 'GetAdminTransactionSwapQuotes':
try { try {
if (!methods.GetAdminTransactionSwapQuote) throw new Error('method: GetAdminTransactionSwapQuote is not implemented') if (!methods.GetAdminTransactionSwapQuotes) throw new Error('method: GetAdminTransactionSwapQuotes is not implemented')
const authContext = await opts.NostrAdminAuthGuard(req.appId, req.authIdentifier) const authContext = await opts.NostrAdminAuthGuard(req.appId, req.authIdentifier)
stats.guard = process.hrtime.bigint() stats.guard = process.hrtime.bigint()
authCtx = authContext authCtx = authContext
@ -697,7 +697,7 @@ export default (methods: Types.ServerMethods, opts: NostrOptions) => {
const error = Types.TransactionSwapRequestValidate(request) const error = Types.TransactionSwapRequestValidate(request)
stats.validate = process.hrtime.bigint() stats.validate = process.hrtime.bigint()
if (error !== null) return logErrorAndReturnResponse(error, 'invalid request body', res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback) if (error !== null) return logErrorAndReturnResponse(error, 'invalid request body', res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback)
const response = await methods.GetAdminTransactionSwapQuote({rpcName:'GetAdminTransactionSwapQuote', ctx:authContext , req: request}) const response = await methods.GetAdminTransactionSwapQuotes({rpcName:'GetAdminTransactionSwapQuotes', ctx:authContext , req: request})
stats.handle = process.hrtime.bigint() stats.handle = process.hrtime.bigint()
res({status: 'OK', ...response}) res({status: 'OK', ...response})
opts.metricsCallback([{ ...info, ...stats, ...authContext }]) opts.metricsCallback([{ ...info, ...stats, ...authContext }])
@ -1000,9 +1000,9 @@ export default (methods: Types.ServerMethods, opts: NostrOptions) => {
opts.metricsCallback([{ ...info, ...stats, ...authContext }]) opts.metricsCallback([{ ...info, ...stats, ...authContext }])
}catch(ex){ const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback); if (opts.throwErrors) throw e } }catch(ex){ const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback); if (opts.throwErrors) throw e }
break break
case 'GetTransactionSwapQuote': case 'GetTransactionSwapQuotes':
try { try {
if (!methods.GetTransactionSwapQuote) throw new Error('method: GetTransactionSwapQuote is not implemented') if (!methods.GetTransactionSwapQuotes) throw new Error('method: GetTransactionSwapQuotes is not implemented')
const authContext = await opts.NostrUserAuthGuard(req.appId, req.authIdentifier) const authContext = await opts.NostrUserAuthGuard(req.appId, req.authIdentifier)
stats.guard = process.hrtime.bigint() stats.guard = process.hrtime.bigint()
authCtx = authContext authCtx = authContext
@ -1010,7 +1010,7 @@ export default (methods: Types.ServerMethods, opts: NostrOptions) => {
const error = Types.TransactionSwapRequestValidate(request) const error = Types.TransactionSwapRequestValidate(request)
stats.validate = process.hrtime.bigint() stats.validate = process.hrtime.bigint()
if (error !== null) return logErrorAndReturnResponse(error, 'invalid request body', res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback) if (error !== null) return logErrorAndReturnResponse(error, 'invalid request body', res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback)
const response = await methods.GetTransactionSwapQuote({rpcName:'GetTransactionSwapQuote', ctx:authContext , req: request}) const response = await methods.GetTransactionSwapQuotes({rpcName:'GetTransactionSwapQuotes', ctx:authContext , req: request})
stats.handle = process.hrtime.bigint() stats.handle = process.hrtime.bigint()
res({status: 'OK', ...response}) res({status: 'OK', ...response})
opts.metricsCallback([{ ...info, ...stats, ...authContext }]) opts.metricsCallback([{ ...info, ...stats, ...authContext }])

View file

@ -7,8 +7,8 @@ export type RequestMetric = AuthContext & RequestInfo & RequestStats & { error?:
export type AdminContext = { export type AdminContext = {
admin_id: string admin_id: string
} }
export type AdminMethodInputs = AddApp_Input | AddPeer_Input | AuthApp_Input | BanUser_Input | CloseChannel_Input | CreateOneTimeInviteLink_Input | GetAdminTransactionSwapQuote_Input | GetInviteLinkState_Input | GetSeed_Input | ListAdminSwaps_Input | ListChannels_Input | LndGetInfo_Input | OpenChannel_Input | PayAdminTransactionSwap_Input | UpdateChannelPolicy_Input export type AdminMethodInputs = AddApp_Input | AddPeer_Input | AuthApp_Input | BanUser_Input | CloseChannel_Input | CreateOneTimeInviteLink_Input | GetAdminTransactionSwapQuotes_Input | GetInviteLinkState_Input | GetSeed_Input | ListAdminSwaps_Input | ListChannels_Input | LndGetInfo_Input | OpenChannel_Input | PayAdminTransactionSwap_Input | UpdateChannelPolicy_Input
export type AdminMethodOutputs = AddApp_Output | AddPeer_Output | AuthApp_Output | BanUser_Output | CloseChannel_Output | CreateOneTimeInviteLink_Output | GetAdminTransactionSwapQuote_Output | GetInviteLinkState_Output | GetSeed_Output | ListAdminSwaps_Output | ListChannels_Output | LndGetInfo_Output | OpenChannel_Output | PayAdminTransactionSwap_Output | UpdateChannelPolicy_Output export type AdminMethodOutputs = AddApp_Output | AddPeer_Output | AuthApp_Output | BanUser_Output | CloseChannel_Output | CreateOneTimeInviteLink_Output | GetAdminTransactionSwapQuotes_Output | GetInviteLinkState_Output | GetSeed_Output | ListAdminSwaps_Output | ListChannels_Output | LndGetInfo_Output | OpenChannel_Output | PayAdminTransactionSwap_Output | UpdateChannelPolicy_Output
export type AppContext = { export type AppContext = {
app_id: string app_id: string
} }
@ -35,8 +35,8 @@ export type UserContext = {
app_user_id: string app_user_id: string
user_id: string user_id: string
} }
export type UserMethodInputs = AddProduct_Input | AddUserOffer_Input | AuthorizeManage_Input | BanDebit_Input | DecodeInvoice_Input | DeleteUserOffer_Input | EditDebit_Input | EnrollAdminToken_Input | EnrollMessagingToken_Input | GetDebitAuthorizations_Input | GetHttpCreds_Input | GetLNURLChannelLink_Input | GetLnurlPayLink_Input | GetLnurlWithdrawLink_Input | GetManageAuthorizations_Input | GetPaymentState_Input | GetTransactionSwapQuote_Input | GetUserInfo_Input | GetUserOffer_Input | GetUserOfferInvoices_Input | GetUserOffers_Input | GetUserOperations_Input | ListSwaps_Input | NewAddress_Input | NewInvoice_Input | NewProductInvoice_Input | PayAddress_Input | PayInvoice_Input | ResetDebit_Input | ResetManage_Input | RespondToDebit_Input | UpdateCallbackUrl_Input | UpdateUserOffer_Input | UserHealth_Input export type UserMethodInputs = AddProduct_Input | AddUserOffer_Input | AuthorizeManage_Input | BanDebit_Input | DecodeInvoice_Input | DeleteUserOffer_Input | EditDebit_Input | EnrollAdminToken_Input | EnrollMessagingToken_Input | GetDebitAuthorizations_Input | GetHttpCreds_Input | GetLNURLChannelLink_Input | GetLnurlPayLink_Input | GetLnurlWithdrawLink_Input | GetManageAuthorizations_Input | GetPaymentState_Input | GetTransactionSwapQuotes_Input | GetUserInfo_Input | GetUserOffer_Input | GetUserOfferInvoices_Input | GetUserOffers_Input | GetUserOperations_Input | ListSwaps_Input | NewAddress_Input | NewInvoice_Input | NewProductInvoice_Input | PayAddress_Input | PayInvoice_Input | ResetDebit_Input | ResetManage_Input | RespondToDebit_Input | UpdateCallbackUrl_Input | UpdateUserOffer_Input | UserHealth_Input
export type UserMethodOutputs = AddProduct_Output | AddUserOffer_Output | AuthorizeManage_Output | BanDebit_Output | DecodeInvoice_Output | DeleteUserOffer_Output | EditDebit_Output | EnrollAdminToken_Output | EnrollMessagingToken_Output | GetDebitAuthorizations_Output | GetHttpCreds_Output | GetLNURLChannelLink_Output | GetLnurlPayLink_Output | GetLnurlWithdrawLink_Output | GetManageAuthorizations_Output | GetPaymentState_Output | GetTransactionSwapQuote_Output | GetUserInfo_Output | GetUserOffer_Output | GetUserOfferInvoices_Output | GetUserOffers_Output | GetUserOperations_Output | ListSwaps_Output | NewAddress_Output | NewInvoice_Output | NewProductInvoice_Output | PayAddress_Output | PayInvoice_Output | ResetDebit_Output | ResetManage_Output | RespondToDebit_Output | UpdateCallbackUrl_Output | UpdateUserOffer_Output | UserHealth_Output export type UserMethodOutputs = AddProduct_Output | AddUserOffer_Output | AuthorizeManage_Output | BanDebit_Output | DecodeInvoice_Output | DeleteUserOffer_Output | EditDebit_Output | EnrollAdminToken_Output | EnrollMessagingToken_Output | GetDebitAuthorizations_Output | GetHttpCreds_Output | GetLNURLChannelLink_Output | GetLnurlPayLink_Output | GetLnurlWithdrawLink_Output | GetManageAuthorizations_Output | GetPaymentState_Output | GetTransactionSwapQuotes_Output | GetUserInfo_Output | GetUserOffer_Output | GetUserOfferInvoices_Output | GetUserOffers_Output | GetUserOperations_Output | ListSwaps_Output | NewAddress_Output | NewInvoice_Output | NewProductInvoice_Output | PayAddress_Output | PayInvoice_Output | ResetDebit_Output | ResetManage_Output | RespondToDebit_Output | UpdateCallbackUrl_Output | UpdateUserOffer_Output | UserHealth_Output
export type AuthContext = AdminContext | AppContext | GuestContext | GuestWithPubContext | MetricsContext | UserContext export type AuthContext = AdminContext | AppContext | GuestContext | GuestWithPubContext | MetricsContext | UserContext
export type AddApp_Input = {rpcName:'AddApp', req: AddAppRequest} export type AddApp_Input = {rpcName:'AddApp', req: AddAppRequest}
@ -99,8 +99,8 @@ export type EnrollAdminToken_Output = ResultError | { status: 'OK' }
export type EnrollMessagingToken_Input = {rpcName:'EnrollMessagingToken', req: MessagingToken} export type EnrollMessagingToken_Input = {rpcName:'EnrollMessagingToken', req: MessagingToken}
export type EnrollMessagingToken_Output = ResultError | { status: 'OK' } export type EnrollMessagingToken_Output = ResultError | { status: 'OK' }
export type GetAdminTransactionSwapQuote_Input = {rpcName:'GetAdminTransactionSwapQuote', req: TransactionSwapRequest} export type GetAdminTransactionSwapQuotes_Input = {rpcName:'GetAdminTransactionSwapQuotes', req: TransactionSwapRequest}
export type GetAdminTransactionSwapQuote_Output = ResultError | ({ status: 'OK' } & TransactionSwapQuote) export type GetAdminTransactionSwapQuotes_Output = ResultError | ({ status: 'OK' } & TransactionSwapQuoteList)
export type GetApp_Input = {rpcName:'GetApp'} export type GetApp_Input = {rpcName:'GetApp'}
export type GetApp_Output = ResultError | ({ status: 'OK' } & Application) export type GetApp_Output = ResultError | ({ status: 'OK' } & Application)
@ -189,8 +189,8 @@ export type GetSingleBundleMetrics_Output = ResultError | ({ status: 'OK' } & Bu
export type GetSingleUsageMetrics_Input = {rpcName:'GetSingleUsageMetrics', req: SingleMetricReq} export type GetSingleUsageMetrics_Input = {rpcName:'GetSingleUsageMetrics', req: SingleMetricReq}
export type GetSingleUsageMetrics_Output = ResultError | ({ status: 'OK' } & UsageMetricTlv) export type GetSingleUsageMetrics_Output = ResultError | ({ status: 'OK' } & UsageMetricTlv)
export type GetTransactionSwapQuote_Input = {rpcName:'GetTransactionSwapQuote', req: TransactionSwapRequest} export type GetTransactionSwapQuotes_Input = {rpcName:'GetTransactionSwapQuotes', req: TransactionSwapRequest}
export type GetTransactionSwapQuote_Output = ResultError | ({ status: 'OK' } & TransactionSwapQuote) export type GetTransactionSwapQuotes_Output = ResultError | ({ status: 'OK' } & TransactionSwapQuoteList)
export type GetUsageMetrics_Input = {rpcName:'GetUsageMetrics', req: LatestUsageMetricReq} export type GetUsageMetrics_Input = {rpcName:'GetUsageMetrics', req: LatestUsageMetricReq}
export type GetUsageMetrics_Output = ResultError | ({ status: 'OK' } & UsageMetrics) export type GetUsageMetrics_Output = ResultError | ({ status: 'OK' } & UsageMetrics)
@ -357,7 +357,7 @@ export type ServerMethods = {
EncryptionExchange?: (req: EncryptionExchange_Input & {ctx: GuestContext }) => Promise<void> EncryptionExchange?: (req: EncryptionExchange_Input & {ctx: GuestContext }) => Promise<void>
EnrollAdminToken?: (req: EnrollAdminToken_Input & {ctx: UserContext }) => Promise<void> EnrollAdminToken?: (req: EnrollAdminToken_Input & {ctx: UserContext }) => Promise<void>
EnrollMessagingToken?: (req: EnrollMessagingToken_Input & {ctx: UserContext }) => Promise<void> EnrollMessagingToken?: (req: EnrollMessagingToken_Input & {ctx: UserContext }) => Promise<void>
GetAdminTransactionSwapQuote?: (req: GetAdminTransactionSwapQuote_Input & {ctx: AdminContext }) => Promise<TransactionSwapQuote> GetAdminTransactionSwapQuotes?: (req: GetAdminTransactionSwapQuotes_Input & {ctx: AdminContext }) => Promise<TransactionSwapQuoteList>
GetApp?: (req: GetApp_Input & {ctx: AppContext }) => Promise<Application> GetApp?: (req: GetApp_Input & {ctx: AppContext }) => Promise<Application>
GetAppUser?: (req: GetAppUser_Input & {ctx: AppContext }) => Promise<AppUser> GetAppUser?: (req: GetAppUser_Input & {ctx: AppContext }) => Promise<AppUser>
GetAppUserLNURLInfo?: (req: GetAppUserLNURLInfo_Input & {ctx: AppContext }) => Promise<LnurlPayInfoResponse> GetAppUserLNURLInfo?: (req: GetAppUserLNURLInfo_Input & {ctx: AppContext }) => Promise<LnurlPayInfoResponse>
@ -385,7 +385,7 @@ export type ServerMethods = {
GetSeed?: (req: GetSeed_Input & {ctx: AdminContext }) => Promise<LndSeed> GetSeed?: (req: GetSeed_Input & {ctx: AdminContext }) => Promise<LndSeed>
GetSingleBundleMetrics?: (req: GetSingleBundleMetrics_Input & {ctx: MetricsContext }) => Promise<BundleData> GetSingleBundleMetrics?: (req: GetSingleBundleMetrics_Input & {ctx: MetricsContext }) => Promise<BundleData>
GetSingleUsageMetrics?: (req: GetSingleUsageMetrics_Input & {ctx: MetricsContext }) => Promise<UsageMetricTlv> GetSingleUsageMetrics?: (req: GetSingleUsageMetrics_Input & {ctx: MetricsContext }) => Promise<UsageMetricTlv>
GetTransactionSwapQuote?: (req: GetTransactionSwapQuote_Input & {ctx: UserContext }) => Promise<TransactionSwapQuote> GetTransactionSwapQuotes?: (req: GetTransactionSwapQuotes_Input & {ctx: UserContext }) => Promise<TransactionSwapQuoteList>
GetUsageMetrics?: (req: GetUsageMetrics_Input & {ctx: MetricsContext }) => Promise<UsageMetrics> GetUsageMetrics?: (req: GetUsageMetrics_Input & {ctx: MetricsContext }) => Promise<UsageMetrics>
GetUserInfo?: (req: GetUserInfo_Input & {ctx: UserContext }) => Promise<UserInfo> GetUserInfo?: (req: GetUserInfo_Input & {ctx: UserContext }) => Promise<UserInfo>
GetUserOffer?: (req: GetUserOffer_Input & {ctx: UserContext }) => Promise<OfferConfig> GetUserOffer?: (req: GetUserOffer_Input & {ctx: UserContext }) => Promise<OfferConfig>
@ -3991,6 +3991,7 @@ export type TransactionSwapQuote = {
chain_fee_sats: number chain_fee_sats: number
invoice_amount_sats: number invoice_amount_sats: number
service_fee_sats: number service_fee_sats: number
service_url: string
swap_fee_sats: number swap_fee_sats: number
swap_operation_id: string swap_operation_id: string
transaction_amount_sats: number transaction_amount_sats: number
@ -4001,6 +4002,7 @@ export type TransactionSwapQuoteOptions = OptionsBaseMessage & {
chain_fee_sats_CustomCheck?: (v: number) => boolean chain_fee_sats_CustomCheck?: (v: number) => boolean
invoice_amount_sats_CustomCheck?: (v: number) => boolean invoice_amount_sats_CustomCheck?: (v: number) => boolean
service_fee_sats_CustomCheck?: (v: number) => boolean service_fee_sats_CustomCheck?: (v: number) => boolean
service_url_CustomCheck?: (v: string) => boolean
swap_fee_sats_CustomCheck?: (v: number) => boolean swap_fee_sats_CustomCheck?: (v: number) => boolean
swap_operation_id_CustomCheck?: (v: string) => boolean swap_operation_id_CustomCheck?: (v: string) => boolean
transaction_amount_sats_CustomCheck?: (v: number) => boolean transaction_amount_sats_CustomCheck?: (v: number) => boolean
@ -4018,6 +4020,9 @@ export const TransactionSwapQuoteValidate = (o?: TransactionSwapQuote, opts: Tra
if (typeof o.service_fee_sats !== 'number') return new Error(`${path}.service_fee_sats: is not a number`) if (typeof o.service_fee_sats !== 'number') return new Error(`${path}.service_fee_sats: is not a number`)
if (opts.service_fee_sats_CustomCheck && !opts.service_fee_sats_CustomCheck(o.service_fee_sats)) return new Error(`${path}.service_fee_sats: custom check failed`) if (opts.service_fee_sats_CustomCheck && !opts.service_fee_sats_CustomCheck(o.service_fee_sats)) return new Error(`${path}.service_fee_sats: custom check failed`)
if (typeof o.service_url !== 'string') return new Error(`${path}.service_url: is not a string`)
if (opts.service_url_CustomCheck && !opts.service_url_CustomCheck(o.service_url)) return new Error(`${path}.service_url: custom check failed`)
if (typeof o.swap_fee_sats !== 'number') return new Error(`${path}.swap_fee_sats: is not a number`) if (typeof o.swap_fee_sats !== 'number') return new Error(`${path}.swap_fee_sats: is not a number`)
if (opts.swap_fee_sats_CustomCheck && !opts.swap_fee_sats_CustomCheck(o.swap_fee_sats)) return new Error(`${path}.swap_fee_sats: custom check failed`) if (opts.swap_fee_sats_CustomCheck && !opts.swap_fee_sats_CustomCheck(o.swap_fee_sats)) return new Error(`${path}.swap_fee_sats: custom check failed`)
@ -4030,6 +4035,29 @@ export const TransactionSwapQuoteValidate = (o?: TransactionSwapQuote, opts: Tra
return null return null
} }
export type TransactionSwapQuoteList = {
quotes: TransactionSwapQuote[]
}
export const TransactionSwapQuoteListOptionalFields: [] = []
export type TransactionSwapQuoteListOptions = OptionsBaseMessage & {
checkOptionalsAreSet?: []
quotes_ItemOptions?: TransactionSwapQuoteOptions
quotes_CustomCheck?: (v: TransactionSwapQuote[]) => boolean
}
export const TransactionSwapQuoteListValidate = (o?: TransactionSwapQuoteList, opts: TransactionSwapQuoteListOptions = {}, path: string = 'TransactionSwapQuoteList::root.'): Error | null => {
if (opts.checkOptionalsAreSet && opts.allOptionalsAreSet) return new Error(path + ': only one of checkOptionalsAreSet or allOptionalNonDefault can be set for each message')
if (typeof o !== 'object' || o === null) return new Error(path + ': object is not an instance of an object or is null')
if (!Array.isArray(o.quotes)) return new Error(`${path}.quotes: is not an array`)
for (let index = 0; index < o.quotes.length; index++) {
const quotesErr = TransactionSwapQuoteValidate(o.quotes[index], opts.quotes_ItemOptions, `${path}.quotes[${index}]`)
if (quotesErr !== null) return quotesErr
}
if (opts.quotes_CustomCheck && !opts.quotes_CustomCheck(o.quotes)) return new Error(`${path}.quotes: custom check failed`)
return null
}
export type TransactionSwapRequest = { export type TransactionSwapRequest = {
transaction_amount_sats: number transaction_amount_sats: number
} }

View file

@ -175,7 +175,7 @@ service LightningPub {
option (nostr) = true; option (nostr) = true;
} }
rpc GetAdminTransactionSwapQuote(structs.TransactionSwapRequest) returns (structs.TransactionSwapQuote) { rpc GetAdminTransactionSwapQuotes(structs.TransactionSwapRequest) returns (structs.TransactionSwapQuoteList) {
option (auth_type) = "Admin"; option (auth_type) = "Admin";
option (http_method) = "post"; option (http_method) = "post";
option (http_route) = "/api/admin/swap/transaction/quote"; option (http_route) = "/api/admin/swap/transaction/quote";
@ -517,7 +517,7 @@ service LightningPub {
option (nostr) = true; option (nostr) = true;
} }
rpc GetTransactionSwapQuote(structs.TransactionSwapRequest) returns (structs.TransactionSwapQuote){ rpc GetTransactionSwapQuotes(structs.TransactionSwapRequest) returns (structs.TransactionSwapQuoteList){
option (auth_type) = "User"; option (auth_type) = "User";
option (http_method) = "post"; option (http_method) = "post";
option (http_route) = "/api/user/swap/quote"; option (http_route) = "/api/user/swap/quote";

View file

@ -850,6 +850,11 @@ message TransactionSwapQuote {
int64 swap_fee_sats = 4; int64 swap_fee_sats = 4;
int64 chain_fee_sats = 5; int64 chain_fee_sats = 5;
int64 service_fee_sats = 7; int64 service_fee_sats = 7;
string service_url = 8;
}
message TransactionSwapQuoteList {
repeated TransactionSwapQuote quotes = 1;
} }
message AdminSwapResponse { message AdminSwapResponse {

View file

@ -47,14 +47,23 @@ type TransactionSwapResponse = {
type TransactionSwapInfo = { destinationAddress: string, preimage: Buffer, keys: ECPairInterface, chainFee: number } type TransactionSwapInfo = { destinationAddress: string, preimage: Buffer, keys: ECPairInterface, chainFee: number }
export type TransactionSwapData = { createdResponse: TransactionSwapResponse, info: TransactionSwapInfo } export type TransactionSwapData = { createdResponse: TransactionSwapResponse, info: TransactionSwapInfo }
export class Swaps { export class Swaps {
reverseSwaps: ReverseSwaps settings: SettingsManager
submarineSwaps: SubmarineSwaps revSwappers: Record<string, ReverseSwaps>
// submarineSwaps: SubmarineSwaps
storage: Storage storage: Storage
lnd: LND lnd: LND
log = getLogger({ component: 'swaps' }) log = getLogger({ component: 'swaps' })
constructor(settings: SettingsManager, storage: Storage) { constructor(settings: SettingsManager, storage: Storage) {
this.reverseSwaps = new ReverseSwaps(settings) this.settings = settings
this.submarineSwaps = new SubmarineSwaps(settings) this.revSwappers = {}
const network = settings.getSettings().lndSettings.network
const { boltzHttpUrl, boltzWebSocketUrl, boltsHttpUrlAlt, boltsWebSocketUrlAlt } = settings.getSettings().swapsSettings
if (boltzHttpUrl && boltzWebSocketUrl) {
this.revSwappers[boltzHttpUrl] = new ReverseSwaps({ httpUrl: boltzHttpUrl, wsUrl: boltzWebSocketUrl, network })
}
if (boltsHttpUrlAlt && boltsWebSocketUrlAlt) {
this.revSwappers[boltsHttpUrlAlt] = new ReverseSwaps({ httpUrl: boltsHttpUrlAlt, wsUrl: boltsWebSocketUrlAlt, network })
}
this.storage = storage this.storage = storage
} }
@ -92,21 +101,45 @@ export class Swaps {
chain_fee_sats: s.chain_fee_sats, chain_fee_sats: s.chain_fee_sats,
service_fee_sats: serviceFee, service_fee_sats: serviceFee,
swap_fee_sats: s.swap_fee_sats, swap_fee_sats: s.swap_fee_sats,
service_url: s.service_url,
} }
}) })
} }
} }
GetTxSwapQuotes = async (appUserId: string, amt: number, getServiceFee: (decodedAmt: number) => number): Promise<Types.TransactionSwapQuote[]> => {
if (!this.settings.getSettings().swapsSettings.enableSwaps) {
throw new Error("Swaps are not enabled")
}
const swappers = Object.values(this.revSwappers)
if (swappers.length === 0) {
throw new Error("No swap services available")
}
const res = await Promise.allSettled(Object.values(this.revSwappers).map(sw => this.getTxSwapQuote(sw, appUserId, amt, getServiceFee)))
const failures: string[] = []
const success: Types.TransactionSwapQuote[] = []
for (const r of res) {
if (r.status === 'fulfilled') {
success.push(r.value)
} else {
failures.push(r.reason.message ? r.reason.message : r.reason.toString())
}
}
if (success.length === 0) {
throw new Error(failures.join("\n"))
}
return success
}
async GetTxSwapQuote(appUserId: string, amt: number, getServiceFee: (decodedAmt: number) => number): Promise<Types.TransactionSwapQuote> { private async getTxSwapQuote(swapper: ReverseSwaps, appUserId: string, amt: number, getServiceFee: (decodedAmt: number) => number): Promise<Types.TransactionSwapQuote> {
this.log("getting transaction swap quote") this.log("getting transaction swap quote")
const feesRes = await this.reverseSwaps.GetFees() const feesRes = await swapper.GetFees()
if (!feesRes.ok) { if (!feesRes.ok) {
throw new Error(feesRes.error) throw new Error(feesRes.error)
} }
const { claim, lockup } = feesRes.fees.minerFees const { claim, lockup } = feesRes.fees.minerFees
const minerFee = claim + lockup const minerFee = claim + lockup
const chainTotal = amt + minerFee const chainTotal = amt + minerFee
const res = await this.reverseSwaps.SwapTransaction(chainTotal) const res = await swapper.SwapTransaction(chainTotal)
if (!res.ok) { if (!res.ok) {
throw new Error(res.error) throw new Error(res.error)
} }
@ -128,6 +161,7 @@ export class Swaps {
preimage: res.preimage, preimage: res.preimage,
ephemeral_private_key: res.privKey, ephemeral_private_key: res.privKey,
ephemeral_public_key: res.pubkey, ephemeral_public_key: res.pubkey,
service_url: swapper.getHttpUrl(),
}) })
return { return {
swap_operation_id: newSwap.swap_operation_id, swap_operation_id: newSwap.swap_operation_id,
@ -136,10 +170,14 @@ export class Swaps {
transaction_amount_sats: amt, transaction_amount_sats: amt,
chain_fee_sats: minerFee, chain_fee_sats: minerFee,
service_fee_sats: serviceFee, service_fee_sats: serviceFee,
service_url: swapper.getHttpUrl(),
} }
} }
async PayAddrWithSwap(appUserId: string, swapOpId: string, address: string, payInvoice: (invoice: string, amt: number) => Promise<void>) { async PayAddrWithSwap(appUserId: string, swapOpId: string, address: string, payInvoice: (invoice: string, amt: number) => Promise<void>) {
if (!this.settings.getSettings().swapsSettings.enableSwaps) {
throw new Error("Swaps are not enabled")
}
this.log("paying address with swap", { appUserId, swapOpId, address }) this.log("paying address with swap", { appUserId, swapOpId, address })
if (!swapOpId) { if (!swapOpId) {
throw new Error("request a swap quote before paying an external address") throw new Error("request a swap quote before paying an external address")
@ -152,6 +190,10 @@ export class Swaps {
if (info.blockHeight >= txSwap.timeout_block_height) { if (info.blockHeight >= txSwap.timeout_block_height) {
throw new Error("swap timeout") throw new Error("swap timeout")
} }
const swapper = this.revSwappers[txSwap.service_url]
if (!swapper) {
throw new Error("swapper service not found")
}
const keys = this.GetKeys(txSwap.ephemeral_private_key) const keys = this.GetKeys(txSwap.ephemeral_private_key)
const data: TransactionSwapData = { const data: TransactionSwapData = {
createdResponse: { createdResponse: {
@ -172,7 +214,7 @@ export class Swaps {
} }
// the swap and the invoice payment are linked, swap will not start until the invoice payment is started, and will not complete once the invoice payment is completed // the swap and the invoice payment are linked, swap will not start until the invoice payment is started, and will not complete once the invoice payment is completed
let swapResult = { ok: false, error: "swap never completed" } as { ok: true, txId: string } | { ok: false, error: string } let swapResult = { ok: false, error: "swap never completed" } as { ok: true, txId: string } | { ok: false, error: string }
this.reverseSwaps.SubscribeToTransactionSwap(data, result => { swapper.SubscribeToTransactionSwap(data, result => {
swapResult = result swapResult = result
}) })
try { try {
@ -202,6 +244,289 @@ export class Swaps {
} }
} }
export class ReverseSwaps {
// settings: SettingsManager
private httpUrl: string
private wsUrl: string
log: PubLogger
private network: BTCNetwork
constructor({ httpUrl, wsUrl, network }: { httpUrl: string, wsUrl: string, network: BTCNetwork }) {
this.httpUrl = httpUrl
this.wsUrl = wsUrl
this.network = network
this.log = getLogger({ component: 'ReverseSwaps' })
initEccLib(ecc)
}
getHttpUrl = () => {
return this.httpUrl
}
getWsUrl = () => {
return this.wsUrl
}
calculateFees = (fees: TransactionSwapFees, receiveAmount: number) => {
const pct = fees.percentage / 100
const minerFee = fees.minerFees.claim + fees.minerFees.lockup
const preFee = receiveAmount + minerFee
const fee = Math.ceil(preFee * pct)
const total = preFee + fee
return { total, fee, minerFee }
}
GetFees = async (): Promise<{ ok: true, fees: TransactionSwapFees, } | { ok: false, error: string }> => {
const url = `${this.httpUrl}/v2/swap/reverse`
const feesRes = await loggedGet<TransactionSwapFeesRes>(this.log, url)
if (!feesRes.ok) {
return { ok: false, error: feesRes.error }
}
if (!feesRes.data.BTC?.BTC?.fees) {
return { ok: false, error: 'No fees found for BTC to BTC swap' }
}
return { ok: true, fees: feesRes.data.BTC.BTC.fees }
}
SwapTransaction = async (txAmount: number): Promise<{ ok: true, createdResponse: TransactionSwapResponse, preimage: string, pubkey: string, privKey: string } | { ok: false, error: string }> => {
const preimage = randomBytes(32);
const keys = ECPairFactory(ecc).makeRandom()
if (!keys.privateKey) {
return { ok: false, error: 'Failed to generate keys' }
}
const url = `${this.httpUrl}/v2/swap/reverse`
const req: any = {
onchainAmount: txAmount,
to: 'BTC',
from: 'BTC',
claimPublicKey: Buffer.from(keys.publicKey).toString('hex'),
preimageHash: createHash('sha256').update(preimage).digest('hex'),
}
const createdResponseRes = await loggedPost<TransactionSwapResponse>(this.log, url, req)
if (!createdResponseRes.ok) {
return createdResponseRes
}
const createdResponse = createdResponseRes.data
this.log('Created transaction swap');
this.log(createdResponse);
return {
ok: true, createdResponse,
preimage: Buffer.from(preimage).toString('hex'),
pubkey: Buffer.from(keys.publicKey).toString('hex'),
privKey: Buffer.from(keys.privateKey).toString('hex')
}
}
SubscribeToTransactionSwap = async (data: TransactionSwapData, swapDone: (result: { ok: true, txId: string } | { ok: false, error: string }) => void) => {
const webSocket = new ws(`${this.wsUrl}/v2/ws`)
const subReq = { op: 'subscribe', channel: 'swap.update', args: [data.createdResponse.id] }
webSocket.on('open', () => {
webSocket.send(JSON.stringify(subReq))
})
let txId = "", isDone = false
const done = () => {
isDone = true
webSocket.close()
swapDone({ ok: true, txId })
}
webSocket.on('error', (err) => {
this.log(ERROR, 'Error in WebSocket', err.message)
})
webSocket.on('close', () => {
if (!isDone) {
this.log(ERROR, 'WebSocket closed before swap was done');
swapDone({ ok: false, error: 'WebSocket closed before swap was done' })
}
})
webSocket.on('message', async (rawMsg) => {
try {
const result = await this.handleSwapTransactionMessage(rawMsg, data, done)
if (result) {
txId = result
}
} catch (err: any) {
this.log(ERROR, 'Error handling transaction WebSocket message', err.message)
isDone = true
webSocket.close()
swapDone({ ok: false, error: err.message })
return
}
})
}
handleSwapTransactionMessage = async (rawMsg: ws.RawData, data: TransactionSwapData, done: () => void) => {
const msg = JSON.parse(rawMsg.toString('utf-8'));
if (msg.event !== 'update') {
return;
}
this.log('Got WebSocket update');
this.log(msg);
switch (msg.args[0].status) {
// "swap.created" means Boltz is waiting for the invoice to be paid
case 'swap.created':
this.log('Waiting invoice to be paid');
return;
// "transaction.mempool" means that Boltz sent an onchain transaction
case 'transaction.mempool':
const txIdRes = await this.handleTransactionMempool(data, msg.args[0].transaction.hex)
if (!txIdRes.ok) {
throw new Error(txIdRes.error)
}
return txIdRes.txId
case 'invoice.settled':
this.log('Transaction swap successful');
done()
return;
}
}
handleTransactionMempool = async (data: TransactionSwapData, txHex: string): Promise<{ ok: true, txId: string } | { ok: false, error: string }> => {
this.log('Creating claim transaction');
const { createdResponse, info } = data
const { destinationAddress, keys, preimage, chainFee } = info
const boltzPublicKey = Buffer.from(
createdResponse.refundPublicKey,
'hex',
);
// Create a musig signing session and tweak it with the Taptree of the swap scripts
const musig = new Musig(await zkpInit(), keys, randomBytes(32), [
boltzPublicKey,
Buffer.from(keys.publicKey),
]);
const tweakedKey = TaprootUtils.tweakMusig(
musig,
// swap tree can either be a string or an object
SwapTreeSerializer.deserializeSwapTree(createdResponse.swapTree).tree,
);
// Parse the lockup transaction and find the output relevant for the swap
const lockupTx = Transaction.fromHex(txHex);
const swapOutput = detectSwap(tweakedKey, lockupTx);
if (swapOutput === undefined) {
this.log(ERROR, 'No swap output found in lockup transaction');
return { ok: false, error: 'No swap output found in lockup transaction' }
}
const network = getNetwork(this.network)
// Create a claim transaction to be signed cooperatively via a key path spend
const claimTx = constructClaimTransaction(
[
{
...swapOutput,
keys,
preimage,
cooperative: true,
type: OutputType.Taproot,
txHash: lockupTx.getHash(),
},
],
address.toOutputScript(destinationAddress, network),
chainFee,
)
// Get the partial signature from Boltz
const claimUrl = `${this.httpUrl}/v2/swap/reverse/${createdResponse.id}/claim`
const claimReq = {
index: 0,
transaction: claimTx.toHex(),
preimage: preimage.toString('hex'),
pubNonce: Buffer.from(musig.getPublicNonce()).toString('hex'),
}
const boltzSigRes = await loggedPost<{ pubNonce: string, partialSignature: string }>(this.log, claimUrl, claimReq)
if (!boltzSigRes.ok) {
return boltzSigRes
}
const boltzSig = boltzSigRes.data
// Aggregate the nonces
musig.aggregateNonces([
[boltzPublicKey, Buffer.from(boltzSig.pubNonce, 'hex')],
]);
// Initialize the session to sign the claim transaction
musig.initializeSession(
claimTx.hashForWitnessV1(
0,
[swapOutput.script],
[swapOutput.value],
Transaction.SIGHASH_DEFAULT,
),
);
// Add the partial signature from Boltz
musig.addPartial(
boltzPublicKey,
Buffer.from(boltzSig.partialSignature, 'hex'),
);
// Create our partial signature
musig.signPartial();
// Witness of the input to the aggregated signature
claimTx.ins[0].witness = [musig.aggregatePartials()];
// Broadcast the finalized transaction
const broadcastUrl = `${this.httpUrl}/v2/chain/BTC/transaction`
const broadcastReq = {
hex: claimTx.toHex(),
}
const broadcastResponse = await loggedPost<any>(this.log, broadcastUrl, broadcastReq)
if (!broadcastResponse.ok) {
return broadcastResponse
}
this.log('Transaction broadcasted', broadcastResponse.data)
const txId = claimTx.getId()
return { ok: true, txId }
}
}
const loggedPost = async <T>(log: PubLogger, url: string, req: any): Promise<{ ok: true, data: T } | { ok: false, error: string }> => {
try {
const { data } = await axios.post(url, req)
return { ok: true, data: data as T }
} catch (err: any) {
if (err.response?.data) {
log(ERROR, 'Error sending request', err.response.data)
return { ok: false, error: JSON.stringify(err.response.data) }
}
log(ERROR, 'Error sending request', err.message)
return { ok: false, error: err.message }
}
}
const loggedGet = async <T>(log: PubLogger, url: string): Promise<{ ok: true, data: T } | { ok: false, error: string }> => {
try {
const { data } = await axios.get(url)
return { ok: true, data: data as T }
} catch (err: any) {
if (err.response?.data) {
log(ERROR, 'Error getting request', err.response.data)
return { ok: false, error: err.response.data }
}
log(ERROR, 'Error getting request', err.message)
return { ok: false, error: err.message }
}
}
const getNetwork = (network: BTCNetwork): Network => {
switch (network) {
case 'mainnet':
return Networks.bitcoinMainnet
case 'testnet':
return Networks.bitcoinTestnet
case 'regtest':
return Networks.bitcoinRegtest
default:
throw new Error(`Invalid network: ${network}`)
}
}
// Submarine swaps currently not supported, keeping the code for future reference
/*
export class SubmarineSwaps { export class SubmarineSwaps {
settings: SettingsManager settings: SettingsManager
log: PubLogger log: PubLogger
@ -329,274 +654,4 @@ export class SubmarineSwaps {
this.log('Claim response', claimResponse) this.log('Claim response', claimResponse)
} }
} }
export class ReverseSwaps { */
settings: SettingsManager
log: PubLogger
constructor(settings: SettingsManager) {
this.settings = settings
this.log = getLogger({ component: 'ReverseSwaps' })
initEccLib(ecc)
}
calculateFees = (fees: TransactionSwapFees, receiveAmount: number) => {
const pct = fees.percentage / 100
const minerFee = fees.minerFees.claim + fees.minerFees.lockup
const preFee = receiveAmount + minerFee
const fee = Math.ceil(preFee * pct)
const total = preFee + fee
return { total, fee, minerFee }
}
GetFees = async (): Promise<{ ok: true, fees: TransactionSwapFees, } | { ok: false, error: string }> => {
const url = `${this.settings.getSettings().swapsSettings.boltzHttpUrl}/v2/swap/reverse`
const feesRes = await loggedGet<TransactionSwapFeesRes>(this.log, url)
if (!feesRes.ok) {
return { ok: false, error: feesRes.error }
}
if (!feesRes.data.BTC?.BTC?.fees) {
return { ok: false, error: 'No fees found for BTC to BTC swap' }
}
return { ok: true, fees: feesRes.data.BTC.BTC.fees }
}
SwapTransaction = async (txAmount: number): Promise<{ ok: true, createdResponse: TransactionSwapResponse, preimage: string, pubkey: string, privKey: string } | { ok: false, error: string }> => {
if (!this.settings.getSettings().swapsSettings.enableSwaps) {
this.log(ERROR, 'Swaps are not enabled');
return { ok: false, error: 'Swaps are not enabled' }
}
const preimage = randomBytes(32);
const keys = ECPairFactory(ecc).makeRandom()
if (!keys.privateKey) {
return { ok: false, error: 'Failed to generate keys' }
}
const url = `${this.settings.getSettings().swapsSettings.boltzHttpUrl}/v2/swap/reverse`
const req: any = {
onchainAmount: txAmount,
to: 'BTC',
from: 'BTC',
claimPublicKey: Buffer.from(keys.publicKey).toString('hex'),
preimageHash: createHash('sha256').update(preimage).digest('hex'),
}
const createdResponseRes = await loggedPost<TransactionSwapResponse>(this.log, url, req)
if (!createdResponseRes.ok) {
return createdResponseRes
}
const createdResponse = createdResponseRes.data
this.log('Created transaction swap');
this.log(createdResponse);
return {
ok: true, createdResponse,
preimage: Buffer.from(preimage).toString('hex'),
pubkey: Buffer.from(keys.publicKey).toString('hex'),
privKey: Buffer.from(keys.privateKey).toString('hex')
}
}
SubscribeToTransactionSwap = async (data: TransactionSwapData, swapDone: (result: { ok: true, txId: string } | { ok: false, error: string }) => void) => {
const webSocket = new ws(`${this.settings.getSettings().swapsSettings.boltzWebSocketUrl}/v2/ws`)
const subReq = { op: 'subscribe', channel: 'swap.update', args: [data.createdResponse.id] }
webSocket.on('open', () => {
webSocket.send(JSON.stringify(subReq))
})
let txId = "", isDone = false
const done = () => {
isDone = true
webSocket.close()
swapDone({ ok: true, txId })
}
webSocket.on('error', (err) => {
this.log(ERROR, 'Error in WebSocket', err.message)
})
webSocket.on('close', () => {
if (!isDone) {
this.log(ERROR, 'WebSocket closed before swap was done');
swapDone({ ok: false, error: 'WebSocket closed before swap was done' })
}
})
webSocket.on('message', async (rawMsg) => {
try {
const result = await this.handleSwapTransactionMessage(rawMsg, data, done)
if (result) {
txId = result
}
} catch (err: any) {
this.log(ERROR, 'Error handling transaction WebSocket message', err.message)
isDone = true
webSocket.close()
swapDone({ ok: false, error: err.message })
return
}
})
}
handleSwapTransactionMessage = async (rawMsg: ws.RawData, data: TransactionSwapData, done: () => void) => {
const msg = JSON.parse(rawMsg.toString('utf-8'));
if (msg.event !== 'update') {
return;
}
this.log('Got WebSocket update');
this.log(msg);
switch (msg.args[0].status) {
// "swap.created" means Boltz is waiting for the invoice to be paid
case 'swap.created':
this.log('Waiting invoice to be paid');
return;
// "transaction.mempool" means that Boltz sent an onchain transaction
case 'transaction.mempool':
const txIdRes = await this.handleTransactionMempool(data, msg.args[0].transaction.hex)
if (!txIdRes.ok) {
throw new Error(txIdRes.error)
}
return txIdRes.txId
case 'invoice.settled':
this.log('Transaction swap successful');
done()
return;
}
}
handleTransactionMempool = async (data: TransactionSwapData, txHex: string): Promise<{ ok: true, txId: string } | { ok: false, error: string }> => {
this.log('Creating claim transaction');
const { createdResponse, info } = data
const { destinationAddress, keys, preimage, chainFee } = info
const boltzPublicKey = Buffer.from(
createdResponse.refundPublicKey,
'hex',
);
// Create a musig signing session and tweak it with the Taptree of the swap scripts
const musig = new Musig(await zkpInit(), keys, randomBytes(32), [
boltzPublicKey,
Buffer.from(keys.publicKey),
]);
const tweakedKey = TaprootUtils.tweakMusig(
musig,
// swap tree can either be a string or an object
SwapTreeSerializer.deserializeSwapTree(createdResponse.swapTree).tree,
);
// Parse the lockup transaction and find the output relevant for the swap
const lockupTx = Transaction.fromHex(txHex);
const swapOutput = detectSwap(tweakedKey, lockupTx);
if (swapOutput === undefined) {
this.log(ERROR, 'No swap output found in lockup transaction');
return { ok: false, error: 'No swap output found in lockup transaction' }
}
const network = getNetwork(this.settings.getSettings().lndSettings.network)
// Create a claim transaction to be signed cooperatively via a key path spend
const claimTx = constructClaimTransaction(
[
{
...swapOutput,
keys,
preimage,
cooperative: true,
type: OutputType.Taproot,
txHash: lockupTx.getHash(),
},
],
address.toOutputScript(destinationAddress, network),
chainFee,
)
const { boltzHttpUrl } = this.settings.getSettings().swapsSettings
// Get the partial signature from Boltz
const claimUrl = `${boltzHttpUrl}/v2/swap/reverse/${createdResponse.id}/claim`
const claimReq = {
index: 0,
transaction: claimTx.toHex(),
preimage: preimage.toString('hex'),
pubNonce: Buffer.from(musig.getPublicNonce()).toString('hex'),
}
const boltzSigRes = await loggedPost<{ pubNonce: string, partialSignature: string }>(this.log, claimUrl, claimReq)
if (!boltzSigRes.ok) {
return boltzSigRes
}
const boltzSig = boltzSigRes.data
// Aggregate the nonces
musig.aggregateNonces([
[boltzPublicKey, Buffer.from(boltzSig.pubNonce, 'hex')],
]);
// Initialize the session to sign the claim transaction
musig.initializeSession(
claimTx.hashForWitnessV1(
0,
[swapOutput.script],
[swapOutput.value],
Transaction.SIGHASH_DEFAULT,
),
);
// Add the partial signature from Boltz
musig.addPartial(
boltzPublicKey,
Buffer.from(boltzSig.partialSignature, 'hex'),
);
// Create our partial signature
musig.signPartial();
// Witness of the input to the aggregated signature
claimTx.ins[0].witness = [musig.aggregatePartials()];
// Broadcast the finalized transaction
const broadcastUrl = `${boltzHttpUrl}/v2/chain/BTC/transaction`
const broadcastReq = {
hex: claimTx.toHex(),
}
const broadcastResponse = await loggedPost<any>(this.log, broadcastUrl, broadcastReq)
if (!broadcastResponse.ok) {
return broadcastResponse
}
this.log('Transaction broadcasted', broadcastResponse.data)
const txId = claimTx.getId()
return { ok: true, txId }
}
}
const loggedPost = async <T>(log: PubLogger, url: string, req: any): Promise<{ ok: true, data: T } | { ok: false, error: string }> => {
try {
const { data } = await axios.post(url, req)
return { ok: true, data: data as T }
} catch (err: any) {
if (err.response?.data) {
log(ERROR, 'Error sending request', err.response.data)
return { ok: false, error: err.response.data }
}
log(ERROR, 'Error sending request', err.message)
return { ok: false, error: err.message }
}
}
const loggedGet = async <T>(log: PubLogger, url: string): Promise<{ ok: true, data: T } | { ok: false, error: string }> => {
try {
const { data } = await axios.get(url)
return { ok: true, data: data as T }
} catch (err: any) {
if (err.response?.data) {
log(ERROR, 'Error getting request', err.response.data)
return { ok: false, error: err.response.data }
}
log(ERROR, 'Error getting request', err.message)
return { ok: false, error: err.message }
}
}
const getNetwork = (network: BTCNetwork): Network => {
switch (network) {
case 'mainnet':
return Networks.bitcoinMainnet
case 'testnet':
return Networks.bitcoinTestnet
case 'regtest':
return Networks.bitcoinRegtest
default:
throw new Error(`Invalid network: ${network}`)
}
}

View file

@ -264,8 +264,9 @@ export class AdminManager {
return this.swaps.ListSwaps("admin", [], p => undefined, amt => 0) return this.swaps.ListSwaps("admin", [], p => undefined, amt => 0)
} }
async GetAdminTransactionSwapQuote(req: Types.TransactionSwapRequest): Promise<Types.TransactionSwapQuote> { async GetAdminTransactionSwapQuotes(req: Types.TransactionSwapRequest): Promise<Types.TransactionSwapQuoteList> {
return this.swaps.GetTxSwapQuote("admin", req.transaction_amount_sats, () => 0) const quotes = await this.swaps.GetTxSwapQuotes("admin", req.transaction_amount_sats, () => 0)
return { quotes }
} }
async PayAdminTransactionSwap(req: Types.PayAdminTransactionSwapRequest): Promise<Types.AdminSwapResponse> { async PayAdminTransactionSwap(req: Types.PayAdminTransactionSwapRequest): Promise<Types.AdminSwapResponse> {
const routingFloor = this.settings.getSettings().lndSettings.routingFeeFloor const routingFloor = this.settings.getSettings().lndSettings.routingFeeFloor

View file

@ -541,12 +541,13 @@ export default class {
async GetTransactionSwapQuote(ctx: Types.UserContext, req: Types.TransactionSwapRequest): Promise<Types.TransactionSwapQuote> { async GetTransactionSwapQuotes(ctx: Types.UserContext, req: Types.TransactionSwapRequest): Promise<Types.TransactionSwapQuoteList> {
const app = await this.storage.applicationStorage.GetApplication(ctx.app_id) const app = await this.storage.applicationStorage.GetApplication(ctx.app_id)
return this.swaps.GetTxSwapQuote(ctx.app_user_id, req.transaction_amount_sats, decodedAmt => { const quotes = await this.swaps.GetTxSwapQuotes(ctx.app_user_id, req.transaction_amount_sats, decodedAmt => {
const isManagedUser = ctx.user_id !== app.owner.user_id const isManagedUser = ctx.user_id !== app.owner.user_id
return this.getSendServiceFee(Types.UserOperationType.OUTGOING_INVOICE, decodedAmt, isManagedUser) return this.getSendServiceFee(Types.UserOperationType.OUTGOING_INVOICE, decodedAmt, isManagedUser)
}) })
return { quotes }
} }
async PayAddress(ctx: Types.UserContext, req: Types.PayAddressRequest): Promise<Types.PayAddressResponse> { async PayAddress(ctx: Types.UserContext, req: Types.PayAddressRequest): Promise<Types.PayAddressResponse> {

View file

@ -196,6 +196,8 @@ const decodeNprofile = (nprofile: string) => {
export type SwapsSettings = { export type SwapsSettings = {
boltzHttpUrl: string boltzHttpUrl: string
boltzWebSocketUrl: string boltzWebSocketUrl: string
boltsHttpUrlAlt: string
boltsWebSocketUrlAlt: string
enableSwaps: boolean enableSwaps: boolean
} }
@ -203,6 +205,8 @@ export const LoadSwapsSettingsFromEnv = (dbEnv: Record<string, string | undefine
return { return {
boltzHttpUrl: chooseEnv("BOLTZ_HTTP_URL", dbEnv, "https://swaps.zeuslsp.com/api", addToDb), boltzHttpUrl: chooseEnv("BOLTZ_HTTP_URL", dbEnv, "https://swaps.zeuslsp.com/api", addToDb),
boltzWebSocketUrl: chooseEnv("BOLTZ_WEBSOCKET_URL", dbEnv, "wss://swaps.zeuslsp.com/api", addToDb), boltzWebSocketUrl: chooseEnv("BOLTZ_WEBSOCKET_URL", dbEnv, "wss://swaps.zeuslsp.com/api", addToDb),
boltsHttpUrlAlt: chooseEnv("BOLTZ_HTTP_URL_ALT", dbEnv, "https://api.boltz.exchange/", addToDb),
boltsWebSocketUrlAlt: chooseEnv("BOLTZ_WEBSOCKET_URL_ALT", dbEnv, "wss://api.boltz.exchange/", addToDb),
enableSwaps: chooseEnvBool("ENABLE_SWAPS", dbEnv, false, addToDb) enableSwaps: chooseEnvBool("ENABLE_SWAPS", dbEnv, false, addToDb)
} }
} }

View file

@ -91,12 +91,12 @@ export default (mainHandler: Main): Types.ServerMethods => {
if (err != null) throw new Error(err.message) if (err != null) throw new Error(err.message)
return mainHandler.adminManager.CloseChannel(req) return mainHandler.adminManager.CloseChannel(req)
}, },
GetAdminTransactionSwapQuote: async ({ ctx, req }) => { GetAdminTransactionSwapQuotes: async ({ ctx, req }) => {
const err = Types.TransactionSwapRequestValidate(req, { const err = Types.TransactionSwapRequestValidate(req, {
transaction_amount_sats_CustomCheck: amt => amt > 0 transaction_amount_sats_CustomCheck: amt => amt > 0
}) })
if (err != null) throw new Error(err.message) if (err != null) throw new Error(err.message)
return mainHandler.adminManager.GetAdminTransactionSwapQuote(req) return mainHandler.adminManager.GetAdminTransactionSwapQuotes(req)
}, },
PayAdminTransactionSwap: async ({ ctx, req }) => { PayAdminTransactionSwap: async ({ ctx, req }) => {
const err = Types.PayAdminTransactionSwapRequestValidate(req, { const err = Types.PayAdminTransactionSwapRequestValidate(req, {
@ -168,8 +168,8 @@ export default (mainHandler: Main): Types.ServerMethods => {
ListSwaps: async ({ ctx }) => { ListSwaps: async ({ ctx }) => {
return mainHandler.paymentManager.ListSwaps(ctx) return mainHandler.paymentManager.ListSwaps(ctx)
}, },
GetTransactionSwapQuote: async ({ ctx, req }) => { GetTransactionSwapQuotes: async ({ ctx, req }) => {
return mainHandler.paymentManager.GetTransactionSwapQuote(ctx, req) return mainHandler.paymentManager.GetTransactionSwapQuotes(ctx, req)
}, },
NewInvoice: ({ ctx, req }) => mainHandler.appUserManager.NewInvoice(ctx, req), NewInvoice: ({ ctx, req }) => mainHandler.appUserManager.NewInvoice(ctx, req),
DecodeInvoice: async ({ ctx, req }) => { DecodeInvoice: async ({ ctx, req }) => {

View file

@ -63,6 +63,9 @@ export class TransactionSwap {
@Column({ default: "" }) @Column({ default: "" })
address_paid: string address_paid: string
@Column({ default: "" })
service_url: string
@CreateDateColumn() @CreateDateColumn()
created_at: Date created_at: Date

View file

@ -0,0 +1,20 @@
import { MigrationInterface, QueryRunner } from "typeorm";
export class SwapsServiceUrl1768413055036 implements MigrationInterface {
name = 'SwapsServiceUrl1768413055036'
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`CREATE TABLE "temporary_transaction_swap" ("swap_operation_id" varchar PRIMARY KEY NOT NULL, "app_user_id" varchar NOT NULL, "swap_quote_id" varchar NOT NULL, "swap_tree" varchar NOT NULL, "lockup_address" varchar NOT NULL, "refund_public_key" varchar NOT NULL, "timeout_block_height" integer NOT NULL, "invoice" varchar NOT NULL, "invoice_amount" integer NOT NULL, "transaction_amount" integer NOT NULL, "swap_fee_sats" integer NOT NULL, "chain_fee_sats" integer NOT NULL, "preimage" varchar NOT NULL, "ephemeral_public_key" varchar NOT NULL, "ephemeral_private_key" varchar NOT NULL, "used" boolean NOT NULL DEFAULT (0), "failure_reason" varchar NOT NULL DEFAULT (''), "tx_id" varchar NOT NULL DEFAULT (''), "created_at" datetime NOT NULL DEFAULT (datetime('now')), "updated_at" datetime NOT NULL DEFAULT (datetime('now')), "address_paid" varchar NOT NULL DEFAULT (''), "service_url" varchar NOT NULL DEFAULT (''))`);
await queryRunner.query(`INSERT INTO "temporary_transaction_swap"("swap_operation_id", "app_user_id", "swap_quote_id", "swap_tree", "lockup_address", "refund_public_key", "timeout_block_height", "invoice", "invoice_amount", "transaction_amount", "swap_fee_sats", "chain_fee_sats", "preimage", "ephemeral_public_key", "ephemeral_private_key", "used", "failure_reason", "tx_id", "created_at", "updated_at", "address_paid") SELECT "swap_operation_id", "app_user_id", "swap_quote_id", "swap_tree", "lockup_address", "refund_public_key", "timeout_block_height", "invoice", "invoice_amount", "transaction_amount", "swap_fee_sats", "chain_fee_sats", "preimage", "ephemeral_public_key", "ephemeral_private_key", "used", "failure_reason", "tx_id", "created_at", "updated_at", "address_paid" FROM "transaction_swap"`);
await queryRunner.query(`DROP TABLE "transaction_swap"`);
await queryRunner.query(`ALTER TABLE "temporary_transaction_swap" RENAME TO "transaction_swap"`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "transaction_swap" RENAME TO "temporary_transaction_swap"`);
await queryRunner.query(`CREATE TABLE "transaction_swap" ("swap_operation_id" varchar PRIMARY KEY NOT NULL, "app_user_id" varchar NOT NULL, "swap_quote_id" varchar NOT NULL, "swap_tree" varchar NOT NULL, "lockup_address" varchar NOT NULL, "refund_public_key" varchar NOT NULL, "timeout_block_height" integer NOT NULL, "invoice" varchar NOT NULL, "invoice_amount" integer NOT NULL, "transaction_amount" integer NOT NULL, "swap_fee_sats" integer NOT NULL, "chain_fee_sats" integer NOT NULL, "preimage" varchar NOT NULL, "ephemeral_public_key" varchar NOT NULL, "ephemeral_private_key" varchar NOT NULL, "used" boolean NOT NULL DEFAULT (0), "failure_reason" varchar NOT NULL DEFAULT (''), "tx_id" varchar NOT NULL DEFAULT (''), "created_at" datetime NOT NULL DEFAULT (datetime('now')), "updated_at" datetime NOT NULL DEFAULT (datetime('now')), "address_paid" varchar NOT NULL DEFAULT (''))`);
await queryRunner.query(`INSERT INTO "transaction_swap"("swap_operation_id", "app_user_id", "swap_quote_id", "swap_tree", "lockup_address", "refund_public_key", "timeout_block_height", "invoice", "invoice_amount", "transaction_amount", "swap_fee_sats", "chain_fee_sats", "preimage", "ephemeral_public_key", "ephemeral_private_key", "used", "failure_reason", "tx_id", "created_at", "updated_at", "address_paid") SELECT "swap_operation_id", "app_user_id", "swap_quote_id", "swap_tree", "lockup_address", "refund_public_key", "timeout_block_height", "invoice", "invoice_amount", "transaction_amount", "swap_fee_sats", "chain_fee_sats", "preimage", "ephemeral_public_key", "ephemeral_private_key", "used", "failure_reason", "tx_id", "created_at", "updated_at", "address_paid" FROM "temporary_transaction_swap"`);
await queryRunner.query(`DROP TABLE "temporary_transaction_swap"`);
}
}

View file

@ -31,13 +31,15 @@ import { TxSwap1762890527098 } from './1762890527098-tx_swap.js'
import { TxSwapAddress1764779178945 } from './1764779178945-tx_swap_address.js' import { TxSwapAddress1764779178945 } from './1764779178945-tx_swap_address.js'
import { ClinkRequester1765497600000 } from './1765497600000-clink_requester.js' import { ClinkRequester1765497600000 } from './1765497600000-clink_requester.js'
import { TrackedProviderHeight1766504040000 } from './1766504040000-tracked_provider_height.js' import { TrackedProviderHeight1766504040000 } from './1766504040000-tracked_provider_height.js'
import { SwapsServiceUrl1768413055036 } from './1768413055036-swaps_service_url.js'
export const allMigrations = [Initial1703170309875, LspOrder1718387847693, LiquidityProvider1719335699480, LndNodeInfo1720187506189, export const allMigrations = [Initial1703170309875, LspOrder1718387847693, LiquidityProvider1719335699480, LndNodeInfo1720187506189,
TrackedProvider1720814323679, CreateInviteTokenTable1721751414878, PaymentIndex1721760297610, DebitAccess1726496225078, DebitAccessFixes1726685229264, TrackedProvider1720814323679, CreateInviteTokenTable1721751414878, PaymentIndex1721760297610, DebitAccess1726496225078, DebitAccessFixes1726685229264,
DebitToPub1727105758354, UserCbUrl1727112281043, UserOffer1733502626042, ManagementGrant1751307732346, ManagementGrantBanned1751989251513, DebitToPub1727105758354, UserCbUrl1727112281043, UserOffer1733502626042, ManagementGrant1751307732346, ManagementGrantBanned1751989251513,
InvoiceCallbackUrls1752425992291, OldSomethingLeftover1753106599604, UserReceivingInvoiceIdx1753109184611, AppUserDevice1753285173175, InvoiceCallbackUrls1752425992291, OldSomethingLeftover1753106599604, UserReceivingInvoiceIdx1753109184611, AppUserDevice1753285173175,
UserAccess1759426050669, AddBlindToUserOffer1760000000000, ApplicationAvatarUrl1761000001000, AdminSettings1761683639419, TxSwap1762890527098, TxSwapAddress1764779178945, ClinkRequester1765497600000, TrackedProviderHeight1766504040000] UserAccess1759426050669, AddBlindToUserOffer1760000000000, ApplicationAvatarUrl1761000001000, AdminSettings1761683639419, TxSwap1762890527098,
TxSwapAddress1764779178945, ClinkRequester1765497600000, TrackedProviderHeight1766504040000, SwapsServiceUrl1768413055036]
export const allMetricsMigrations = [LndMetrics1703170330183, ChannelRouting1709316653538, HtlcCount1724266887195, BalanceEvents1724860966825, RootOps1732566440447, RootOpsTime1745428134124, ChannelEvents1750777346411] export const allMetricsMigrations = [LndMetrics1703170330183, ChannelRouting1709316653538, HtlcCount1724266887195, BalanceEvents1724860966825, RootOps1732566440447, RootOpsTime1745428134124, ChannelEvents1750777346411]
/* export const TypeOrmMigrationRunner = async (log: PubLogger, storageManager: Storage, settings: DbSettings, arg: string | undefined): Promise<boolean> => { /* export const TypeOrmMigrationRunner = async (log: PubLogger, storageManager: Storage, settings: DbSettings, arg: string | undefined): Promise<boolean> => {