diff --git a/example.env b/example.env index be980418..92d7cdc0 100644 --- a/example.env +++ b/example.env @@ -2,4 +2,10 @@ LND_ADDRESS= LND_CERT_PATH= LND_MACAROON_PATH= DATABASE_FILE=db.sqlite -JWT_SECRET=bigsecrethere \ No newline at end of file +JWT_SECRET=bigsecrethere +LIMIT_FEE_RATE_MILLISATS=6 +LIMIT_FEE_FIXED_SATS=100 +SERVICE_FEE_INCOMING_TX_PERCENT=0 +SERVICE_FEE_OUTGOING_TX_PERCENT=0 +SERVICE_FEE_INCOMING_INVOICE_PERCENT=0 +SERVICE_FEE_OUTGOING_INVOICE_PERCENT=0 \ No newline at end of file diff --git a/proto/autogenerated/debug.txt b/proto/autogenerated/debug.txt index 538bc2c2..0a3a5ab6 100644 --- a/proto/autogenerated/debug.txt +++ b/proto/autogenerated/debug.txt @@ -1,5 +1,5 @@ ([]*main.Method) (len=11 cap=16) { - (*main.Method)(0xc0002886e0)({ + (*main.Method)(0xc0002ba730)({ in: (main.MethodMessage) { name: (string) (len=5) "Empty", hasZeroFields: (bool) true @@ -9,8 +9,8 @@ name: (string) (len=5) "Empty", hasZeroFields: (bool) true }, - opts: (*main.methodOptions)(0xc0003cc420)({ - authType: (*main.supportedAuth)(0xc000379fb0)({ + opts: (*main.methodOptions)(0xc0003fc4e0)({ + authType: (*main.supportedAuth)(0xc00040c030)({ id: (string) (len=5) "guest", name: (string) (len=5) "Guest", encrypted: (bool) false, @@ -25,7 +25,7 @@ query: ([]string) }) }), - (*main.Method)(0xc000288730)({ + (*main.Method)(0xc0002ba780)({ in: (main.MethodMessage) { name: (string) (len=25) "EncryptionExchangeRequest", hasZeroFields: (bool) false @@ -35,8 +35,8 @@ name: (string) (len=5) "Empty", hasZeroFields: (bool) true }, - opts: (*main.methodOptions)(0xc0003cc5a0)({ - authType: (*main.supportedAuth)(0xc0003d60c0)({ + opts: (*main.methodOptions)(0xc0003fc660)({ + authType: (*main.supportedAuth)(0xc00040c120)({ id: (string) (len=5) "guest", name: (string) (len=5) "Guest", encrypted: (bool) false, @@ -51,7 +51,7 @@ query: ([]string) }) }), - (*main.Method)(0xc000288780)({ + (*main.Method)(0xc0002ba7d0)({ in: (main.MethodMessage) { name: (string) (len=17) "LndGetInfoRequest", hasZeroFields: (bool) false @@ -61,8 +61,8 @@ name: (string) (len=18) "LndGetInfoResponse", hasZeroFields: (bool) false }, - opts: (*main.methodOptions)(0xc0003cc720)({ - authType: (*main.supportedAuth)(0xc0003d6180)({ + opts: (*main.methodOptions)(0xc0003fc7e0)({ + authType: (*main.supportedAuth)(0xc00040c1e0)({ id: (string) (len=5) "admin", name: (string) (len=5) "Admin", encrypted: (bool) true, @@ -78,7 +78,7 @@ query: ([]string) }) }), - (*main.Method)(0xc0002887d0)({ + (*main.Method)(0xc0002ba820)({ in: (main.MethodMessage) { name: (string) (len=14) "AddUserRequest", hasZeroFields: (bool) false @@ -88,8 +88,8 @@ name: (string) (len=15) "AddUserResponse", hasZeroFields: (bool) false }, - opts: (*main.methodOptions)(0xc0003cc8a0)({ - authType: (*main.supportedAuth)(0xc0003d6240)({ + opts: (*main.methodOptions)(0xc0003fc960)({ + authType: (*main.supportedAuth)(0xc00040c2a0)({ id: (string) (len=5) "guest", name: (string) (len=5) "Guest", encrypted: (bool) false, @@ -104,7 +104,7 @@ query: ([]string) }) }), - (*main.Method)(0xc000288820)({ + (*main.Method)(0xc0002ba870)({ in: (main.MethodMessage) { name: (string) (len=15) "AuthUserRequest", hasZeroFields: (bool) false @@ -114,8 +114,8 @@ name: (string) (len=16) "AuthUserResponse", hasZeroFields: (bool) false }, - opts: (*main.methodOptions)(0xc0003cca20)({ - authType: (*main.supportedAuth)(0xc0003d6300)({ + opts: (*main.methodOptions)(0xc0003fcae0)({ + authType: (*main.supportedAuth)(0xc00040c360)({ id: (string) (len=5) "guest", name: (string) (len=5) "Guest", encrypted: (bool) false, @@ -130,7 +130,7 @@ query: ([]string) }) }), - (*main.Method)(0xc0002888c0)({ + (*main.Method)(0xc0002ba910)({ in: (main.MethodMessage) { name: (string) (len=17) "NewAddressRequest", hasZeroFields: (bool) false @@ -140,8 +140,8 @@ name: (string) (len=18) "NewAddressResponse", hasZeroFields: (bool) false }, - opts: (*main.methodOptions)(0xc0003ccba0)({ - authType: (*main.supportedAuth)(0xc0003d63c0)({ + opts: (*main.methodOptions)(0xc0003fcc60)({ + authType: (*main.supportedAuth)(0xc00040c420)({ id: (string) (len=4) "user", name: (string) (len=4) "User", encrypted: (bool) false, @@ -157,7 +157,7 @@ query: ([]string) }) }), - (*main.Method)(0xc000288960)({ + (*main.Method)(0xc0002ba9b0)({ in: (main.MethodMessage) { name: (string) (len=17) "PayAddressRequest", hasZeroFields: (bool) false @@ -167,8 +167,8 @@ name: (string) (len=18) "PayAddressResponse", hasZeroFields: (bool) false }, - opts: (*main.methodOptions)(0xc0003ccd20)({ - authType: (*main.supportedAuth)(0xc0003d6480)({ + opts: (*main.methodOptions)(0xc0003fcde0)({ + authType: (*main.supportedAuth)(0xc00040c4e0)({ id: (string) (len=4) "user", name: (string) (len=4) "User", encrypted: (bool) false, @@ -184,7 +184,7 @@ query: ([]string) }) }), - (*main.Method)(0xc000288a00)({ + (*main.Method)(0xc0002baa50)({ in: (main.MethodMessage) { name: (string) (len=17) "NewInvoiceRequest", hasZeroFields: (bool) false @@ -194,8 +194,8 @@ name: (string) (len=18) "NewInvoiceResponse", hasZeroFields: (bool) false }, - opts: (*main.methodOptions)(0xc0003ccea0)({ - authType: (*main.supportedAuth)(0xc0003d6540)({ + opts: (*main.methodOptions)(0xc0003fcf60)({ + authType: (*main.supportedAuth)(0xc00040c5a0)({ id: (string) (len=4) "user", name: (string) (len=4) "User", encrypted: (bool) false, @@ -211,7 +211,7 @@ query: ([]string) }) }), - (*main.Method)(0xc000288aa0)({ + (*main.Method)(0xc0002baaf0)({ in: (main.MethodMessage) { name: (string) (len=17) "PayInvoiceRequest", hasZeroFields: (bool) false @@ -221,8 +221,8 @@ name: (string) (len=18) "PayInvoiceResponse", hasZeroFields: (bool) false }, - opts: (*main.methodOptions)(0xc0003cd020)({ - authType: (*main.supportedAuth)(0xc0003d6600)({ + opts: (*main.methodOptions)(0xc0003fd0e0)({ + authType: (*main.supportedAuth)(0xc00040c660)({ id: (string) (len=4) "user", name: (string) (len=4) "User", encrypted: (bool) false, @@ -238,7 +238,7 @@ query: ([]string) }) }), - (*main.Method)(0xc000288b40)({ + (*main.Method)(0xc0002bab90)({ in: (main.MethodMessage) { name: (string) (len=18) "OpenChannelRequest", hasZeroFields: (bool) false @@ -248,8 +248,8 @@ name: (string) (len=19) "OpenChannelResponse", hasZeroFields: (bool) false }, - opts: (*main.methodOptions)(0xc0003cd1a0)({ - authType: (*main.supportedAuth)(0xc0003d66c0)({ + opts: (*main.methodOptions)(0xc0003fd260)({ + authType: (*main.supportedAuth)(0xc00040c720)({ id: (string) (len=4) "user", name: (string) (len=4) "User", encrypted: (bool) false, @@ -265,7 +265,7 @@ query: ([]string) }) }), - (*main.Method)(0xc000288b90)({ + (*main.Method)(0xc0002babe0)({ in: (main.MethodMessage) { name: (string) (len=5) "Empty", hasZeroFields: (bool) true @@ -275,8 +275,8 @@ name: (string) (len=27) "GetOpenChannelLNURLResponse", hasZeroFields: (bool) false }, - opts: (*main.methodOptions)(0xc0003cd320)({ - authType: (*main.supportedAuth)(0xc0003d6780)({ + opts: (*main.methodOptions)(0xc0003fd3e0)({ + authType: (*main.supportedAuth)(0xc00040c7e0)({ id: (string) (len=4) "user", name: (string) (len=4) "User", encrypted: (bool) false, @@ -295,7 +295,7 @@ } ([]*main.Enum) (len=1 cap=1) { - (*main.Enum)(0xc000379860)({ + (*main.Enum)(0xc0003cf8c0)({ name: (string) (len=11) "AddressType", values: ([]main.EnumValue) (len=3 cap=4) { (main.EnumValue) { @@ -315,86 +315,11 @@ } (map[string]*main.Message) (len=19) { - (string) (len=17) "LndGetInfoRequest": (*main.Message)(0xc000215140)({ - fullName: (string) (len=17) "LndGetInfoRequest", - name: (string) (len=17) "LndGetInfoRequest", - fields: ([]*main.Field) (len=1 cap=1) { - (*main.Field)(0xc000379320)({ - name: (string) (len=7) "node_id", - kind: (string) (len=5) "int64", - isMap: (bool) false, - isArray: (bool) false, - isEnum: (bool) false, - isMessage: (bool) false, - isOptional: (bool) false - }) - } - }), - (string) (len=18) "LndGetInfoResponse": (*main.Message)(0xc000215180)({ - fullName: (string) (len=18) "LndGetInfoResponse", - name: (string) (len=18) "LndGetInfoResponse", - fields: ([]*main.Field) (len=1 cap=1) { - (*main.Field)(0xc000379350)({ - name: (string) (len=5) "alias", - kind: (string) (len=6) "string", - isMap: (bool) false, - isArray: (bool) false, - isEnum: (bool) false, - isMessage: (bool) false, - isOptional: (bool) false - }) - } - }), - (string) (len=17) "NewAddressRequest": (*main.Message)(0xc0002151c0)({ - fullName: (string) (len=17) "NewAddressRequest", - name: (string) (len=17) "NewAddressRequest", - fields: ([]*main.Field) (len=1 cap=1) { - (*main.Field)(0xc000379380)({ - name: (string) (len=12) "address_type", - kind: (string) (len=11) "AddressType", - isMap: (bool) false, - isArray: (bool) false, - isEnum: (bool) true, - isMessage: (bool) false, - isOptional: (bool) false - }) - } - }), - (string) (len=17) "NewInvoiceRequest": (*main.Message)(0xc0002152c0)({ - fullName: (string) (len=17) "NewInvoiceRequest", - name: (string) (len=17) "NewInvoiceRequest", - fields: ([]*main.Field) (len=1 cap=1) { - (*main.Field)(0xc000379470)({ - name: (string) (len=11) "amount_sats", - kind: (string) (len=5) "int64", - isMap: (bool) false, - isArray: (bool) false, - isEnum: (bool) false, - isMessage: (bool) false, - isOptional: (bool) false - }) - } - }), - (string) (len=18) "PayInvoiceResponse": (*main.Message)(0xc000215380)({ - fullName: (string) (len=18) "PayInvoiceResponse", - name: (string) (len=18) "PayInvoiceResponse", - fields: ([]*main.Field) (len=1 cap=1) { - (*main.Field)(0xc000379530)({ - name: (string) (len=8) "preimage", - kind: (string) (len=6) "string", - isMap: (bool) false, - isArray: (bool) false, - isEnum: (bool) false, - isMessage: (bool) false, - isOptional: (bool) false - }) - } - }), - (string) (len=19) "OpenChannelResponse": (*main.Message)(0xc000215400)({ + (string) (len=19) "OpenChannelResponse": (*main.Message)(0xc000249440)({ fullName: (string) (len=19) "OpenChannelResponse", name: (string) (len=19) "OpenChannelResponse", fields: ([]*main.Field) (len=1 cap=1) { - (*main.Field)(0xc000379620)({ + (*main.Field)(0xc0003cf680)({ name: (string) (len=10) "channel_id", kind: (string) (len=6) "string", isMap: (bool) false, @@ -405,89 +330,11 @@ }) } }), - (string) (len=18) "PayAddressResponse": (*main.Message)(0xc000215280)({ - fullName: (string) (len=18) "PayAddressResponse", - name: (string) (len=18) "PayAddressResponse", - fields: ([]*main.Field) (len=1 cap=1) { - (*main.Field)(0xc000379440)({ - name: (string) (len=5) "tx_id", - kind: (string) (len=6) "string", - isMap: (bool) false, - isArray: (bool) false, - isEnum: (bool) false, - isMessage: (bool) false, - isOptional: (bool) false - }) - } - }), - (string) (len=17) "PayInvoiceRequest": (*main.Message)(0xc000215340)({ - fullName: (string) (len=17) "PayInvoiceRequest", - name: (string) (len=17) "PayInvoiceRequest", - fields: ([]*main.Field) (len=2 cap=2) { - (*main.Field)(0xc0003794d0)({ - name: (string) (len=7) "invoice", - kind: (string) (len=6) "string", - isMap: (bool) false, - isArray: (bool) false, - isEnum: (bool) false, - isMessage: (bool) false, - isOptional: (bool) false - }), - (*main.Field)(0xc000379500)({ - name: (string) (len=6) "amount", - kind: (string) (len=5) "int64", - isMap: (bool) false, - isArray: (bool) false, - isEnum: (bool) false, - isMessage: (bool) false, - isOptional: (bool) false - }) - } - }), - (string) (len=25) "EncryptionExchangeRequest": (*main.Message)(0xc000215100)({ - fullName: (string) (len=25) "EncryptionExchangeRequest", - name: (string) (len=25) "EncryptionExchangeRequest", - fields: ([]*main.Field) (len=2 cap=2) { - (*main.Field)(0xc0003792c0)({ - name: (string) (len=10) "public_key", - kind: (string) (len=6) "string", - isMap: (bool) false, - isArray: (bool) false, - isEnum: (bool) false, - isMessage: (bool) false, - isOptional: (bool) false - }), - (*main.Field)(0xc0003792f0)({ - name: (string) (len=9) "device_id", - kind: (string) (len=6) "string", - isMap: (bool) false, - isArray: (bool) false, - isEnum: (bool) false, - isMessage: (bool) false, - isOptional: (bool) false - }) - } - }), - (string) (len=18) "NewAddressResponse": (*main.Message)(0xc000215200)({ - fullName: (string) (len=18) "NewAddressResponse", - name: (string) (len=18) "NewAddressResponse", - fields: ([]*main.Field) (len=1 cap=1) { - (*main.Field)(0xc0003793b0)({ - name: (string) (len=7) "address", - kind: (string) (len=6) "string", - isMap: (bool) false, - isArray: (bool) false, - isEnum: (bool) false, - isMessage: (bool) false, - isOptional: (bool) false - }) - } - }), - (string) (len=27) "GetOpenChannelLNURLResponse": (*main.Message)(0xc000215440)({ + (string) (len=27) "GetOpenChannelLNURLResponse": (*main.Message)(0xc000249480)({ fullName: (string) (len=27) "GetOpenChannelLNURLResponse", name: (string) (len=27) "GetOpenChannelLNURLResponse", fields: ([]*main.Field) (len=1 cap=1) { - (*main.Field)(0xc000379650)({ + (*main.Field)(0xc0003cf6b0)({ name: (string) (len=5) "lnurl", kind: (string) (len=6) "string", isMap: (bool) false, @@ -498,44 +345,11 @@ }) } }), - (string) (len=14) "AddUserRequest": (*main.Message)(0xc000215480)({ - fullName: (string) (len=14) "AddUserRequest", - name: (string) (len=14) "AddUserRequest", - fields: ([]*main.Field) (len=3 cap=4) { - (*main.Field)(0xc000379680)({ - name: (string) (len=12) "callback_url", - kind: (string) (len=6) "string", - isMap: (bool) false, - isArray: (bool) false, - isEnum: (bool) false, - isMessage: (bool) false, - isOptional: (bool) false - }), - (*main.Field)(0xc0003796b0)({ - name: (string) (len=4) "name", - kind: (string) (len=6) "string", - isMap: (bool) false, - isArray: (bool) false, - isEnum: (bool) false, - isMessage: (bool) false, - isOptional: (bool) false - }), - (*main.Field)(0xc0003796e0)({ - name: (string) (len=6) "secret", - kind: (string) (len=6) "string", - isMap: (bool) false, - isArray: (bool) false, - isEnum: (bool) false, - isMessage: (bool) false, - isOptional: (bool) false - }) - } - }), - (string) (len=15) "AddUserResponse": (*main.Message)(0xc0002154c0)({ + (string) (len=15) "AddUserResponse": (*main.Message)(0xc000249500)({ fullName: (string) (len=15) "AddUserResponse", name: (string) (len=15) "AddUserResponse", fields: ([]*main.Field) (len=2 cap=2) { - (*main.Field)(0xc000379710)({ + (*main.Field)(0xc0003cf770)({ name: (string) (len=7) "user_id", kind: (string) (len=6) "string", isMap: (bool) false, @@ -544,7 +358,7 @@ isMessage: (bool) false, isOptional: (bool) false }), - (*main.Field)(0xc000379740)({ + (*main.Field)(0xc0003cf7a0)({ name: (string) (len=10) "auth_token", kind: (string) (len=6) "string", isMap: (bool) false, @@ -555,12 +369,12 @@ }) } }), - (string) (len=15) "AuthUserRequest": (*main.Message)(0xc000215500)({ - fullName: (string) (len=15) "AuthUserRequest", - name: (string) (len=15) "AuthUserRequest", + (string) (len=25) "EncryptionExchangeRequest": (*main.Message)(0xc000249140)({ + fullName: (string) (len=25) "EncryptionExchangeRequest", + name: (string) (len=25) "EncryptionExchangeRequest", fields: ([]*main.Field) (len=2 cap=2) { - (*main.Field)(0xc000379770)({ - name: (string) (len=4) "name", + (*main.Field)(0xc0003cf2f0)({ + name: (string) (len=10) "public_key", kind: (string) (len=6) "string", isMap: (bool) false, isArray: (bool) false, @@ -568,8 +382,8 @@ isMessage: (bool) false, isOptional: (bool) false }), - (*main.Field)(0xc0003797a0)({ - name: (string) (len=6) "secret", + (*main.Field)(0xc0003cf320)({ + name: (string) (len=9) "device_id", kind: (string) (len=6) "string", isMap: (bool) false, isArray: (bool) false, @@ -579,64 +393,11 @@ }) } }), - (string) (len=16) "AuthUserResponse": (*main.Message)(0xc000215540)({ - fullName: (string) (len=16) "AuthUserResponse", - name: (string) (len=16) "AuthUserResponse", - fields: ([]*main.Field) (len=2 cap=2) { - (*main.Field)(0xc0003797d0)({ - name: (string) (len=7) "user_id", - kind: (string) (len=6) "string", - isMap: (bool) false, - isArray: (bool) false, - isEnum: (bool) false, - isMessage: (bool) false, - isOptional: (bool) false - }), - (*main.Field)(0xc000379800)({ - name: (string) (len=10) "auth_token", - kind: (string) (len=6) "string", - isMap: (bool) false, - isArray: (bool) false, - isEnum: (bool) false, - isMessage: (bool) false, - isOptional: (bool) false - }) - } - }), - (string) (len=5) "Empty": (*main.Message)(0xc0002150c0)({ - fullName: (string) (len=5) "Empty", - name: (string) (len=5) "Empty", - fields: ([]*main.Field) - }), - (string) (len=17) "PayAddressRequest": (*main.Message)(0xc000215240)({ - fullName: (string) (len=17) "PayAddressRequest", - name: (string) (len=17) "PayAddressRequest", - fields: ([]*main.Field) (len=2 cap=2) { - (*main.Field)(0xc0003793e0)({ - name: (string) (len=7) "address", - kind: (string) (len=6) "string", - isMap: (bool) false, - isArray: (bool) false, - isEnum: (bool) false, - isMessage: (bool) false, - isOptional: (bool) false - }), - (*main.Field)(0xc000379410)({ - name: (string) (len=10) "amout_sats", - kind: (string) (len=5) "int64", - isMap: (bool) false, - isArray: (bool) false, - isEnum: (bool) false, - isMessage: (bool) false, - isOptional: (bool) false - }) - } - }), - (string) (len=18) "NewInvoiceResponse": (*main.Message)(0xc000215300)({ + (string) (len=18) "NewInvoiceResponse": (*main.Message)(0xc000249340)({ fullName: (string) (len=18) "NewInvoiceResponse", name: (string) (len=18) "NewInvoiceResponse", fields: ([]*main.Field) (len=1 cap=1) { - (*main.Field)(0xc0003794a0)({ + (*main.Field)(0xc0003cf500)({ name: (string) (len=7) "invoice", kind: (string) (len=6) "string", isMap: (bool) false, @@ -647,11 +408,95 @@ }) } }), - (string) (len=18) "OpenChannelRequest": (*main.Message)(0xc0002153c0)({ + (string) (len=18) "PayInvoiceResponse": (*main.Message)(0xc0002493c0)({ + fullName: (string) (len=18) "PayInvoiceResponse", + name: (string) (len=18) "PayInvoiceResponse", + fields: ([]*main.Field) (len=1 cap=1) { + (*main.Field)(0xc0003cf590)({ + name: (string) (len=8) "preimage", + kind: (string) (len=6) "string", + isMap: (bool) false, + isArray: (bool) false, + isEnum: (bool) false, + isMessage: (bool) false, + isOptional: (bool) false + }) + } + }), + (string) (len=15) "AuthUserRequest": (*main.Message)(0xc000249540)({ + fullName: (string) (len=15) "AuthUserRequest", + name: (string) (len=15) "AuthUserRequest", + fields: ([]*main.Field) (len=2 cap=2) { + (*main.Field)(0xc0003cf7d0)({ + name: (string) (len=4) "name", + kind: (string) (len=6) "string", + isMap: (bool) false, + isArray: (bool) false, + isEnum: (bool) false, + isMessage: (bool) false, + isOptional: (bool) false + }), + (*main.Field)(0xc0003cf800)({ + name: (string) (len=6) "secret", + kind: (string) (len=6) "string", + isMap: (bool) false, + isArray: (bool) false, + isEnum: (bool) false, + isMessage: (bool) false, + isOptional: (bool) false + }) + } + }), + (string) (len=18) "LndGetInfoResponse": (*main.Message)(0xc0002491c0)({ + fullName: (string) (len=18) "LndGetInfoResponse", + name: (string) (len=18) "LndGetInfoResponse", + fields: ([]*main.Field) (len=1 cap=1) { + (*main.Field)(0xc0003cf380)({ + name: (string) (len=5) "alias", + kind: (string) (len=6) "string", + isMap: (bool) false, + isArray: (bool) false, + isEnum: (bool) false, + isMessage: (bool) false, + isOptional: (bool) false + }) + } + }), + (string) (len=17) "NewAddressRequest": (*main.Message)(0xc000249200)({ + fullName: (string) (len=17) "NewAddressRequest", + name: (string) (len=17) "NewAddressRequest", + fields: ([]*main.Field) (len=1 cap=1) { + (*main.Field)(0xc0003cf3b0)({ + name: (string) (len=12) "address_type", + kind: (string) (len=11) "AddressType", + isMap: (bool) false, + isArray: (bool) false, + isEnum: (bool) true, + isMessage: (bool) false, + isOptional: (bool) false + }) + } + }), + (string) (len=17) "NewInvoiceRequest": (*main.Message)(0xc000249300)({ + fullName: (string) (len=17) "NewInvoiceRequest", + name: (string) (len=17) "NewInvoiceRequest", + fields: ([]*main.Field) (len=1 cap=1) { + (*main.Field)(0xc0003cf4d0)({ + name: (string) (len=11) "amount_sats", + kind: (string) (len=5) "int64", + isMap: (bool) false, + isArray: (bool) false, + isEnum: (bool) false, + isMessage: (bool) false, + isOptional: (bool) false + }) + } + }), + (string) (len=18) "OpenChannelRequest": (*main.Message)(0xc000249400)({ fullName: (string) (len=18) "OpenChannelRequest", name: (string) (len=18) "OpenChannelRequest", fields: ([]*main.Field) (len=4 cap=4) { - (*main.Field)(0xc000379560)({ + (*main.Field)(0xc0003cf5c0)({ name: (string) (len=11) "destination", kind: (string) (len=6) "string", isMap: (bool) false, @@ -660,7 +505,7 @@ isMessage: (bool) false, isOptional: (bool) false }), - (*main.Field)(0xc000379590)({ + (*main.Field)(0xc0003cf5f0)({ name: (string) (len=14) "funding_amount", kind: (string) (len=5) "int64", isMap: (bool) false, @@ -669,7 +514,7 @@ isMessage: (bool) false, isOptional: (bool) false }), - (*main.Field)(0xc0003795c0)({ + (*main.Field)(0xc0003cf620)({ name: (string) (len=11) "push_amount", kind: (string) (len=5) "int64", isMap: (bool) false, @@ -678,7 +523,7 @@ isMessage: (bool) false, isOptional: (bool) false }), - (*main.Field)(0xc0003795f0)({ + (*main.Field)(0xc0003cf650)({ name: (string) (len=13) "close_address", kind: (string) (len=6) "string", isMap: (bool) false, @@ -688,6 +533,170 @@ isOptional: (bool) false }) } + }), + (string) (len=14) "AddUserRequest": (*main.Message)(0xc0002494c0)({ + fullName: (string) (len=14) "AddUserRequest", + name: (string) (len=14) "AddUserRequest", + fields: ([]*main.Field) (len=3 cap=4) { + (*main.Field)(0xc0003cf6e0)({ + name: (string) (len=12) "callback_url", + kind: (string) (len=6) "string", + isMap: (bool) false, + isArray: (bool) false, + isEnum: (bool) false, + isMessage: (bool) false, + isOptional: (bool) false + }), + (*main.Field)(0xc0003cf710)({ + name: (string) (len=4) "name", + kind: (string) (len=6) "string", + isMap: (bool) false, + isArray: (bool) false, + isEnum: (bool) false, + isMessage: (bool) false, + isOptional: (bool) false + }), + (*main.Field)(0xc0003cf740)({ + name: (string) (len=6) "secret", + kind: (string) (len=6) "string", + isMap: (bool) false, + isArray: (bool) false, + isEnum: (bool) false, + isMessage: (bool) false, + isOptional: (bool) false + }) + } + }), + (string) (len=16) "AuthUserResponse": (*main.Message)(0xc000249580)({ + fullName: (string) (len=16) "AuthUserResponse", + name: (string) (len=16) "AuthUserResponse", + fields: ([]*main.Field) (len=2 cap=2) { + (*main.Field)(0xc0003cf830)({ + name: (string) (len=7) "user_id", + kind: (string) (len=6) "string", + isMap: (bool) false, + isArray: (bool) false, + isEnum: (bool) false, + isMessage: (bool) false, + isOptional: (bool) false + }), + (*main.Field)(0xc0003cf860)({ + name: (string) (len=10) "auth_token", + kind: (string) (len=6) "string", + isMap: (bool) false, + isArray: (bool) false, + isEnum: (bool) false, + isMessage: (bool) false, + isOptional: (bool) false + }) + } + }), + (string) (len=5) "Empty": (*main.Message)(0xc000249100)({ + fullName: (string) (len=5) "Empty", + name: (string) (len=5) "Empty", + fields: ([]*main.Field) + }), + (string) (len=17) "LndGetInfoRequest": (*main.Message)(0xc000249180)({ + fullName: (string) (len=17) "LndGetInfoRequest", + name: (string) (len=17) "LndGetInfoRequest", + fields: ([]*main.Field) (len=1 cap=1) { + (*main.Field)(0xc0003cf350)({ + name: (string) (len=7) "node_id", + kind: (string) (len=5) "int64", + isMap: (bool) false, + isArray: (bool) false, + isEnum: (bool) false, + isMessage: (bool) false, + isOptional: (bool) false + }) + } + }), + (string) (len=18) "NewAddressResponse": (*main.Message)(0xc000249240)({ + fullName: (string) (len=18) "NewAddressResponse", + name: (string) (len=18) "NewAddressResponse", + fields: ([]*main.Field) (len=1 cap=1) { + (*main.Field)(0xc0003cf3e0)({ + name: (string) (len=7) "address", + kind: (string) (len=6) "string", + isMap: (bool) false, + isArray: (bool) false, + isEnum: (bool) false, + isMessage: (bool) false, + isOptional: (bool) false + }) + } + }), + (string) (len=17) "PayAddressRequest": (*main.Message)(0xc000249280)({ + fullName: (string) (len=17) "PayAddressRequest", + name: (string) (len=17) "PayAddressRequest", + fields: ([]*main.Field) (len=3 cap=4) { + (*main.Field)(0xc0003cf410)({ + name: (string) (len=7) "address", + kind: (string) (len=6) "string", + isMap: (bool) false, + isArray: (bool) false, + isEnum: (bool) false, + isMessage: (bool) false, + isOptional: (bool) false + }), + (*main.Field)(0xc0003cf440)({ + name: (string) (len=10) "amout_sats", + kind: (string) (len=5) "int64", + isMap: (bool) false, + isArray: (bool) false, + isEnum: (bool) false, + isMessage: (bool) false, + isOptional: (bool) false + }), + (*main.Field)(0xc0003cf470)({ + name: (string) (len=11) "target_conf", + kind: (string) (len=5) "int64", + isMap: (bool) false, + isArray: (bool) false, + isEnum: (bool) false, + isMessage: (bool) false, + isOptional: (bool) false + }) + } + }), + (string) (len=18) "PayAddressResponse": (*main.Message)(0xc0002492c0)({ + fullName: (string) (len=18) "PayAddressResponse", + name: (string) (len=18) "PayAddressResponse", + fields: ([]*main.Field) (len=1 cap=1) { + (*main.Field)(0xc0003cf4a0)({ + name: (string) (len=5) "tx_id", + kind: (string) (len=6) "string", + isMap: (bool) false, + isArray: (bool) false, + isEnum: (bool) false, + isMessage: (bool) false, + isOptional: (bool) false + }) + } + }), + (string) (len=17) "PayInvoiceRequest": (*main.Message)(0xc000249380)({ + fullName: (string) (len=17) "PayInvoiceRequest", + name: (string) (len=17) "PayInvoiceRequest", + fields: ([]*main.Field) (len=2 cap=2) { + (*main.Field)(0xc0003cf530)({ + name: (string) (len=7) "invoice", + kind: (string) (len=6) "string", + isMap: (bool) false, + isArray: (bool) false, + isEnum: (bool) false, + isMessage: (bool) false, + isOptional: (bool) false + }), + (*main.Field)(0xc0003cf560)({ + name: (string) (len=6) "amount", + kind: (string) (len=5) "int64", + isMap: (bool) false, + isArray: (bool) false, + isEnum: (bool) false, + isMessage: (bool) false, + isOptional: (bool) false + }) + } }) } diff --git a/proto/autogenerated/ts/types.ts b/proto/autogenerated/ts/types.ts index 50480939..87eaadaa 100644 --- a/proto/autogenerated/ts/types.ts +++ b/proto/autogenerated/ts/types.ts @@ -106,15 +106,53 @@ export const EmptyValidate = (o?: Empty, opts: EmptyOptions = {}, path: string = return null } +export type LndGetInfoRequest = { + node_id: number +} +export const LndGetInfoRequestOptionalFields: [] = [] +export type LndGetInfoRequestOptions = OptionsBaseMessage & { + checkOptionalsAreSet?: [] + node_id_CustomCheck?: (v: number) => boolean +} +export const LndGetInfoRequestValidate = (o?: LndGetInfoRequest, opts: LndGetInfoRequestOptions = {}, path: string = 'LndGetInfoRequest::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 (typeof o.node_id !== 'number') return new Error(`${path}.node_id: is not a number`) + if (opts.node_id_CustomCheck && !opts.node_id_CustomCheck(o.node_id)) return new Error(`${path}.node_id: custom check failed`) + + return null +} + +export type NewAddressResponse = { + address: string +} +export const NewAddressResponseOptionalFields: [] = [] +export type NewAddressResponseOptions = OptionsBaseMessage & { + checkOptionalsAreSet?: [] + address_CustomCheck?: (v: string) => boolean +} +export const NewAddressResponseValidate = (o?: NewAddressResponse, opts: NewAddressResponseOptions = {}, path: string = 'NewAddressResponse::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 (typeof o.address !== 'string') return new Error(`${path}.address: is not a string`) + if (opts.address_CustomCheck && !opts.address_CustomCheck(o.address)) return new Error(`${path}.address: custom check failed`) + + return null +} + export type PayAddressRequest = { address: string amout_sats: number + target_conf: number } export const PayAddressRequestOptionalFields: [] = [] export type PayAddressRequestOptions = OptionsBaseMessage & { checkOptionalsAreSet?: [] address_CustomCheck?: (v: string) => boolean amout_sats_CustomCheck?: (v: number) => boolean + target_conf_CustomCheck?: (v: number) => boolean } export const PayAddressRequestValidate = (o?: PayAddressRequest, opts: PayAddressRequestOptions = {}, path: string = 'PayAddressRequest::root.'): Error | null => { if (opts.checkOptionalsAreSet && opts.allOptionalsAreSet) return new Error(path + ': only one of checkOptionalsAreSet or allOptionalNonDefault can be set for each message') @@ -126,56 +164,49 @@ export const PayAddressRequestValidate = (o?: PayAddressRequest, opts: PayAddres if (typeof o.amout_sats !== 'number') return new Error(`${path}.amout_sats: is not a number`) if (opts.amout_sats_CustomCheck && !opts.amout_sats_CustomCheck(o.amout_sats)) return new Error(`${path}.amout_sats: custom check failed`) + if (typeof o.target_conf !== 'number') return new Error(`${path}.target_conf: is not a number`) + if (opts.target_conf_CustomCheck && !opts.target_conf_CustomCheck(o.target_conf)) return new Error(`${path}.target_conf: custom check failed`) + return null } -export type NewInvoiceResponse = { - invoice: string +export type PayAddressResponse = { + tx_id: string } -export const NewInvoiceResponseOptionalFields: [] = [] -export type NewInvoiceResponseOptions = OptionsBaseMessage & { +export const PayAddressResponseOptionalFields: [] = [] +export type PayAddressResponseOptions = OptionsBaseMessage & { + checkOptionalsAreSet?: [] + tx_id_CustomCheck?: (v: string) => boolean +} +export const PayAddressResponseValidate = (o?: PayAddressResponse, opts: PayAddressResponseOptions = {}, path: string = 'PayAddressResponse::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 (typeof o.tx_id !== 'string') return new Error(`${path}.tx_id: is not a string`) + if (opts.tx_id_CustomCheck && !opts.tx_id_CustomCheck(o.tx_id)) return new Error(`${path}.tx_id: custom check failed`) + + return null +} + +export type PayInvoiceRequest = { + invoice: string + amount: number +} +export const PayInvoiceRequestOptionalFields: [] = [] +export type PayInvoiceRequestOptions = OptionsBaseMessage & { checkOptionalsAreSet?: [] invoice_CustomCheck?: (v: string) => boolean + amount_CustomCheck?: (v: number) => boolean } -export const NewInvoiceResponseValidate = (o?: NewInvoiceResponse, opts: NewInvoiceResponseOptions = {}, path: string = 'NewInvoiceResponse::root.'): Error | null => { +export const PayInvoiceRequestValidate = (o?: PayInvoiceRequest, opts: PayInvoiceRequestOptions = {}, path: string = 'PayInvoiceRequest::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 (typeof o.invoice !== 'string') return new Error(`${path}.invoice: is not a string`) if (opts.invoice_CustomCheck && !opts.invoice_CustomCheck(o.invoice)) return new Error(`${path}.invoice: custom check failed`) - return null -} - -export type OpenChannelRequest = { - destination: string - funding_amount: number - push_amount: number - close_address: string -} -export const OpenChannelRequestOptionalFields: [] = [] -export type OpenChannelRequestOptions = OptionsBaseMessage & { - checkOptionalsAreSet?: [] - destination_CustomCheck?: (v: string) => boolean - funding_amount_CustomCheck?: (v: number) => boolean - push_amount_CustomCheck?: (v: number) => boolean - close_address_CustomCheck?: (v: string) => boolean -} -export const OpenChannelRequestValidate = (o?: OpenChannelRequest, opts: OpenChannelRequestOptions = {}, path: string = 'OpenChannelRequest::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 (typeof o.destination !== 'string') return new Error(`${path}.destination: is not a string`) - if (opts.destination_CustomCheck && !opts.destination_CustomCheck(o.destination)) return new Error(`${path}.destination: custom check failed`) - - if (typeof o.funding_amount !== 'number') return new Error(`${path}.funding_amount: is not a number`) - if (opts.funding_amount_CustomCheck && !opts.funding_amount_CustomCheck(o.funding_amount)) return new Error(`${path}.funding_amount: custom check failed`) - - if (typeof o.push_amount !== 'number') return new Error(`${path}.push_amount: is not a number`) - if (opts.push_amount_CustomCheck && !opts.push_amount_CustomCheck(o.push_amount)) return new Error(`${path}.push_amount: custom check failed`) - - if (typeof o.close_address !== 'string') return new Error(`${path}.close_address: is not a string`) - if (opts.close_address_CustomCheck && !opts.close_address_CustomCheck(o.close_address)) return new Error(`${path}.close_address: custom check failed`) + if (typeof o.amount !== 'number') return new Error(`${path}.amount: is not a number`) + if (opts.amount_CustomCheck && !opts.amount_CustomCheck(o.amount)) return new Error(`${path}.amount: custom check failed`) return null } @@ -198,20 +229,125 @@ export const OpenChannelResponseValidate = (o?: OpenChannelResponse, opts: OpenC return null } -export type LndGetInfoRequest = { - node_id: number +export type GetOpenChannelLNURLResponse = { + lnurl: string } -export const LndGetInfoRequestOptionalFields: [] = [] -export type LndGetInfoRequestOptions = OptionsBaseMessage & { +export const GetOpenChannelLNURLResponseOptionalFields: [] = [] +export type GetOpenChannelLNURLResponseOptions = OptionsBaseMessage & { checkOptionalsAreSet?: [] - node_id_CustomCheck?: (v: number) => boolean + lnurl_CustomCheck?: (v: string) => boolean } -export const LndGetInfoRequestValidate = (o?: LndGetInfoRequest, opts: LndGetInfoRequestOptions = {}, path: string = 'LndGetInfoRequest::root.'): Error | null => { +export const GetOpenChannelLNURLResponseValidate = (o?: GetOpenChannelLNURLResponse, opts: GetOpenChannelLNURLResponseOptions = {}, path: string = 'GetOpenChannelLNURLResponse::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 (typeof o.node_id !== 'number') return new Error(`${path}.node_id: is not a number`) - if (opts.node_id_CustomCheck && !opts.node_id_CustomCheck(o.node_id)) return new Error(`${path}.node_id: custom check failed`) + if (typeof o.lnurl !== 'string') return new Error(`${path}.lnurl: is not a string`) + if (opts.lnurl_CustomCheck && !opts.lnurl_CustomCheck(o.lnurl)) return new Error(`${path}.lnurl: custom check failed`) + + return null +} + +export type AddUserResponse = { + user_id: string + auth_token: string +} +export const AddUserResponseOptionalFields: [] = [] +export type AddUserResponseOptions = OptionsBaseMessage & { + checkOptionalsAreSet?: [] + user_id_CustomCheck?: (v: string) => boolean + auth_token_CustomCheck?: (v: string) => boolean +} +export const AddUserResponseValidate = (o?: AddUserResponse, opts: AddUserResponseOptions = {}, path: string = 'AddUserResponse::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 (typeof o.user_id !== 'string') return new Error(`${path}.user_id: is not a string`) + if (opts.user_id_CustomCheck && !opts.user_id_CustomCheck(o.user_id)) return new Error(`${path}.user_id: custom check failed`) + + if (typeof o.auth_token !== 'string') return new Error(`${path}.auth_token: is not a string`) + if (opts.auth_token_CustomCheck && !opts.auth_token_CustomCheck(o.auth_token)) return new Error(`${path}.auth_token: custom check failed`) + + return null +} + +export type EncryptionExchangeRequest = { + public_key: string + device_id: string +} +export const EncryptionExchangeRequestOptionalFields: [] = [] +export type EncryptionExchangeRequestOptions = OptionsBaseMessage & { + checkOptionalsAreSet?: [] + public_key_CustomCheck?: (v: string) => boolean + device_id_CustomCheck?: (v: string) => boolean +} +export const EncryptionExchangeRequestValidate = (o?: EncryptionExchangeRequest, opts: EncryptionExchangeRequestOptions = {}, path: string = 'EncryptionExchangeRequest::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 (typeof o.public_key !== 'string') return new Error(`${path}.public_key: is not a string`) + if (opts.public_key_CustomCheck && !opts.public_key_CustomCheck(o.public_key)) return new Error(`${path}.public_key: custom check failed`) + + if (typeof o.device_id !== 'string') return new Error(`${path}.device_id: is not a string`) + if (opts.device_id_CustomCheck && !opts.device_id_CustomCheck(o.device_id)) return new Error(`${path}.device_id: custom check failed`) + + return null +} + +export type NewInvoiceResponse = { + invoice: string +} +export const NewInvoiceResponseOptionalFields: [] = [] +export type NewInvoiceResponseOptions = OptionsBaseMessage & { + checkOptionalsAreSet?: [] + invoice_CustomCheck?: (v: string) => boolean +} +export const NewInvoiceResponseValidate = (o?: NewInvoiceResponse, opts: NewInvoiceResponseOptions = {}, path: string = 'NewInvoiceResponse::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 (typeof o.invoice !== 'string') return new Error(`${path}.invoice: is not a string`) + if (opts.invoice_CustomCheck && !opts.invoice_CustomCheck(o.invoice)) return new Error(`${path}.invoice: custom check failed`) + + return null +} + +export type PayInvoiceResponse = { + preimage: string +} +export const PayInvoiceResponseOptionalFields: [] = [] +export type PayInvoiceResponseOptions = OptionsBaseMessage & { + checkOptionalsAreSet?: [] + preimage_CustomCheck?: (v: string) => boolean +} +export const PayInvoiceResponseValidate = (o?: PayInvoiceResponse, opts: PayInvoiceResponseOptions = {}, path: string = 'PayInvoiceResponse::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 (typeof o.preimage !== 'string') return new Error(`${path}.preimage: is not a string`) + if (opts.preimage_CustomCheck && !opts.preimage_CustomCheck(o.preimage)) return new Error(`${path}.preimage: custom check failed`) + + return null +} + +export type AuthUserRequest = { + name: string + secret: string +} +export const AuthUserRequestOptionalFields: [] = [] +export type AuthUserRequestOptions = OptionsBaseMessage & { + checkOptionalsAreSet?: [] + name_CustomCheck?: (v: string) => boolean + secret_CustomCheck?: (v: string) => boolean +} +export const AuthUserRequestValidate = (o?: AuthUserRequest, opts: AuthUserRequestOptions = {}, path: string = 'AuthUserRequest::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 (typeof o.name !== 'string') return new Error(`${path}.name: is not a string`) + if (opts.name_CustomCheck && !opts.name_CustomCheck(o.name)) return new Error(`${path}.name: custom check failed`) + + if (typeof o.secret !== 'string') return new Error(`${path}.secret: is not a string`) + if (opts.secret_CustomCheck && !opts.secret_CustomCheck(o.secret)) return new Error(`${path}.secret: custom check failed`) return null } @@ -270,79 +406,58 @@ export const NewInvoiceRequestValidate = (o?: NewInvoiceRequest, opts: NewInvoic return null } -export type PayInvoiceResponse = { - preimage: string +export type OpenChannelRequest = { + destination: string + funding_amount: number + push_amount: number + close_address: string } -export const PayInvoiceResponseOptionalFields: [] = [] -export type PayInvoiceResponseOptions = OptionsBaseMessage & { +export const OpenChannelRequestOptionalFields: [] = [] +export type OpenChannelRequestOptions = OptionsBaseMessage & { checkOptionalsAreSet?: [] - preimage_CustomCheck?: (v: string) => boolean + destination_CustomCheck?: (v: string) => boolean + funding_amount_CustomCheck?: (v: number) => boolean + push_amount_CustomCheck?: (v: number) => boolean + close_address_CustomCheck?: (v: string) => boolean } -export const PayInvoiceResponseValidate = (o?: PayInvoiceResponse, opts: PayInvoiceResponseOptions = {}, path: string = 'PayInvoiceResponse::root.'): Error | null => { +export const OpenChannelRequestValidate = (o?: OpenChannelRequest, opts: OpenChannelRequestOptions = {}, path: string = 'OpenChannelRequest::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 (typeof o.preimage !== 'string') return new Error(`${path}.preimage: is not a string`) - if (opts.preimage_CustomCheck && !opts.preimage_CustomCheck(o.preimage)) return new Error(`${path}.preimage: custom check failed`) + if (typeof o.destination !== 'string') return new Error(`${path}.destination: is not a string`) + if (opts.destination_CustomCheck && !opts.destination_CustomCheck(o.destination)) return new Error(`${path}.destination: custom check failed`) + + if (typeof o.funding_amount !== 'number') return new Error(`${path}.funding_amount: is not a number`) + if (opts.funding_amount_CustomCheck && !opts.funding_amount_CustomCheck(o.funding_amount)) return new Error(`${path}.funding_amount: custom check failed`) + + if (typeof o.push_amount !== 'number') return new Error(`${path}.push_amount: is not a number`) + if (opts.push_amount_CustomCheck && !opts.push_amount_CustomCheck(o.push_amount)) return new Error(`${path}.push_amount: custom check failed`) + + if (typeof o.close_address !== 'string') return new Error(`${path}.close_address: is not a string`) + if (opts.close_address_CustomCheck && !opts.close_address_CustomCheck(o.close_address)) return new Error(`${path}.close_address: custom check failed`) return null } -export type PayAddressResponse = { - tx_id: string -} -export const PayAddressResponseOptionalFields: [] = [] -export type PayAddressResponseOptions = OptionsBaseMessage & { - checkOptionalsAreSet?: [] - tx_id_CustomCheck?: (v: string) => boolean -} -export const PayAddressResponseValidate = (o?: PayAddressResponse, opts: PayAddressResponseOptions = {}, path: string = 'PayAddressResponse::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 (typeof o.tx_id !== 'string') return new Error(`${path}.tx_id: is not a string`) - if (opts.tx_id_CustomCheck && !opts.tx_id_CustomCheck(o.tx_id)) return new Error(`${path}.tx_id: custom check failed`) - - return null -} - -export type PayInvoiceRequest = { - invoice: string - amount: number -} -export const PayInvoiceRequestOptionalFields: [] = [] -export type PayInvoiceRequestOptions = OptionsBaseMessage & { - checkOptionalsAreSet?: [] - invoice_CustomCheck?: (v: string) => boolean - amount_CustomCheck?: (v: number) => boolean -} -export const PayInvoiceRequestValidate = (o?: PayInvoiceRequest, opts: PayInvoiceRequestOptions = {}, path: string = 'PayInvoiceRequest::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 (typeof o.invoice !== 'string') return new Error(`${path}.invoice: is not a string`) - if (opts.invoice_CustomCheck && !opts.invoice_CustomCheck(o.invoice)) return new Error(`${path}.invoice: custom check failed`) - - if (typeof o.amount !== 'number') return new Error(`${path}.amount: is not a number`) - if (opts.amount_CustomCheck && !opts.amount_CustomCheck(o.amount)) return new Error(`${path}.amount: custom check failed`) - - return null -} - -export type AuthUserRequest = { +export type AddUserRequest = { + callback_url: string name: string secret: string } -export const AuthUserRequestOptionalFields: [] = [] -export type AuthUserRequestOptions = OptionsBaseMessage & { +export const AddUserRequestOptionalFields: [] = [] +export type AddUserRequestOptions = OptionsBaseMessage & { checkOptionalsAreSet?: [] + callback_url_CustomCheck?: (v: string) => boolean name_CustomCheck?: (v: string) => boolean secret_CustomCheck?: (v: string) => boolean } -export const AuthUserRequestValidate = (o?: AuthUserRequest, opts: AuthUserRequestOptions = {}, path: string = 'AuthUserRequest::root.'): Error | null => { +export const AddUserRequestValidate = (o?: AddUserRequest, opts: AddUserRequestOptions = {}, path: string = 'AddUserRequest::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 (typeof o.callback_url !== 'string') return new Error(`${path}.callback_url: is not a string`) + if (opts.callback_url_CustomCheck && !opts.callback_url_CustomCheck(o.callback_url)) return new Error(`${path}.callback_url: custom check failed`) + if (typeof o.name !== 'string') return new Error(`${path}.name: is not a string`) if (opts.name_CustomCheck && !opts.name_CustomCheck(o.name)) return new Error(`${path}.name: custom check failed`) @@ -375,113 +490,3 @@ export const AuthUserResponseValidate = (o?: AuthUserResponse, opts: AuthUserRes return null } -export type EncryptionExchangeRequest = { - public_key: string - device_id: string -} -export const EncryptionExchangeRequestOptionalFields: [] = [] -export type EncryptionExchangeRequestOptions = OptionsBaseMessage & { - checkOptionalsAreSet?: [] - public_key_CustomCheck?: (v: string) => boolean - device_id_CustomCheck?: (v: string) => boolean -} -export const EncryptionExchangeRequestValidate = (o?: EncryptionExchangeRequest, opts: EncryptionExchangeRequestOptions = {}, path: string = 'EncryptionExchangeRequest::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 (typeof o.public_key !== 'string') return new Error(`${path}.public_key: is not a string`) - if (opts.public_key_CustomCheck && !opts.public_key_CustomCheck(o.public_key)) return new Error(`${path}.public_key: custom check failed`) - - if (typeof o.device_id !== 'string') return new Error(`${path}.device_id: is not a string`) - if (opts.device_id_CustomCheck && !opts.device_id_CustomCheck(o.device_id)) return new Error(`${path}.device_id: custom check failed`) - - return null -} - -export type NewAddressResponse = { - address: string -} -export const NewAddressResponseOptionalFields: [] = [] -export type NewAddressResponseOptions = OptionsBaseMessage & { - checkOptionalsAreSet?: [] - address_CustomCheck?: (v: string) => boolean -} -export const NewAddressResponseValidate = (o?: NewAddressResponse, opts: NewAddressResponseOptions = {}, path: string = 'NewAddressResponse::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 (typeof o.address !== 'string') return new Error(`${path}.address: is not a string`) - if (opts.address_CustomCheck && !opts.address_CustomCheck(o.address)) return new Error(`${path}.address: custom check failed`) - - return null -} - -export type GetOpenChannelLNURLResponse = { - lnurl: string -} -export const GetOpenChannelLNURLResponseOptionalFields: [] = [] -export type GetOpenChannelLNURLResponseOptions = OptionsBaseMessage & { - checkOptionalsAreSet?: [] - lnurl_CustomCheck?: (v: string) => boolean -} -export const GetOpenChannelLNURLResponseValidate = (o?: GetOpenChannelLNURLResponse, opts: GetOpenChannelLNURLResponseOptions = {}, path: string = 'GetOpenChannelLNURLResponse::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 (typeof o.lnurl !== 'string') return new Error(`${path}.lnurl: is not a string`) - if (opts.lnurl_CustomCheck && !opts.lnurl_CustomCheck(o.lnurl)) return new Error(`${path}.lnurl: custom check failed`) - - return null -} - -export type AddUserRequest = { - callback_url: string - name: string - secret: string -} -export const AddUserRequestOptionalFields: [] = [] -export type AddUserRequestOptions = OptionsBaseMessage & { - checkOptionalsAreSet?: [] - callback_url_CustomCheck?: (v: string) => boolean - name_CustomCheck?: (v: string) => boolean - secret_CustomCheck?: (v: string) => boolean -} -export const AddUserRequestValidate = (o?: AddUserRequest, opts: AddUserRequestOptions = {}, path: string = 'AddUserRequest::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 (typeof o.callback_url !== 'string') return new Error(`${path}.callback_url: is not a string`) - if (opts.callback_url_CustomCheck && !opts.callback_url_CustomCheck(o.callback_url)) return new Error(`${path}.callback_url: custom check failed`) - - if (typeof o.name !== 'string') return new Error(`${path}.name: is not a string`) - if (opts.name_CustomCheck && !opts.name_CustomCheck(o.name)) return new Error(`${path}.name: custom check failed`) - - if (typeof o.secret !== 'string') return new Error(`${path}.secret: is not a string`) - if (opts.secret_CustomCheck && !opts.secret_CustomCheck(o.secret)) return new Error(`${path}.secret: custom check failed`) - - return null -} - -export type AddUserResponse = { - user_id: string - auth_token: string -} -export const AddUserResponseOptionalFields: [] = [] -export type AddUserResponseOptions = OptionsBaseMessage & { - checkOptionalsAreSet?: [] - user_id_CustomCheck?: (v: string) => boolean - auth_token_CustomCheck?: (v: string) => boolean -} -export const AddUserResponseValidate = (o?: AddUserResponse, opts: AddUserResponseOptions = {}, path: string = 'AddUserResponse::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 (typeof o.user_id !== 'string') return new Error(`${path}.user_id: is not a string`) - if (opts.user_id_CustomCheck && !opts.user_id_CustomCheck(o.user_id)) return new Error(`${path}.user_id: custom check failed`) - - if (typeof o.auth_token !== 'string') return new Error(`${path}.auth_token: is not a string`) - if (opts.auth_token_CustomCheck && !opts.auth_token_CustomCheck(o.auth_token)) return new Error(`${path}.auth_token: custom check failed`) - - return null -} - diff --git a/proto/service/structs.proto b/proto/service/structs.proto index fb16525f..06956528 100644 --- a/proto/service/structs.proto +++ b/proto/service/structs.proto @@ -33,6 +33,7 @@ message NewAddressResponse{ message PayAddressRequest{ string address = 1; int64 amout_sats = 2; + int64 target_conf = 3; } message PayAddressResponse{ diff --git a/src/index.spec.ts b/src/index.spec.ts index 923a9619..11b630c0 100644 --- a/src/index.spec.ts +++ b/src/index.spec.ts @@ -21,13 +21,14 @@ export class ServerTestSuite { encryptCallback: async (b) => b, deviceId: "device0" }) - mainHandler = new Main(LoadMainSettingsFromEnv()) // TODO - test env file + mainHandler = new Main(LoadMainSettingsFromEnv(true)) // TODO - test env file server = NewServer(GetServerMethods(this.mainHandler), { ...serverOptions(this.mainHandler), throwErrors: true }) @BeforeAll() async startServer() { await this.mainHandler.storage.Connect() + await this.mainHandler.lnd.Warmup() this.server.Listen(testPort) } @AfterAll() diff --git a/src/services/helpers/envParser.ts b/src/services/helpers/envParser.ts new file mode 100644 index 00000000..545461ab --- /dev/null +++ b/src/services/helpers/envParser.ts @@ -0,0 +1,12 @@ +export const EnvMustBeNonEmptyString = (name: string): string => { + const env = process.env[name] + if (!env) throw new Error(`${name} ENV must be non empty`) + return env +} +export const EnvMustBeInteger = (name: string): number => { + const env = EnvMustBeNonEmptyString(name) + if (isNaN(+env) || !Number.isInteger(+env)) { + throw new Error(`${name} ENV must be an integer number`); + } + return +env +} \ No newline at end of file diff --git a/src/services/lnd/index.spec.ts b/src/services/lnd/index.spec.ts index 82bb0383..4141bf1d 100644 --- a/src/services/lnd/index.spec.ts +++ b/src/services/lnd/index.spec.ts @@ -6,8 +6,9 @@ import * as Types from '../../../proto/autogenerated/ts/types'; export class LndTestSuite { lnd: LndHandler @BeforeAll() - createLnd() { - this.lnd = new LndHandler(LoadLndSettingsFromEnv()) + async createLnd() { + this.lnd = new LndHandler(LoadLndSettingsFromEnv(true), console.log, console.log) + await this.lnd.Warmup() } @AfterAll() stopStreams() { @@ -27,6 +28,11 @@ export class LndTestSuite { const res = await this.lnd.NewInvoice(1000) console.log(res) } + @FTest() + async estimateFees() { + const res = await this.lnd.EstimateChainFees("bcrt1qajzzx453x9fx5gtlyax8zrsennckrw3syd2llt", 1000, 100) + console.log(res) + } @Test() async openChannel() { diff --git a/src/services/lnd/index.ts b/src/services/lnd/index.ts index dd145186..a212deed 100644 --- a/src/services/lnd/index.ts +++ b/src/services/lnd/index.ts @@ -10,41 +10,23 @@ import { GetInfoResponse, AddressType, NewAddressResponse, AddInvoiceResponse, I import { OpenChannelReq } from './openChannelReq'; import { AddInvoiceReq } from './addInvoiceReq'; import { PayInvoiceReq } from './payInvoiceReq'; +import { SendCoinsReq } from './sendCoinsReq'; +import { EnvMustBeInteger, EnvMustBeNonEmptyString } from '../helpers/envParser'; const DeadLineMetadata = (deadline = 10 * 1000) => ({ deadline: Date.now() + deadline }) export type LndSettings = { lndAddr: string lndCertPath: string lndMacaroonPath: string - walletAccount: string feeRateLimit: number feeFixedLimit: number } export const LoadLndSettingsFromEnv = (test = false): LndSettings => { - const lndAddr = process.env.LND_ADDRESS; - const lndCertPath = process.env.LND_CERT_PATH; - const lndMacaroonPath = process.env.LND_MACAROON_PATH; - if (!lndAddr) throw new Error(`missing env for LND_ADDRESS`) - if (!lndCertPath) throw new Error(`missing env for LND_CERT_PATH`) - if (!lndMacaroonPath) throw new Error(`missing env for LND_MACAROON_PATH`) - - if (!process.env.LIMIT_FEE_RATE_MILLISATS) { - throw new Error(`missing env for LIMIT_FEE_RATE_MILLISATS`) - } - if (isNaN(+process.env.LIMIT_FEE_RATE_MILLISATS) || !Number.isInteger(+process.env.LIMIT_FEE_RATE_MILLISATS)) { - throw new Error("LIMIT_FEE_RATE_MILLISATS must be an integer number"); - } - - if (!process.env.LIMIT_FEE_FIXED_SATS) { - throw new Error(`missing env for LIMIT_FEE_FIXED_SATS`) - } - if (isNaN(+process.env.LIMIT_FEE_FIXED_SATS) || !Number.isInteger(+process.env.LIMIT_FEE_FIXED_SATS)) { - throw new Error("LIMIT_FEE_FIXED_SATS must be an integer number"); - } - const feeRateLimit = +process.env.LIMIT_FEE_RATE_MILLISATS / 1000 - const feeFixedLimit = +process.env.LIMIT_FEE_FIXED_SATS - - const walletAccount = process.env.LND_WALLET_ACCOUNT || "" - return { lndAddr, lndCertPath, lndMacaroonPath, walletAccount, feeRateLimit, feeFixedLimit } + const lndAddr = EnvMustBeNonEmptyString("LND_ADDRESS") + const lndCertPath = EnvMustBeNonEmptyString("LND_CERT_PATH") + const lndMacaroonPath = EnvMustBeNonEmptyString("LND_MACAROON_PATH") + const feeRateLimit = EnvMustBeInteger("LIMIT_FEE_RATE_MILLISATS") / 1000 + const feeFixedLimit = EnvMustBeInteger("LIMIT_FEE_FIXED_SATS") + return { lndAddr, lndCertPath, lndMacaroonPath, feeRateLimit, feeFixedLimit } } type TxOutput = { hash: string @@ -102,7 +84,7 @@ export default class { } SubscribeAddressPaid() { const stream = this.lightning.subscribeTransactions({ - account: this.settings.walletAccount, + account: "", endHeight: 0, startHeight: this.latestKnownBlockHeigh, }, { abort: this.abortController.signal }) @@ -154,7 +136,7 @@ export default class { default: throw new Error("unknown address type " + addressType) } - const res = await this.lightning.newAddress({ account: this.settings.walletAccount, type: lndAddressType }, DeadLineMetadata()) + const res = await this.lightning.newAddress({ account: "", type: lndAddressType }, DeadLineMetadata()) return res.response } @@ -193,6 +175,22 @@ export default class { }) } + async EstimateChainFees(address: string, amount: number, targetConf: number) { + const res = await this.lightning.estimateFee({ + addrToAmount: { [address]: BigInt(amount) }, + minConfs: 1, + spendUnconfirmed: false, + targetConf: targetConf + }) + return res.response + } + + async PayAddress(address: string, amount: number, satPerVByte: number, label = "") { + this.checkReady() + const res = await this.lightning.sendCoins(SendCoinsReq(address, amount, satPerVByte, label), DeadLineMetadata()) + return res.response + } + async OpenChannel(destination: string, closeAddress: string, fundingAmount: number, pushSats: number): Promise { this.checkReady() @@ -201,6 +199,7 @@ export default class { const stream = this.lightning.openChannel(req, { abort: abortController.signal }) return new Promise((res, rej) => { stream.responses.onMessage(message => { + switch (message.update.oneofKind) { case 'chanPending': abortController.abort() diff --git a/src/services/lnd/openChannelReq.ts b/src/services/lnd/openChannelReq.ts index 3010e96b..7860637a 100644 --- a/src/services/lnd/openChannelReq.ts +++ b/src/services/lnd/openChannelReq.ts @@ -11,6 +11,7 @@ export const OpenChannelReq = (destination: string, closeAddress: string, fundin minConfs: 0, // TBD baseFee: 0n, // TBD feeRate: 0n, // TBD + targetConf: 0, zeroConf: false, maxLocalCsv: 0, remoteCsvDelay: 0, @@ -19,7 +20,6 @@ export const OpenChannelReq = (destination: string, closeAddress: string, fundin remoteChanReserveSat: 0n, remoteMaxHtlcs: 0, remoteMaxValueInFlightMsat: 0n, - targetConf: 0, useBaseFee: false, useFeeRate: false, diff --git a/src/services/lnd/sendCoinsReq.ts b/src/services/lnd/sendCoinsReq.ts new file mode 100644 index 00000000..377d9cff --- /dev/null +++ b/src/services/lnd/sendCoinsReq.ts @@ -0,0 +1,13 @@ +import { SendCoinsRequest } from "../../../proto/lnd/lightning"; + +export const SendCoinsReq = (address: string, amount: number, satPerVByte: number, label = ""): SendCoinsRequest => ({ + addr: address, + amount: BigInt(amount), + label: label, + satPerVbyte: BigInt(satPerVByte), + targetConf: 0, + minConfs: 1, + sendAll: false, + spendUnconfirmed: false, + satPerByte: 0n +}) \ No newline at end of file diff --git a/src/services/main/index.ts b/src/services/main/index.ts index 605e4771..2c5463e6 100644 --- a/src/services/main/index.ts +++ b/src/services/main/index.ts @@ -2,20 +2,34 @@ import jwt from 'jsonwebtoken' import Storage, { LoadStorageSettingsFromEnv, StorageSettings } from '../storage' import * as Types from '../../../proto/autogenerated/ts/types' import LND, { AddressPaidCb, InvoicePaidCb, LndSettings, LoadLndSettingsFromEnv } from '../lnd' +import { EnvMustBeInteger, EnvMustBeNonEmptyString } from '../helpers/envParser' export type MainSettings = { storageSettings: StorageSettings, lndSettings: LndSettings, jwtSecret: string + incomingTxFee: number + outgoingTxFee: number + incomingInvoiceFee: number + outgoingInvoiceFee: number } export const LoadMainSettingsFromEnv = (test = false): MainSettings => { - const jwtSecret = process.env.JWT_SECRET; - if (!jwtSecret) throw new Error(`missing env for JWT_SECRET`) return { lndSettings: LoadLndSettingsFromEnv(test), storageSettings: LoadStorageSettingsFromEnv(test), - jwtSecret + jwtSecret: EnvMustBeNonEmptyString("JWT_SECRET"), + incomingTxFee: EnvMustBeInteger("SERVICE_FEE_INCOMING_TX_PERCENT") / 100, + outgoingTxFee: EnvMustBeInteger("SERVICE_FEE_OUTGOING_TX_PERCENT") / 100, + incomingInvoiceFee: EnvMustBeInteger("SERVICE_FEE_INCOMING_INVOICE_PERCENT") / 100, + outgoingInvoiceFee: EnvMustBeInteger("SERVICE_FEE_OUTGOING_INVOICE_PERCENT") / 100, } } +enum ActionType { + INCOMING_TX = "INCOMING_TX", + OUTGOING_TX = "OUTGOING_TX", + INCOMING_INVOICE = "INCOMING_INVOICE", + OUTGOING_INVOICE = "OUTGOING_INVOICE" + +} export default class { storage: Storage lnd: LND @@ -25,12 +39,27 @@ export default class { this.storage = new Storage(settings.storageSettings) this.lnd = new LND(settings.lndSettings, this.addressPaidCb, this.invoicePaidCb) } + getServiceFee(action: ActionType, amount: number): number { + switch (action) { + case ActionType.INCOMING_TX: + return Math.ceil(this.settings.incomingTxFee * amount) + case ActionType.OUTGOING_TX: + return Math.ceil(this.settings.outgoingTxFee * amount) + case ActionType.INCOMING_INVOICE: + return Math.ceil(this.settings.incomingInvoiceFee * amount) + case ActionType.OUTGOING_INVOICE: + return Math.ceil(this.settings.outgoingInvoiceFee * amount) + default: + throw new Error("Unknown service action type") + } + } addressPaidCb: AddressPaidCb = (txOutput, address, amount) => { this.storage.StartTransaction(async tx => { const userAddress = await this.storage.GetAddressOwner(address, tx) if (!userAddress) { return } + const fee = this.getServiceFee(ActionType.INCOMING_TX, amount) // This call will fail if the transaction is already registered - const addedTx = await this.storage.AddAddressTransaction(address, txOutput.hash, txOutput.index, amount) + const addedTx = await this.storage.AddAddressReceivingTransaction(userAddress, txOutput.hash, txOutput.index, amount, fee, tx) await this.storage.IncrementUserBalance(userAddress.user.user_id, addedTx.amount, tx) }) } @@ -38,9 +67,10 @@ export default class { this.storage.StartTransaction(async tx => { const userInvoice = await this.storage.GetInvoiceOwner(paymentRequest, tx) if (!userInvoice || userInvoice.paid) { return } + const fee = this.getServiceFee(ActionType.INCOMING_INVOICE, amount) // This call will fail if the invoice is already registered - await this.storage.FlagInvoiceAsPaid(userInvoice.serial_id, amount, tx) - await this.storage.IncrementUserBalance(userInvoice.user.user_id, amount) + await this.storage.FlagInvoiceAsPaid(userInvoice, amount, fee, tx) + await this.storage.IncrementUserBalance(userInvoice.user.user_id, amount, tx) }) } SignUserToken(userId: string): string { @@ -76,21 +106,55 @@ export default class { } } + async lockUserWithMinBalance(userId: string, minBalance: number) { + return this.storage.StartTransaction(async tx => { + const user = await this.storage.GetUser(userId, tx) + if (user.balance_sats < minBalance) { + throw new Error("insufficient balance") + } + // this call will fail if the user is already locked + await this.storage.LockUser(userId, tx) + }) + } + async PayInvoice(userId: string, req: Types.PayInvoiceRequest): Promise { const decoded = await this.lnd.DecodeInvoice(req.invoice) - const payAmount = Number(decoded.numSatoshis) - const feeLimit = this.lnd.GetFeeLimitAmount(payAmount) - const decrement = payAmount + feeLimit - // this call will fail if the user balance is not enough - await this.storage.DecrementUserBalance(userId, decrement) - try { - await this.lnd.PayInvoice(req.invoice, req.amount, feeLimit) - - } catch (e) { - await this.storage.IncrementUserBalance(userId, decrement) - throw e + if (decoded.numSatoshis !== 0n && req.amount !== 0) { + throw new Error("invoice has value, do not provide amount the the request") + } + if (decoded.numSatoshis === 0n && req.amount === 0) { + throw new Error("invoice has no value, an amount must be provided in the request") + } + const payAmount = req.amount !== 0 ? req.amount : Number(decoded.numSatoshis) + const serviceFee = this.getServiceFee(ActionType.OUTGOING_INVOICE, payAmount) + const totalAmountToDecrement = payAmount + serviceFee + + + const routingFeeLimit = this.lnd.GetFeeLimitAmount(payAmount) + await this.lockUserWithMinBalance(userId, totalAmountToDecrement + routingFeeLimit) + const payment = await this.lnd.PayInvoice(req.invoice, req.amount, routingFeeLimit) + await this.storage.DecrementUserBalance(userId, totalAmountToDecrement + Number(payment.feeSat)) + await this.storage.UnlockUser(userId) + await this.storage.AddUserInvoicePayment(userId, req.invoice, payAmount, Number(payment.feeSat), serviceFee) + return { + preimage: payment.paymentPreimage + } + } + + async PayAddress(userId: string, req: Types.PayAddressRequest): Promise { + const estimate = await this.lnd.EstimateChainFees(req.address, req.amout_sats, req.target_conf) + const satPerVByte = Number(estimate.satPerVbyte) + const chainFees = Number(estimate.feeSat) + const total = req.amout_sats + chainFees + const serviceFee = this.getServiceFee(ActionType.OUTGOING_INVOICE, req.amout_sats) + await this.lockUserWithMinBalance(userId, total + serviceFee) + const payment = await this.lnd.PayAddress(req.address, req.amout_sats, satPerVByte) + await this.storage.DecrementUserBalance(userId, total + serviceFee) + await this.storage.UnlockUser(userId) + await this.storage.AddUserTransactionPayment(userId, req.address, payment.txid, 0, req.amout_sats, chainFees, serviceFee) + return { + tx_id: payment.txid } - await this.storage.AddUserPayment(userId, req.invoice,) } async OpenChannel(userId: string, req: Types.OpenChannelRequest): Promise { throw new Error("WIP") } diff --git a/src/services/serverMethods/index.ts b/src/services/serverMethods/index.ts index e86aca58..718a5775 100644 --- a/src/services/serverMethods/index.ts +++ b/src/services/serverMethods/index.ts @@ -33,7 +33,13 @@ export default (mainHandler: Main): Types.ServerMethods => { return mainHandler.NewAddress(ctx.user_id, req) }, PayAddress: async (ctx: Types.UserContext, req: Types.PayAddressRequest): Promise => { - throw new Error("unimplemented") + const err = Types.PayAddressRequestValidate(req, { + address_CustomCheck: addr => addr !== '', + amout_sats_CustomCheck: amt => amt > 0, + target_conf_CustomCheck: target => target > 0 + }) + if (err != null) throw new Error(err.message) + return mainHandler.PayAddress(ctx.user_id, req) }, NewInvoice: async (ctx: Types.UserContext, req: Types.NewInvoiceRequest): Promise => { throw new Error("unimplemented") diff --git a/src/services/storage/db.ts b/src/services/storage/db.ts index b0db4963..fa02375e 100644 --- a/src/services/storage/db.ts +++ b/src/services/storage/db.ts @@ -1,24 +1,24 @@ import "reflect-metadata" import { DataSource } from "typeorm" -import { AddressTransaction } from "./entity/AddressTransaction" +import { AddressReceivingTransaction } from "./entity/AddressReceivingTransaction" import { User } from "./entity/User" -import { UserAddress } from "./entity/UserAddress" -import { UserInvoice } from "./entity/UserInvoice" -import { UserPayment } from "./entity/UserPayment" +import { UserReceivingAddress } from "./entity/UserReceivingAddress" +import { UserReceivingInvoice } from "./entity/UserReceivingInvoice" +import { UserInvoicePayment } from "./entity/UserInvoicePayment" +import { EnvMustBeNonEmptyString } from "../helpers/envParser" +import { UserTransactionPayment } from "./entity/UserTransactionPayment" export type DbSettings = { databaseFile: string } export const LoadDbSettingsFromEnv = (test = false): DbSettings => { - const databaseFile = process.env.DATABASE_FILE - if (!databaseFile) throw new Error(`missing env for DATABASE_FILE`) - return { databaseFile: test ? ":memory:" : databaseFile } + return { databaseFile: test ? ":memory:" : EnvMustBeNonEmptyString("DATABASE_FILE") } } export default async (settings: DbSettings) => { return new DataSource({ type: "sqlite", database: settings.databaseFile, //logging: true, - entities: [User, UserInvoice, UserAddress, AddressTransaction, UserPayment], + entities: [User, UserReceivingInvoice, UserReceivingAddress, AddressReceivingTransaction, UserInvoicePayment, UserTransactionPayment], synchronize: true }).initialize() } \ No newline at end of file diff --git a/src/services/storage/entity/AddressReceivingTransaction.ts b/src/services/storage/entity/AddressReceivingTransaction.ts new file mode 100644 index 00000000..760ae226 --- /dev/null +++ b/src/services/storage/entity/AddressReceivingTransaction.ts @@ -0,0 +1,26 @@ +import { Entity, PrimaryGeneratedColumn, Column, Index, Check, ManyToOne } from "typeorm" +import { User } from "./User" +import { UserReceivingAddress } from "./UserReceivingAddress" + +@Entity() +@Index("address_transaction_unique", ["tx_hash", "output_index"], { unique: true }) +export class AddressReceivingTransaction { + + @PrimaryGeneratedColumn() + serial_id: number + + @ManyToOne(type => UserReceivingAddress) + user_address: UserReceivingAddress + + @Column() + tx_hash: string + + @Column() + output_index: number + + @Column() + amount: number + + @Column() + service_fee: number +} diff --git a/src/services/storage/entity/User.ts b/src/services/storage/entity/User.ts index 1d26459a..5cb84543 100644 --- a/src/services/storage/entity/User.ts +++ b/src/services/storage/entity/User.ts @@ -1,7 +1,6 @@ import { Entity, PrimaryGeneratedColumn, Column, Index, Check } from "typeorm" @Entity() -@Check(`"balance_sats" >= 0`) export class User { @PrimaryGeneratedColumn() diff --git a/src/services/storage/entity/UserPayment.ts b/src/services/storage/entity/UserInvoicePayment.ts similarity index 74% rename from src/services/storage/entity/UserPayment.ts rename to src/services/storage/entity/UserInvoicePayment.ts index 45d2eb2f..6eab0674 100644 --- a/src/services/storage/entity/UserPayment.ts +++ b/src/services/storage/entity/UserInvoicePayment.ts @@ -2,7 +2,7 @@ import { Entity, PrimaryGeneratedColumn, Column, Index, Check, ManyToOne } from import { User } from "./User" @Entity() -export class UserPayment { +export class UserInvoicePayment { @PrimaryGeneratedColumn() serial_id: number @@ -16,4 +16,10 @@ export class UserPayment { @Column() amount: number + + @Column() + routing_fees: number + + @Column() + service_fees: number } diff --git a/src/services/storage/entity/UserAddress.ts b/src/services/storage/entity/UserReceivingAddress.ts similarity index 90% rename from src/services/storage/entity/UserAddress.ts rename to src/services/storage/entity/UserReceivingAddress.ts index aa8853ae..56b98f8d 100644 --- a/src/services/storage/entity/UserAddress.ts +++ b/src/services/storage/entity/UserReceivingAddress.ts @@ -2,7 +2,7 @@ import { Entity, PrimaryGeneratedColumn, Column, Index, Check, ManyToOne } from import { User } from "./User" @Entity() -export class UserAddress { +export class UserReceivingAddress { @PrimaryGeneratedColumn() serial_id: number diff --git a/src/services/storage/entity/UserInvoice.ts b/src/services/storage/entity/UserReceivingInvoice.ts similarity index 85% rename from src/services/storage/entity/UserInvoice.ts rename to src/services/storage/entity/UserReceivingInvoice.ts index 9d223dd8..7af8611d 100644 --- a/src/services/storage/entity/UserInvoice.ts +++ b/src/services/storage/entity/UserReceivingInvoice.ts @@ -2,7 +2,7 @@ import { Entity, PrimaryGeneratedColumn, Column, Index, Check, ManyToOne } from import { User } from "./User" @Entity() -export class UserInvoice { +export class UserReceivingInvoice { @PrimaryGeneratedColumn() serial_id: number @@ -22,4 +22,7 @@ export class UserInvoice { @Column({ default: 0 }) settle_amount: number + + @Column() + service_fee: number } diff --git a/src/services/storage/entity/AddressTransaction.ts b/src/services/storage/entity/UserTransactionPayment.ts similarity index 66% rename from src/services/storage/entity/AddressTransaction.ts rename to src/services/storage/entity/UserTransactionPayment.ts index 36e08133..80307ffc 100644 --- a/src/services/storage/entity/AddressTransaction.ts +++ b/src/services/storage/entity/UserTransactionPayment.ts @@ -1,16 +1,18 @@ import { Entity, PrimaryGeneratedColumn, Column, Index, Check, ManyToOne } from "typeorm" import { User } from "./User" -import { UserAddress } from "./UserAddress" @Entity() @Index("address_transaction_unique", ["tx_hash", "output_index"], { unique: true }) -export class AddressTransaction { +export class UserTransactionPayment { @PrimaryGeneratedColumn() serial_id: number - @ManyToOne(type => UserAddress) - user_address: UserAddress + @ManyToOne(type => User) + user: User + + @Column() + address: string @Column() tx_hash: string @@ -20,4 +22,10 @@ export class AddressTransaction { @Column() amount: number + + @Column() + chain_fees: number + + @Column() + service_fees: number } diff --git a/src/services/storage/index.ts b/src/services/storage/index.ts index 31088026..8ac5f5cc 100644 --- a/src/services/storage/index.ts +++ b/src/services/storage/index.ts @@ -2,10 +2,11 @@ import { DataSource, EntityManager } from "typeorm" import crypto from 'crypto'; import NewDB, { DbSettings, LoadDbSettingsFromEnv } from "./db" import { User } from "./entity/User" -import { UserAddress } from "./entity/UserAddress"; -import { UserInvoice } from "./entity/UserInvoice"; -import { AddressTransaction } from "./entity/AddressTransaction"; -import { UserPayment } from "./entity/UserPayment"; +import { UserReceivingAddress } from "./entity/UserReceivingAddress"; +import { UserReceivingInvoice } from "./entity/UserReceivingInvoice"; +import { AddressReceivingTransaction } from "./entity/AddressReceivingTransaction"; +import { UserInvoicePayment } from "./entity/UserInvoicePayment"; +import { UserTransactionPayment } from "./entity/UserTransactionPayment"; export type StorageSettings = { dbSettings: DbSettings } @@ -23,7 +24,7 @@ export default class { this.DB = await NewDB(this.settings.dbSettings) } StartTransaction(exec: (entityManager: EntityManager) => Promise) { - this.DB.transaction(exec) + return this.DB.transaction(exec) } async AddUser(name: string, callbackUrl: string, secret: string, entityManager = this.DB): Promise { const newUser = entityManager.getRepository(User).create({ @@ -49,72 +50,93 @@ export default class { return user } - async AddAddressTransaction(address: string, txHash: string, outputIndex: number, amount: number, entityManager = this.DB) { - const newAddressTransaction = entityManager.getRepository(AddressTransaction).create({ - user_address: { address: address }, + async AddAddressReceivingTransaction(address: UserReceivingAddress, txHash: string, outputIndex: number, amount: number, serviceFee: number, entityManager = this.DB) { + const newAddressTransaction = entityManager.getRepository(AddressReceivingTransaction).create({ + user_address: address, tx_hash: txHash, output_index: outputIndex, - amount: amount + amount: amount, + service_fee: serviceFee }) - return entityManager.getRepository(AddressTransaction).save(newAddressTransaction) + return entityManager.getRepository(AddressReceivingTransaction).save(newAddressTransaction) } - async AddUserAddress(userId: string, address: string, callbackUrl = "", entityManager = this.DB): Promise { - const newUserAddress = entityManager.getRepository(UserAddress).create({ + async AddUserAddress(userId: string, address: string, callbackUrl = "", entityManager = this.DB): Promise { + const newUserAddress = entityManager.getRepository(UserReceivingAddress).create({ address, callbackUrl, - user: { user_id: userId } + user: await this.GetUser(userId, entityManager) }) - return entityManager.getRepository(UserAddress).save(newUserAddress) + return entityManager.getRepository(UserReceivingAddress).save(newUserAddress) } - async FlagInvoiceAsPaid(invoiceSerialId: number, amount: number, entityManager = this.DB) { - return entityManager.getRepository(UserInvoice).update(invoiceSerialId, { paid: true, settle_amount: amount }) + async FlagInvoiceAsPaid(invoice: UserReceivingInvoice, amount: number, serviceFee: number, entityManager = this.DB) { + return entityManager.getRepository(UserReceivingInvoice).update(invoice.serial_id, { paid: true, settle_amount: amount, service_fee: serviceFee }) } - async AddUserInvoice(userId: string, invoice: string, callbackUrl = "", entityManager = this.DB): Promise { - const newUserInvoice = entityManager.getRepository(UserInvoice).create({ + async AddUserInvoice(userId: string, invoice: string, callbackUrl = "", entityManager = this.DB): Promise { + const newUserInvoice = entityManager.getRepository(UserReceivingInvoice).create({ invoice: invoice, callbackUrl, - user: { user_id: userId } + user: await this.GetUser(userId, entityManager) }) - return entityManager.getRepository(UserInvoice).save(newUserInvoice) + return entityManager.getRepository(UserReceivingInvoice).save(newUserInvoice) } - async GetAddressOwner(address: string, entityManager = this.DB): Promise { - return entityManager.getRepository(UserAddress).findOne({ + async GetAddressOwner(address: string, entityManager = this.DB): Promise { + return entityManager.getRepository(UserReceivingAddress).findOne({ where: { address } }) } - async GetInvoiceOwner(paymentRequest: string, entityManager = this.DB): Promise { - return entityManager.getRepository(UserInvoice).findOne({ + async GetInvoiceOwner(paymentRequest: string, entityManager = this.DB): Promise { + return entityManager.getRepository(UserReceivingInvoice).findOne({ where: { invoice: paymentRequest } }) } - async AddUserPayment(userId: string, invoice: string, amount: number, entityManager = this.DB): Promise { - const newPayment = entityManager.getRepository(UserPayment).create({ - user: { user_id: userId }, + async AddUserInvoicePayment(userId: string, invoice: string, amount: number, routingFees: number, serviceFees: number, entityManager = this.DB): Promise { + const newPayment = entityManager.getRepository(UserInvoicePayment).create({ + user: await this.GetUser(userId), amount, - invoice + invoice, + routing_fees: routingFees, + service_fees: serviceFees }) - return entityManager.getRepository(UserPayment).save(newPayment) + return entityManager.getRepository(UserInvoicePayment).save(newPayment) } - LockUser(userId: string, entityManager = this.DB) { - return entityManager.getRepository(User).update({ + async AddUserTransactionPayment(userId: string, address: string, txHash: string, txOutput: number, amount: number, chainFees: number, serviceFees: number, entityManager = this.DB): Promise { + const newTx = entityManager.getRepository(UserTransactionPayment).create({ + user: await this.GetUser(userId), + address, + amount, + chain_fees: chainFees, + output_index: txOutput, + tx_hash: txHash, + service_fees: serviceFees + }) + return entityManager.getRepository(UserTransactionPayment).save(newTx) + } + async LockUser(userId: string, entityManager = this.DB) { + const res = await entityManager.getRepository(User).update({ user_id: userId }, { locked: true }) + if (!res.affected) { + throw new Error("unaffected user lock for " + userId) // TODO: fix logs doxing + } } - UnlockUser(userId: string, entityManager = this.DB) { - return entityManager.getRepository(User).update({ + async UnlockUser(userId: string, entityManager = this.DB) { + const res = await entityManager.getRepository(User).update({ user_id: userId }, { locked: false }) + if (!res.affected) { + throw new Error("unaffected user unlock for " + userId) // TODO: fix logs doxing + } } async IncrementUserBalance(userId: string, increment: number, entityManager = this.DB) { const res = await entityManager.getRepository(User).increment({