diff --git a/BTCPayServer.Lightning.sln b/BTCPayServer.Lightning.sln index 6568ac47..1f7a4fc1 100644 --- a/BTCPayServer.Lightning.sln +++ b/BTCPayServer.Lightning.sln @@ -31,6 +31,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "misc", "misc", "{216059DB-7 src\Build\Common.csproj = src\Build\Common.csproj EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BTCPayServer.Lightning.Blink", "src\BTCPayServer.Lightning.Blink\BTCPayServer.Lightning.Blink.csproj", "{DD000D73-3AD5-465E-9AA9-64802CC88BF3}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BlinkTest", "BlinkTest\BlinkTest.csproj", "{4D375E18-F7FF-4A6D-9994-EE1D0C3C05C3}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -149,6 +153,30 @@ Global {B024DBD2-FCF4-4C48-9EBE-09AA7AAF36FB}.Release|x64.Build.0 = Release|Any CPU {B024DBD2-FCF4-4C48-9EBE-09AA7AAF36FB}.Release|x86.ActiveCfg = Release|Any CPU {B024DBD2-FCF4-4C48-9EBE-09AA7AAF36FB}.Release|x86.Build.0 = Release|Any CPU + {DD000D73-3AD5-465E-9AA9-64802CC88BF3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DD000D73-3AD5-465E-9AA9-64802CC88BF3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DD000D73-3AD5-465E-9AA9-64802CC88BF3}.Debug|x64.ActiveCfg = Debug|Any CPU + {DD000D73-3AD5-465E-9AA9-64802CC88BF3}.Debug|x64.Build.0 = Debug|Any CPU + {DD000D73-3AD5-465E-9AA9-64802CC88BF3}.Debug|x86.ActiveCfg = Debug|Any CPU + {DD000D73-3AD5-465E-9AA9-64802CC88BF3}.Debug|x86.Build.0 = Debug|Any CPU + {DD000D73-3AD5-465E-9AA9-64802CC88BF3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DD000D73-3AD5-465E-9AA9-64802CC88BF3}.Release|Any CPU.Build.0 = Release|Any CPU + {DD000D73-3AD5-465E-9AA9-64802CC88BF3}.Release|x64.ActiveCfg = Release|Any CPU + {DD000D73-3AD5-465E-9AA9-64802CC88BF3}.Release|x64.Build.0 = Release|Any CPU + {DD000D73-3AD5-465E-9AA9-64802CC88BF3}.Release|x86.ActiveCfg = Release|Any CPU + {DD000D73-3AD5-465E-9AA9-64802CC88BF3}.Release|x86.Build.0 = Release|Any CPU + {4D375E18-F7FF-4A6D-9994-EE1D0C3C05C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4D375E18-F7FF-4A6D-9994-EE1D0C3C05C3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4D375E18-F7FF-4A6D-9994-EE1D0C3C05C3}.Debug|x64.ActiveCfg = Debug|Any CPU + {4D375E18-F7FF-4A6D-9994-EE1D0C3C05C3}.Debug|x64.Build.0 = Debug|Any CPU + {4D375E18-F7FF-4A6D-9994-EE1D0C3C05C3}.Debug|x86.ActiveCfg = Debug|Any CPU + {4D375E18-F7FF-4A6D-9994-EE1D0C3C05C3}.Debug|x86.Build.0 = Debug|Any CPU + {4D375E18-F7FF-4A6D-9994-EE1D0C3C05C3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4D375E18-F7FF-4A6D-9994-EE1D0C3C05C3}.Release|Any CPU.Build.0 = Release|Any CPU + {4D375E18-F7FF-4A6D-9994-EE1D0C3C05C3}.Release|x64.ActiveCfg = Release|Any CPU + {4D375E18-F7FF-4A6D-9994-EE1D0C3C05C3}.Release|x64.Build.0 = Release|Any CPU + {4D375E18-F7FF-4A6D-9994-EE1D0C3C05C3}.Release|x86.ActiveCfg = Release|Any CPU + {4D375E18-F7FF-4A6D-9994-EE1D0C3C05C3}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -162,6 +190,7 @@ Global {542D3F73-7067-4873-89EF-FA0345E32C04} = {5BA1A1B2-2713-4CF4-9B63-087531598797} {4057015B-9D8A-411A-B7C2-3342D9F53BD0} = {5BA1A1B2-2713-4CF4-9B63-087531598797} {B024DBD2-FCF4-4C48-9EBE-09AA7AAF36FB} = {5BA1A1B2-2713-4CF4-9B63-087531598797} + {DD000D73-3AD5-465E-9AA9-64802CC88BF3} = {5BA1A1B2-2713-4CF4-9B63-087531598797} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {5E91C820-C650-4ACA-B064-05ECE0A74CE8} diff --git a/BlinkTest/BlinkTest.csproj b/BlinkTest/BlinkTest.csproj new file mode 100644 index 00000000..38bfbd64 --- /dev/null +++ b/BlinkTest/BlinkTest.csproj @@ -0,0 +1,13 @@ + + + + Exe + net6.0 + enable + enable + + + + + + diff --git a/BlinkTest/Program.cs b/BlinkTest/Program.cs new file mode 100644 index 00000000..89dd28e1 --- /dev/null +++ b/BlinkTest/Program.cs @@ -0,0 +1,38 @@ +using System.Text.Json; + +using BTCPayServer.Lightning.Blink; +using BTCPayServer.Lightning.Blink.Utilities; + +Uri apiUri = new Uri("https://api.blink.sv/graphql"); +// put your blink API key here +string apiKey = ""; + +// To include your stablesats in your total balance as converted to sats +// var blinkApiClient = new BlinkApiClient(apiUri, apiKey, true); +var blinkApiClient = new BlinkApiClient(apiUri, apiKey, false); + +var defaultWallet = await blinkApiClient.GetDefaultWallet(CancellationToken.None); +Console.WriteLine(defaultWallet.Id); + +var totalBalance = await blinkApiClient.GetBalance(CancellationToken.None); +Console.WriteLine(totalBalance.OffchainBalance.Local); + +var createIncoiceResponse = await blinkApiClient.CreateLnInvoice( + CancellationToken.None, + 100L, + TimeSpan.FromMinutes(10), + "a memo to remember"); + +string jsonResponse = JsonSerializer.Serialize(createIncoiceResponse, new JsonSerializerOptions { WriteIndented = true }); +Console.WriteLine(jsonResponse); + +var btcRate = await blinkApiClient.getBtcRate(CancellationToken.None); + +var convertedToSats = RateConversions.convertCentsToSats(createIncoiceResponse.Amount, btcRate); +var deviation = (createIncoiceResponse.AmountReceived - convertedToSats) / createIncoiceResponse.AmountReceived * 100; + +Console.WriteLine($"Invoice created with {createIncoiceResponse.Amount.MilliSatoshi} {defaultWallet.WalletCurrency}"); +Console.WriteLine($"Conversion rate is {btcRate.Base} / 10^{btcRate.Offset}"); +Console.WriteLine($"My own conversion: {convertedToSats} sats"); +Console.WriteLine($"Received sats: {createIncoiceResponse.AmountReceived.MilliSatoshi}"); +Console.WriteLine($"Deviation: %{deviation}"); diff --git a/src/BTCPayServer.Lightning.Blink/BTCPayServer.Lightning.Blink.csproj b/src/BTCPayServer.Lightning.Blink/BTCPayServer.Lightning.Blink.csproj new file mode 100644 index 00000000..97a54fb9 --- /dev/null +++ b/src/BTCPayServer.Lightning.Blink/BTCPayServer.Lightning.Blink.csproj @@ -0,0 +1,26 @@ + + + + net6.0 + enable + enable + + + + + + + + + + + + + + + + + + + + diff --git a/src/BTCPayServer.Lightning.Blink/BlinkApiClient.cs b/src/BTCPayServer.Lightning.Blink/BlinkApiClient.cs new file mode 100644 index 00000000..63594546 --- /dev/null +++ b/src/BTCPayServer.Lightning.Blink/BlinkApiClient.cs @@ -0,0 +1,234 @@ +using GraphQL; +using GraphQL.Client.Http; +using GraphQL.Client.Serializer.Newtonsoft; + +using BTCPayServer.Lightning.Blink.Models.Responses; +using BTCPayServer.Lightning.Blink.Models; +using BTCPayServer.Lightning.Blink.Utilities; + +namespace BTCPayServer.Lightning.Blink; + +public class BlinkApiClient +{ + private Uri _baseUri; + private string _apiKey; + private bool _includeStablesats; + + public BlinkApiClient(Uri baseUri, string apiKey, bool includeStablesats = false) + { + _baseUri = baseUri; + _apiKey = apiKey; + _includeStablesats = includeStablesats; + } + + public async Task GetDefaultWallet(CancellationToken cancellationToken) + { + string query = @" + query GetDefaultWallet { + me { + defaultAccount { + defaultWalletId + wallets { + balance + id + walletCurrency + } + } + } + }"; + + var defaultWalletRequest = new GraphQLRequest + { + Query = query, + OperationName = "GetDefaultWallet" + }; + + var response = await getGraphQLHttpClient().SendQueryAsync(defaultWalletRequest, cancellationToken); + string defaultWalletId = response.Data.Me.DefaultAccount.DefaultWalletId; + var defaultWallet = response.Data.Me.DefaultAccount.Wallets.Find((w) => w.Id == defaultWalletId); + + if (defaultWallet == null) + { + throw new Exception(string.Format("Could not find wallet information for wallet id {0}", defaultWalletId)); + } + + return new BlinkWallet + { + Id = defaultWallet.Id, + Balance = defaultWallet.Balance, + WalletCurrency = defaultWallet.WalletCurrency + }; + } + + public async Task getBtcRate(CancellationToken cancellationToken) + { + string query = @" + query RealtimePrice($currency: DisplayCurrency) { + realtimePrice(currency: $currency) { + btcSatPrice { + base, + offset + } + } + }"; + + var getBtcRateRequest = new GraphQLRequest + { + Query = query, + Variables = new + { + input = new + { + currency = "USD" + } + } + }; + + var response = await getGraphQLHttpClient().SendQueryAsync(getBtcRateRequest, cancellationToken); + + return response.Data.RealtimePrice.BtcSatPrice; + } + + public async Task GetBalance(CancellationToken cancellationToken) + { + string realtimePriceQuery = _includeStablesats + ? @"realtimePrice { + btcSatPrice { + base + offset + } + denominatorCurrency + }" + : ""; + + string query = $@" + query GetWalletBalances {{ + me {{ + defaultAccount {{ + defaultWalletId, + wallets {{ + balance + id + walletCurrency + }} + }} + }} + {realtimePriceQuery} + }}"; + + var balancesRequest = new GraphQLRequest + { + Query = query, + OperationName = "GetWalletBalances" + }; + + var response = await getGraphQLHttpClient().SendQueryAsync(balancesRequest, cancellationToken); + var responseData = response.Data; + + long totalBalanceSats = responseData.Me.DefaultAccount.Wallets.Sum(wallet => + { + if (wallet.WalletCurrency == "USD") + { + var realtimePrice = responseData.RealtimePrice; + + if (_includeStablesats + && realtimePrice != null + && realtimePrice.BtcSatPrice != null + && realtimePrice.DenominatorCurrency == "USD") + { + return RateConversions.convertCentsToSats(wallet.Balance, realtimePrice.BtcSatPrice); + } + + return 0; + } else + { + return wallet.Balance; + } + }); + + var offchain = new OffchainBalance + { + Local = new LightMoney(totalBalanceSats, LightMoneyUnit.MilliSatoshi) + }; + + return new LightningNodeBalance(null, offchain); + } + + public async Task CreateLnInvoice(CancellationToken cancellationToken, long amount, TimeSpan expiresIn, string? memo = null) + { + var wallet = await this.GetDefaultWallet(cancellationToken); + + string apiCall = wallet.WalletCurrency == "USD" ? "lnUsdInvoiceCreate" : "LnInvoiceCreate"; + + string query = $@" + mutation {apiCall}($input: {apiCall.Capitalize()}Input!) {{ + {apiCall}(input: $input) {{ + invoice {{ + paymentRequest + paymentHash + paymentSecret + satoshis + }} + errors {{ + message + }} + }} + }}"; + + var createLnInvoiceRequest = new GraphQLRequest + { + Query = query, + OperationName = apiCall, + Variables = new + { + input = new + { + amount, + walletId = wallet.Id, + expiresIn, + memo + } + } + }; + + var response = await getGraphQLHttpClient().SendMutationAsync(createLnInvoiceRequest, cancellationToken); + var graphQlErrors = response.Errors; + + if (graphQlErrors != null && graphQlErrors.Length > 0) + { + var messages = string.Join(", ", graphQlErrors.Select(e => e.Message)); + throw new Exception($"An error occured with invoice creation query: {messages}"); + } + + var invoiceData = response.Data.LnInvoiceCreate != null + ? response.Data.LnInvoiceCreate + : response.Data.LnUsdInvoiceCreate; + + if (invoiceData.Errors.Count > 0) + { + var messages = string.Join(", ", invoiceData.Errors.Select(e => e.Message)); + throw new Exception($"An error occured with invoice creation: {messages}"); + } + + return new LightningInvoice + { + Amount = amount, + PaymentHash = invoiceData.Invoice.PaymentHash, + ExpiresAt = DateTimeOffset.UtcNow.Add(expiresIn), + AmountReceived = invoiceData.Invoice.Satoshis, + Status = LightningInvoiceStatus.Unpaid, + BOLT11 = invoiceData.Invoice.PaymentRequest, + Preimage = invoiceData.Invoice.PaymentSecret + }; + } + + private GraphQLHttpClient getGraphQLHttpClient() { + var graphQLClient = new GraphQLHttpClient(_baseUri, new NewtonsoftJsonSerializer()); + + graphQLClient.HttpClient.DefaultRequestHeaders.Clear(); + graphQLClient.HttpClient.DefaultRequestHeaders.Add("X-API-KEY", _apiKey); + graphQLClient.HttpClient.DefaultRequestHeaders.Add("User-Agent", "BTCPayServer.Lightning.BlinkApiClient"); + + return graphQLClient; + } +} diff --git a/src/BTCPayServer.Lightning.Blink/BlinkLightningClient.cs b/src/BTCPayServer.Lightning.Blink/BlinkLightningClient.cs new file mode 100644 index 00000000..a02aa235 --- /dev/null +++ b/src/BTCPayServer.Lightning.Blink/BlinkLightningClient.cs @@ -0,0 +1,116 @@ +using System; +using NBitcoin; + +namespace BTCPayServer.Lightning.Blink +{ + public class BlinkLightningClient : ILightningClient + { + private BlinkApiClient _client; + + public BlinkLightningClient(Uri baseUri, string apiKey) + { + _client = new BlinkApiClient(baseUri, apiKey); + } + + public Task CancelInvoice(string invoiceId, CancellationToken cancellation = default) + { + throw new NotImplementedException(); + } + + public Task ConnectTo(NodeInfo nodeInfo, CancellationToken cancellation = default) + { + throw new NotImplementedException(); + } + + public Task CreateInvoice(LightMoney amount, string description, TimeSpan expiry, CancellationToken cancellation = default) + { + return _client.CreateLnInvoice(cancellation, amount.MilliSatoshi, expiry); + } + + public Task CreateInvoice(CreateInvoiceParams createInvoiceRequest, CancellationToken cancellation = default) + { + return _client.CreateLnInvoice(cancellation, createInvoiceRequest.Amount, createInvoiceRequest.Expiry, createInvoiceRequest.Description); + } + + public Task GetBalance(CancellationToken cancellation = default) + { + return _client.GetBalance(cancellation); + } + + public Task GetDepositAddress(CancellationToken cancellation = default) + { + throw new NotImplementedException(); + } + + public Task GetInfo(CancellationToken cancellation = default) + { + throw new NotImplementedException(); + } + + public Task GetInvoice(string invoiceId, CancellationToken cancellation = default) + { + throw new NotImplementedException(); + } + + public Task GetInvoice(uint256 paymentHash, CancellationToken cancellation = default) + { + throw new NotImplementedException(); + } + + public Task GetPayment(string paymentHash, CancellationToken cancellation = default) + { + throw new NotImplementedException(); + } + + public Task ListChannels(CancellationToken cancellation = default) + { + throw new NotImplementedException(); + } + + public Task Listen(CancellationToken cancellation = default) + { + throw new NotImplementedException(); + } + + public Task ListInvoices(CancellationToken cancellation = default) + { + throw new NotImplementedException(); + } + + public Task ListInvoices(ListInvoicesParams request, CancellationToken cancellation = default) + { + throw new NotImplementedException(); + } + + public Task ListPayments(CancellationToken cancellation = default) + { + throw new NotImplementedException(); + } + + public Task ListPayments(ListPaymentsParams request, CancellationToken cancellation = default) + { + throw new NotImplementedException(); + } + + public Task OpenChannel(OpenChannelRequest openChannelRequest, CancellationToken cancellation = default) + { + throw new NotImplementedException(); + } + + public Task Pay(PayInvoiceParams payParams, CancellationToken cancellation = default) + { + throw new NotImplementedException(); + } + + public Task Pay(string bolt11, PayInvoiceParams payParams, CancellationToken cancellation = default) + { + throw new NotImplementedException(); + } + + public Task Pay(string bolt11, CancellationToken cancellation = default) + { + throw new NotImplementedException(); + } + } +} + diff --git a/src/BTCPayServer.Lightning.Blink/Models/BlinkWallet.cs b/src/BTCPayServer.Lightning.Blink/Models/BlinkWallet.cs new file mode 100644 index 00000000..a697ea09 --- /dev/null +++ b/src/BTCPayServer.Lightning.Blink/Models/BlinkWallet.cs @@ -0,0 +1,10 @@ +namespace BTCPayServer.Lightning.Blink.Models +{ + public class BlinkWallet + { + public string Id { get; set; } + public string WalletCurrency { get; set; } + public long Balance { get; set; } + } +} + diff --git a/src/BTCPayServer.Lightning.Blink/Models/Responses/AccountResponse.cs b/src/BTCPayServer.Lightning.Blink/Models/Responses/AccountResponse.cs new file mode 100644 index 00000000..24a35fc0 --- /dev/null +++ b/src/BTCPayServer.Lightning.Blink/Models/Responses/AccountResponse.cs @@ -0,0 +1,9 @@ +namespace BTCPayServer.Lightning.Blink.Models.Responses +{ + public class AccountResponse + { + public string DefaultWalletId { get; set; } + public List Wallets { get; set; } + } +} + diff --git a/src/BTCPayServer.Lightning.Blink/Models/Responses/ErrorResponse.cs b/src/BTCPayServer.Lightning.Blink/Models/Responses/ErrorResponse.cs new file mode 100644 index 00000000..da2b98c0 --- /dev/null +++ b/src/BTCPayServer.Lightning.Blink/Models/Responses/ErrorResponse.cs @@ -0,0 +1,8 @@ +namespace BTCPayServer.Lightning.Blink.Models.Responses +{ + public class ErrorResponse + { + public string Message { get; set; } + } +} + diff --git a/src/BTCPayServer.Lightning.Blink/Models/Responses/GetBtcRateResponse.cs b/src/BTCPayServer.Lightning.Blink/Models/Responses/GetBtcRateResponse.cs new file mode 100644 index 00000000..dde271ca --- /dev/null +++ b/src/BTCPayServer.Lightning.Blink/Models/Responses/GetBtcRateResponse.cs @@ -0,0 +1,8 @@ +namespace BTCPayServer.Lightning.Blink.Models.Responses +{ + public class GetBtcRateResponse + { + public RealtimePriceResponse RealtimePrice { get; set; } + } +} + diff --git a/src/BTCPayServer.Lightning.Blink/Models/Responses/GetDefaultWalletIdResponse.cs b/src/BTCPayServer.Lightning.Blink/Models/Responses/GetDefaultWalletIdResponse.cs new file mode 100644 index 00000000..2f8d8cc5 --- /dev/null +++ b/src/BTCPayServer.Lightning.Blink/Models/Responses/GetDefaultWalletIdResponse.cs @@ -0,0 +1,9 @@ +using System; +namespace BTCPayServer.Lightning.Blink.Models.Responses +{ + public class GetDefaultWalletIdResponse + { + public MeResponse Me { get; set; } + } +} + diff --git a/src/BTCPayServer.Lightning.Blink/Models/Responses/GetWalletBalancesResponse.cs b/src/BTCPayServer.Lightning.Blink/Models/Responses/GetWalletBalancesResponse.cs new file mode 100644 index 00000000..c78a7fe2 --- /dev/null +++ b/src/BTCPayServer.Lightning.Blink/Models/Responses/GetWalletBalancesResponse.cs @@ -0,0 +1,9 @@ +namespace BTCPayServer.Lightning.Blink.Models.Responses +{ + public class GetWalletBalancesResponse + { + public MeResponse Me { get; set; } + public RealtimePriceResponse RealtimePrice { get; set; } + } +} + diff --git a/src/BTCPayServer.Lightning.Blink/Models/Responses/InvoiceResponse.cs b/src/BTCPayServer.Lightning.Blink/Models/Responses/InvoiceResponse.cs new file mode 100644 index 00000000..2eb9d529 --- /dev/null +++ b/src/BTCPayServer.Lightning.Blink/Models/Responses/InvoiceResponse.cs @@ -0,0 +1,11 @@ +namespace BTCPayServer.Lightning.Blink.Models.Responses +{ + public class InvoiceResponse + { + public string PaymentRequest { get; set; } + public string PaymentHash { get; set; } + public string PaymentSecret { get; set; } + public long Satoshis { get; set; } + } +} + diff --git a/src/BTCPayServer.Lightning.Blink/Models/Responses/LnInvoiceCreate.cs b/src/BTCPayServer.Lightning.Blink/Models/Responses/LnInvoiceCreate.cs new file mode 100644 index 00000000..14a96039 --- /dev/null +++ b/src/BTCPayServer.Lightning.Blink/Models/Responses/LnInvoiceCreate.cs @@ -0,0 +1,9 @@ +namespace BTCPayServer.Lightning.Blink.Models.Responses +{ + public class LnInvoiceCreate + { + public InvoiceResponse Invoice { get; set; } + public List Errors { get; set; } + } +} + diff --git a/src/BTCPayServer.Lightning.Blink/Models/Responses/LnInvoiceCreateResponse.cs b/src/BTCPayServer.Lightning.Blink/Models/Responses/LnInvoiceCreateResponse.cs new file mode 100644 index 00000000..75b265c7 --- /dev/null +++ b/src/BTCPayServer.Lightning.Blink/Models/Responses/LnInvoiceCreateResponse.cs @@ -0,0 +1,9 @@ +namespace BTCPayServer.Lightning.Blink.Models.Responses +{ + public class LnInvoiceCreateResponse + { + public LnInvoiceCreate LnInvoiceCreate { get; set; } + public LnInvoiceCreate LnUsdInvoiceCreate { get; set; } + } +} + diff --git a/src/BTCPayServer.Lightning.Blink/Models/Responses/MeResponse.cs b/src/BTCPayServer.Lightning.Blink/Models/Responses/MeResponse.cs new file mode 100644 index 00000000..eb356eb2 --- /dev/null +++ b/src/BTCPayServer.Lightning.Blink/Models/Responses/MeResponse.cs @@ -0,0 +1,7 @@ +namespace BTCPayServer.Lightning.Blink.Models.Responses +{ + public class MeResponse + { + public AccountResponse DefaultAccount { get; set; } + } +} diff --git a/src/BTCPayServer.Lightning.Blink/Models/Responses/PriceInfo.cs b/src/BTCPayServer.Lightning.Blink/Models/Responses/PriceInfo.cs new file mode 100644 index 00000000..3cd77610 --- /dev/null +++ b/src/BTCPayServer.Lightning.Blink/Models/Responses/PriceInfo.cs @@ -0,0 +1,9 @@ +namespace BTCPayServer.Lightning.Blink.Models.Responses +{ + public class PriceInfo + { + public long Base { get; set; } + public uint Offset { get; set; } + } +} + diff --git a/src/BTCPayServer.Lightning.Blink/Models/Responses/RealtimePriceResponse.cs b/src/BTCPayServer.Lightning.Blink/Models/Responses/RealtimePriceResponse.cs new file mode 100644 index 00000000..c8411ca7 --- /dev/null +++ b/src/BTCPayServer.Lightning.Blink/Models/Responses/RealtimePriceResponse.cs @@ -0,0 +1,10 @@ +namespace BTCPayServer.Lightning.Blink.Models.Responses +{ + public class RealtimePriceResponse + { + public PriceInfo BtcSatPrice { get; set; } + + public string DenominatorCurrency { get; set; } + } +} + diff --git a/src/BTCPayServer.Lightning.Blink/Models/Responses/WalletResponse.cs b/src/BTCPayServer.Lightning.Blink/Models/Responses/WalletResponse.cs new file mode 100644 index 00000000..9cff5698 --- /dev/null +++ b/src/BTCPayServer.Lightning.Blink/Models/Responses/WalletResponse.cs @@ -0,0 +1,9 @@ +namespace BTCPayServer.Lightning.Blink.Models.Responses +{ + public class WalletResponse + { + public long Balance { get; set; } + public string Id { get; set; } + public string WalletCurrency { get; set; } + } +} diff --git a/src/BTCPayServer.Lightning.Blink/Utilities/RateConversions.cs b/src/BTCPayServer.Lightning.Blink/Utilities/RateConversions.cs new file mode 100644 index 00000000..d95db9bb --- /dev/null +++ b/src/BTCPayServer.Lightning.Blink/Utilities/RateConversions.cs @@ -0,0 +1,16 @@ +using System; +using BTCPayServer.Lightning.Blink.Models.Responses; + +namespace BTCPayServer.Lightning.Blink.Utilities +{ + public class RateConversions + { + public static long convertCentsToSats(long cents, PriceInfo btcRate) + { + double divisionResult = (double)cents / btcRate.Base; + + return (long)(divisionResult * (long)Math.Pow(10, btcRate.Offset)); + } + } +} + diff --git a/src/BTCPayServer.Lightning.Blink/Utilities/StringExtensions.cs b/src/BTCPayServer.Lightning.Blink/Utilities/StringExtensions.cs new file mode 100644 index 00000000..f9179df2 --- /dev/null +++ b/src/BTCPayServer.Lightning.Blink/Utilities/StringExtensions.cs @@ -0,0 +1,16 @@ +namespace BTCPayServer.Lightning.Blink.Utilities +{ + public static class StringExtensions + { + public static string Capitalize(this string input) + { + if (string.IsNullOrEmpty(input)) + { + return input; + } + + return char.ToUpper(input[0]) + input.Substring(1); + } + } +} +