Compare commits
9 Commits
v1.12.4-rc
...
v1.12.5
Author | SHA1 | Date | |
---|---|---|---|
a86165ac0f | |||
e055ce627f | |||
497ec38641 | |||
2111b67e2c | |||
b96cfcd14d | |||
086f713752 | |||
fd67e09cf0 | |||
6f4ca47532 | |||
f97f23c8a5 |
@ -8,6 +8,7 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Rating;
|
||||
using ExchangeSharp;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
@ -16,7 +17,7 @@ namespace BTCPayServer.Services.Rates
|
||||
// Make sure that only one request is sent to kraken in general
|
||||
public class KrakenExchangeRateProvider : IRateProvider
|
||||
{
|
||||
public RateSourceInfo RateSourceInfo => new("kraken", "Kraken", "https://api.kraken.com/0/public/Ticker?pair=ATOMETH,ATOMEUR,ATOMUSD,ATOMXBT,BATETH,BATEUR,BATUSD,BATXBT,BCHEUR,BCHUSD,BCHXBT,DAIEUR,DAIUSD,DAIUSDT,DASHEUR,DASHUSD,DASHXBT,EOSETH,EOSXBT,ETHCHF,ETHDAI,ETHUSDC,ETHUSDT,GNOETH,GNOXBT,ICXETH,ICXEUR,ICXUSD,ICXXBT,LINKETH,LINKEUR,LINKUSD,LINKXBT,LSKETH,LSKEUR,LSKUSD,LSKXBT,NANOETH,NANOEUR,NANOUSD,NANOXBT,OMGETH,OMGEUR,OMGUSD,OMGXBT,PAXGETH,PAXGEUR,PAXGUSD,PAXGXBT,SCETH,SCEUR,SCUSD,SCXBT,USDCEUR,USDCUSD,USDCUSDT,USDTCAD,USDTEUR,USDTGBP,USDTZUSD,WAVESETH,WAVESEUR,WAVESUSD,WAVESXBT,XBTCHF,XBTDAI,XBTUSDC,XBTUSDT,XDGEUR,XDGUSD,XETCXETH,XETCXXBT,XETCZEUR,XETCZUSD,XETHXXBT,XETHZCAD,XETHZEUR,XETHZGBP,XETHZJPY,XETHZUSD,XLTCXXBT,XLTCZEUR,XLTCZUSD,XMLNXETH,XMLNXXBT,XMLNZEUR,XMLNZUSD,XREPXETH,XREPXXBT,XREPZEUR,XXBTZCAD,XXBTZEUR,XXBTZGBP,XXBTZJPY,XXBTZUSD,XXDGXXBT,XXLMXXBT,XXMRXXBT,XXMRZEUR,XXMRZUSD,XXRPXXBT,XXRPZEUR,XXRPZUSD,XZECXXBT,XZECZEUR,XZECZUSD");
|
||||
public RateSourceInfo RateSourceInfo => new("kraken", "Kraken", "https://api.kraken.com/0/public/Ticker");
|
||||
public HttpClient HttpClient
|
||||
{
|
||||
get
|
||||
@ -31,39 +32,6 @@ namespace BTCPayServer.Services.Rates
|
||||
|
||||
HttpClient _LocalClient;
|
||||
static readonly HttpClient _Client = new HttpClient();
|
||||
|
||||
// ExchangeSymbolToGlobalSymbol throws exception which would kill perf
|
||||
readonly ConcurrentDictionary<string, string> notFoundSymbols = new ConcurrentDictionary<string, string>(new Dictionary<string, string>()
|
||||
{
|
||||
{"ADAXBT","ADAXBT"},
|
||||
{ "BSVUSD","BSVUSD"},
|
||||
{ "QTUMEUR","QTUMEUR"},
|
||||
{ "QTUMXBT","QTUMXBT"},
|
||||
{ "EOSUSD","EOSUSD"},
|
||||
{ "XTZUSD","XTZUSD"},
|
||||
{ "XREPZUSD","XREPZUSD"},
|
||||
{ "ADAEUR","ADAEUR"},
|
||||
{ "ADAUSD","ADAUSD"},
|
||||
{ "GNOEUR","GNOEUR"},
|
||||
{ "XTZETH","XTZETH"},
|
||||
{ "XXRPZJPY","XXRPZJPY"},
|
||||
{ "XXRPZCAD","XXRPZCAD"},
|
||||
{ "XTZEUR","XTZEUR"},
|
||||
{ "QTUMETH","QTUMETH"},
|
||||
{ "XXLMZUSD","XXLMZUSD"},
|
||||
{ "QTUMCAD","QTUMCAD"},
|
||||
{ "QTUMUSD","QTUMUSD"},
|
||||
{ "XTZXBT","XTZXBT"},
|
||||
{ "GNOUSD","GNOUSD"},
|
||||
{ "ADAETH","ADAETH"},
|
||||
{ "ADACAD","ADACAD"},
|
||||
{ "XTZCAD","XTZCAD"},
|
||||
{ "BSVEUR","BSVEUR"},
|
||||
{ "XZECZJPY","XZECZJPY"},
|
||||
{ "XXLMZEUR","XXLMZEUR"},
|
||||
{"EOSEUR","EOSEUR"},
|
||||
{"BSVXBT","BSVXBT"}
|
||||
});
|
||||
string[] _Symbols = Array.Empty<string>();
|
||||
DateTimeOffset? _LastSymbolUpdate = null;
|
||||
readonly Dictionary<string, string> _TickerMapping = new Dictionary<string, string>()
|
||||
@ -76,47 +44,54 @@ namespace BTCPayServer.Services.Rates
|
||||
{ "ZEUR", "EUR" },
|
||||
{ "ZJPY", "JPY" },
|
||||
{ "ZCAD", "CAD" },
|
||||
{ "ZGBP", "GBP" }
|
||||
{ "ZGBP", "GBP" },
|
||||
{ "XXMR", "XMR" },
|
||||
{ "XETH", "ETH" },
|
||||
{ "USDC", "USDC" }, // On A=A purpose
|
||||
};
|
||||
|
||||
string Normalize(string ticker)
|
||||
{
|
||||
_TickerMapping.TryGetValue(ticker, out var normalized);
|
||||
return normalized ?? ticker;
|
||||
}
|
||||
|
||||
readonly ConcurrentDictionary<string, CurrencyPair> CachedCurrencyPairs = new ConcurrentDictionary<string, CurrencyPair>();
|
||||
private CurrencyPair GetCurrencyPair(string symbol)
|
||||
{
|
||||
if (CachedCurrencyPairs.TryGetValue(symbol, out var pair))
|
||||
return pair;
|
||||
var found = _TickerMapping.Where(t => symbol.StartsWith(t.Key, StringComparison.OrdinalIgnoreCase))
|
||||
.Select(t => new { KrakenTicker = t.Key, PayTicker = t.Value }).FirstOrDefault();
|
||||
if (found is not null)
|
||||
{
|
||||
pair = new CurrencyPair(found.PayTicker, Normalize(symbol.Substring(found.KrakenTicker.Length)));
|
||||
}
|
||||
if (pair is null)
|
||||
{
|
||||
found = _TickerMapping.Where(t => symbol.EndsWith(t.Key, StringComparison.OrdinalIgnoreCase))
|
||||
.Select(t => new { KrakenTicker = t.Key, PayTicker = t.Value }).FirstOrDefault();
|
||||
if (found is not null)
|
||||
pair = new CurrencyPair(Normalize(symbol.Substring(0, symbol.Length - found.KrakenTicker.Length)), found.PayTicker);
|
||||
}
|
||||
if (pair is null)
|
||||
CurrencyPair.TryParse(symbol, out pair);
|
||||
CachedCurrencyPairs.TryAdd(symbol, pair);
|
||||
return pair;
|
||||
}
|
||||
public async Task<PairRate[]> GetRatesAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var result = new List<PairRate>();
|
||||
var symbols = await GetSymbolsAsync(cancellationToken);
|
||||
var helper = (ExchangeKrakenAPI)await ExchangeAPI.GetExchangeAPIAsync<ExchangeKrakenAPI>();
|
||||
var normalizedPairsList = symbols.Where(s => !notFoundSymbols.ContainsKey(s)).Select(s => helper.NormalizeMarketSymbol(s)).ToList();
|
||||
var csvPairsList = string.Join(",", normalizedPairsList);
|
||||
JToken apiTickers = await MakeJsonRequestAsync<JToken>("/0/public/Ticker", null, new Dictionary<string, object> { { "pair", csvPairsList } }, cancellationToken: cancellationToken);
|
||||
JToken apiTickers = await MakeJsonRequestAsync<JToken>("/0/public/Ticker", null, null, cancellationToken: cancellationToken);
|
||||
foreach (string symbol in symbols)
|
||||
{
|
||||
var ticker = ConvertToExchangeTicker(symbol, apiTickers[symbol]);
|
||||
if (ticker != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
string global = null;
|
||||
var mapped1 = _TickerMapping.Where(t => symbol.StartsWith(t.Key, StringComparison.OrdinalIgnoreCase))
|
||||
.Select(t => new { KrakenTicker = t.Key, PayTicker = t.Value }).SingleOrDefault();
|
||||
if (mapped1 != null)
|
||||
{
|
||||
var p2 = symbol.Substring(mapped1.KrakenTicker.Length);
|
||||
if (_TickerMapping.TryGetValue(p2, out var mapped2))
|
||||
p2 = mapped2;
|
||||
global = $"{mapped1.PayTicker}_{p2}";
|
||||
}
|
||||
else
|
||||
{
|
||||
global = await helper.ExchangeMarketSymbolToGlobalMarketSymbolAsync(symbol);
|
||||
}
|
||||
if (CurrencyPair.TryParse(global, out var pair))
|
||||
result.Add(new PairRate(pair, new BidAsk(ticker.Bid, ticker.Ask)));
|
||||
else
|
||||
notFoundSymbols.TryAdd(symbol, symbol);
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
notFoundSymbols.TryAdd(symbol, symbol);
|
||||
}
|
||||
var pair = GetCurrencyPair(symbol);
|
||||
if (pair is not null)
|
||||
result.Add(new PairRate(pair, new BidAsk(ticker.Bid, ticker.Ask)));
|
||||
}
|
||||
}
|
||||
return result.ToArray();
|
||||
|
@ -24,7 +24,7 @@
|
||||
<PackageReference Include="Newtonsoft.Json.Schema" Version="3.0.15" />
|
||||
<PackageReference Include="Selenium.Support" Version="4.1.1" />
|
||||
<PackageReference Include="Selenium.WebDriver" Version="4.1.1" />
|
||||
<PackageReference Include="Selenium.WebDriver.ChromeDriver" Version="119.0.6045.10500" />
|
||||
<PackageReference Include="Selenium.WebDriver.ChromeDriver" Version="121.0.6167.8500" />
|
||||
<PackageReference Include="xunit" Version="2.6.6" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.6">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
|
@ -2332,10 +2332,27 @@ namespace BTCPayServer.Tests
|
||||
s.Server.ActivateLightning();
|
||||
await s.StartAsync();
|
||||
await s.Server.EnsureChannelsSetup();
|
||||
|
||||
// Create users
|
||||
var user = s.RegisterNewUser();
|
||||
var userAccount = s.AsTestAccount();
|
||||
s.GoToHome();
|
||||
s.Logout();
|
||||
s.GoToRegister();
|
||||
s.RegisterNewUser(true);
|
||||
|
||||
// Setup store and associate user
|
||||
s.CreateNewStore();
|
||||
s.GoToStore();
|
||||
s.AddLightningNode(LightningConnectionType.CLightning, false);
|
||||
s.GoToStore(StoreNavPages.Users);
|
||||
s.Driver.FindElement(By.Id("Email")).Clear();
|
||||
s.Driver.FindElement(By.Id("Email")).SendKeys(user);
|
||||
new SelectElement(s.Driver.FindElement(By.Id("Role"))).SelectByValue("Guest");
|
||||
s.Driver.FindElement(By.Id("AddUser")).Click();
|
||||
Assert.Contains("User added successfully", s.FindAlertMessage().Text);
|
||||
|
||||
// Setup POS
|
||||
s.Driver.FindElement(By.Id("StoreNav-CreatePointOfSale")).Click();
|
||||
s.Driver.FindElement(By.Id("AppName")).SendKeys(Guid.NewGuid().ToString());
|
||||
s.Driver.FindElement(By.Id("Create")).Click();
|
||||
@ -2360,6 +2377,8 @@ namespace BTCPayServer.Tests
|
||||
s.Driver.WaitForElement(By.ClassName("keypad"));
|
||||
|
||||
// basic checks
|
||||
var keypadUrl = s.Driver.Url;
|
||||
s.Driver.FindElement(By.Id("RecentTransactionsToggle"));
|
||||
Assert.Contains("EUR", s.Driver.FindElement(By.Id("Currency")).Text);
|
||||
Assert.Contains("0,00", s.Driver.FindElement(By.Id("Amount")).Text);
|
||||
Assert.Equal("", s.Driver.FindElement(By.Id("Calculation")).Text);
|
||||
@ -2405,6 +2424,19 @@ namespace BTCPayServer.Tests
|
||||
s.Driver.FindElement(By.Id("DetailsToggle")).Click();
|
||||
s.Driver.WaitForElement(By.Id("PaymentDetails-TotalFiat"));
|
||||
Assert.Contains("1 222,21 €", s.Driver.FindElement(By.Id("PaymentDetails-TotalFiat")).Text);
|
||||
|
||||
// Guest user can access recent transactions
|
||||
s.GoToHome();
|
||||
s.Logout();
|
||||
s.LogIn(user, userAccount.RegisterDetails.Password);
|
||||
s.GoToUrl(keypadUrl);
|
||||
s.Driver.FindElement(By.Id("RecentTransactionsToggle"));
|
||||
s.GoToHome();
|
||||
s.Logout();
|
||||
|
||||
// Unauthenticated user can't access recent transactions
|
||||
s.GoToUrl(keypadUrl);
|
||||
s.Driver.ElementDoesNotExist(By.Id("RecentTransactionsToggle"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -51,17 +51,31 @@ namespace BTCPayServer.HostedServices
|
||||
|
||||
// find all wallet objects that fit this transaction
|
||||
// that means see if there are any utxo objects that match in/outs and scripts/addresses that match outs
|
||||
var matchedObjects = transactionEvent.NewTransactionEvent.TransactionData.Transaction.Inputs
|
||||
.Select<TxIn, ObjectTypeId>(txIn => new ObjectTypeId(WalletObjectData.Types.Utxo, txIn.PrevOut.ToString()))
|
||||
.Concat(transactionEvent.NewTransactionEvent.Outputs.SelectMany<NBXplorer.Models.MatchedOutput, ObjectTypeId>(txOut =>
|
||||
|
||||
new[]{
|
||||
new ObjectTypeId(WalletObjectData.Types.Address, GetAddress(derivation, txOut, network).ToString()),
|
||||
new ObjectTypeId(WalletObjectData.Types.Utxo, new OutPoint(transactionEvent.NewTransactionEvent.TransactionData.TransactionHash, (uint)txOut.Index).ToString())
|
||||
var matchedObjects = new List<ObjectTypeId>();
|
||||
// Check if inputs match some UTXOs
|
||||
foreach (var txIn in transactionEvent.NewTransactionEvent.TransactionData.Transaction.Inputs)
|
||||
{
|
||||
matchedObjects.Add(new ObjectTypeId(WalletObjectData.Types.Utxo, txIn.PrevOut.ToString()));
|
||||
}
|
||||
|
||||
})).Distinct().ToArray();
|
||||
// Check if outputs match some UTXOs
|
||||
var walletOutputsByIndex = transactionEvent.NewTransactionEvent.Outputs.ToDictionary(o => (uint)o.Index);
|
||||
foreach (var txOut in transactionEvent.NewTransactionEvent.TransactionData.Transaction.Outputs.AsIndexedOutputs())
|
||||
{
|
||||
BitcoinAddress? address = null;
|
||||
// Technically, walletTxOut.Address can be calculated.
|
||||
// However in liquid for example, this returns the blinded address
|
||||
// rather than the unblinded one.
|
||||
if (walletOutputsByIndex.TryGetValue(txOut.N, out var walletTxOut))
|
||||
address = walletTxOut.Address;
|
||||
address ??= txOut.TxOut.ScriptPubKey.GetDestinationAddress(network.NBitcoinNetwork);
|
||||
if (address is not null)
|
||||
matchedObjects.Add(new ObjectTypeId(WalletObjectData.Types.Address, address.ToString()));
|
||||
matchedObjects.Add(new ObjectTypeId(WalletObjectData.Types.Utxo, new OutPoint(transactionEvent.NewTransactionEvent.TransactionData.TransactionHash, txOut.N).ToString()));
|
||||
}
|
||||
|
||||
var objs = await _walletRepository.GetWalletObjects(new GetWalletObjectsQuery() { TypesIds = matchedObjects });
|
||||
var objs = await _walletRepository.GetWalletObjects(new GetWalletObjectsQuery() { TypesIds = matchedObjects.Distinct().ToArray() });
|
||||
var links = new List<WalletObjectLinkData>();
|
||||
foreach (var walletObjectDatas in objs.GroupBy(data => data.Key.WalletId))
|
||||
{
|
||||
@ -112,11 +126,5 @@ namespace BTCPayServer.HostedServices
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private BitcoinAddress GetAddress(DerivationStrategyBase derivationStrategy, NBXplorer.Models.MatchedOutput txOut, BTCPayNetwork network)
|
||||
{
|
||||
// Old version of NBX doesn't give address in the event, so we need to guess
|
||||
return (txOut.Address ?? network.NBXplorerNetwork.CreateAddress(derivationStrategy, txOut.KeyPath, txOut.ScriptPubKey));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Abstractions.Constants;
|
||||
using BTCPayServer.Abstractions.Contracts;
|
||||
@ -74,10 +73,10 @@ namespace BTCPayServer.Security
|
||||
// resolve from app
|
||||
if (routeData.Values.TryGetValue("appId", out var vAppId) && vAppId is string appId)
|
||||
{
|
||||
app = await _appService.GetAppDataIfOwner(userId, appId);
|
||||
app = await _appService.GetAppData(userId, appId);
|
||||
if (storeId == null)
|
||||
{
|
||||
storeId = app?.StoreDataId ?? String.Empty;
|
||||
storeId = app?.StoreDataId ?? string.Empty;
|
||||
}
|
||||
else if (app?.StoreDataId != storeId)
|
||||
{
|
||||
@ -90,7 +89,7 @@ namespace BTCPayServer.Security
|
||||
paymentRequest = await _paymentRequestRepository.FindPaymentRequest(payReqId, userId);
|
||||
if (storeId == null)
|
||||
{
|
||||
storeId = paymentRequest?.StoreDataId ?? String.Empty;
|
||||
storeId = paymentRequest?.StoreDataId ?? string.Empty;
|
||||
}
|
||||
else if (paymentRequest?.StoreDataId != storeId)
|
||||
{
|
||||
@ -103,7 +102,7 @@ namespace BTCPayServer.Security
|
||||
invoice = await _invoiceRepository.GetInvoice(invoiceId);
|
||||
if (storeId == null)
|
||||
{
|
||||
storeId = invoice?.StoreId ?? String.Empty;
|
||||
storeId = invoice?.StoreId ?? string.Empty;
|
||||
}
|
||||
else if (invoice?.StoreId != storeId)
|
||||
{
|
||||
|
@ -371,10 +371,26 @@ namespace BTCPayServer.Services.Apps
|
||||
return null;
|
||||
await using var ctx = _ContextFactory.CreateContext();
|
||||
var app = await ctx.UserStore
|
||||
.Include(store => store.StoreRole)
|
||||
.Where(us => us.ApplicationUserId == userId && us.StoreRole.Permissions.Contains(Policies.CanModifyStoreSettings))
|
||||
.SelectMany(us => us.StoreData.Apps.Where(a => a.Id == appId))
|
||||
.FirstOrDefaultAsync();
|
||||
.Include(store => store.StoreRole)
|
||||
.Where(us => us.ApplicationUserId == userId && us.StoreRole.Permissions.Contains(Policies.CanModifyStoreSettings))
|
||||
.SelectMany(us => us.StoreData.Apps.Where(a => a.Id == appId))
|
||||
.FirstOrDefaultAsync();
|
||||
if (app == null)
|
||||
return null;
|
||||
if (type != null && type != app.AppType)
|
||||
return null;
|
||||
return app;
|
||||
}
|
||||
|
||||
public async Task<AppData?> GetAppData(string userId, string appId, string? type = null)
|
||||
{
|
||||
if (userId == null || appId == null)
|
||||
return null;
|
||||
await using var ctx = _ContextFactory.CreateContext();
|
||||
var app = await ctx.UserStore
|
||||
.Where(us => us.ApplicationUserId == userId && us.StoreData != null && us.StoreData.UserStores.Any(u => u.ApplicationUserId == userId))
|
||||
.SelectMany(us => us.StoreData.Apps.Where(a => a.Id == appId))
|
||||
.FirstOrDefaultAsync();
|
||||
if (app == null)
|
||||
return null;
|
||||
if (type != null && type != app.AppType)
|
||||
|
@ -393,8 +393,7 @@ namespace BTCPayServer.Services
|
||||
}
|
||||
else
|
||||
{
|
||||
var conn = ctx.Database.GetDbConnection();
|
||||
await conn.ExecuteAsync("INSERT INTO \"WalletObjectLinks\" VALUES (@WalletId, @AType, @AId, @BType, @BId, @Data::JSONB) ON CONFLICT DO NOTHING", links);
|
||||
await connection.ExecuteAsync("INSERT INTO \"WalletObjectLinks\" VALUES (@WalletId, @AType, @AId, @BType, @BId, @Data::JSONB) ON CONFLICT DO NOTHING", links);
|
||||
}
|
||||
}
|
||||
|
||||
@ -700,11 +699,9 @@ namespace BTCPayServer.Services
|
||||
walletObjectLinks ??= new List<WalletObjectLinkData>();
|
||||
var objs = walletObjects.Concat(ExtractObjectsFromLinks(walletObjectLinks).Except(walletObjects)).ToArray();
|
||||
await using var ctx = _ContextFactory.CreateContext();
|
||||
await using var connection = ctx.Database.GetDbConnection();
|
||||
await connection.OpenAsync();
|
||||
var connection = ctx.Database.GetDbConnection();
|
||||
await EnsureWalletObjects(ctx,connection, objs);
|
||||
await EnsureWalletObjectLinks(ctx,connection, walletObjectLinks);
|
||||
await connection.CloseAsync();
|
||||
}
|
||||
#nullable restore
|
||||
}
|
||||
|
@ -45,6 +45,10 @@
|
||||
<td>Electrum</td>
|
||||
<td>File ❯ Save backup (not encrypted with a password)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Sparrow</td>
|
||||
<td>File ❯ Export wallet… ❯ Electrum: export file…</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Wasabi</td>
|
||||
<td>Tools ❯ Wallet Manager ❯ Open Wallets Folder</td>
|
||||
|
@ -33,7 +33,7 @@
|
||||
</select>
|
||||
</div>
|
||||
<div class="ms-3">
|
||||
<button type="submit" role="button" class="btn btn-primary">Add User</button>
|
||||
<button type="submit" role="button" class="btn btn-primary" id="AddUser">Add User</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
@ -7,21 +7,7 @@ body {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
@media (max-width: 575px) {
|
||||
#wizard-navbar {
|
||||
margin-top: -35px;
|
||||
}
|
||||
}
|
||||
@media (min-width: 576px) {
|
||||
#wizard-navbar {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
padding: 24px;
|
||||
}
|
||||
margin-top: -2rem;
|
||||
}
|
||||
|
||||
#wizard-navbar a,
|
||||
|
@ -1,5 +1,5 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<Version>1.12.4</Version>
|
||||
<Version>1.12.5</Version>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
|
@ -1,5 +1,11 @@
|
||||
# Changelog
|
||||
|
||||
## 1.12.5
|
||||
|
||||
### Bug fixes
|
||||
|
||||
* Fix: Emergency fix for stores using Kraken rate source @NicolasDorier
|
||||
|
||||
## 1.12.4
|
||||
|
||||
### Bug fixes
|
||||
@ -15,6 +21,8 @@
|
||||
* Fix: Test webhook for payment requests (#5680) @Kukks
|
||||
* Fix: Sometimes importing a wallet file from Electrum would fail @NicolasDorier
|
||||
* Fix: Creating a Store as a Guest generates a 403 error (#5688 #5689) @dennisreimann
|
||||
* Fix: In Wallet Send, label were not applied to transactions (#5700) @NicolasDorier
|
||||
* Fix: "View recent invoices" in Keypad PoS should be accessible for authenticated Guest users (#5702 #5698) @dennisreimann
|
||||
|
||||
### Improvements
|
||||
|
||||
|
Reference in New Issue
Block a user