From b82abb066d2db35c534e25113f505f4f9bf7c0cb Mon Sep 17 00:00:00 2001 From: Oleg Semyonov Date: Sat, 25 Apr 2020 20:42:01 +0300 Subject: [PATCH 1/2] Fix typo --- BTCPayServer/Views/Wallets/WalletRescan.cshtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BTCPayServer/Views/Wallets/WalletRescan.cshtml b/BTCPayServer/Views/Wallets/WalletRescan.cshtml index 60fa6f5d3..8555d2eae 100644 --- a/BTCPayServer/Views/Wallets/WalletRescan.cshtml +++ b/BTCPayServer/Views/Wallets/WalletRescan.cshtml @@ -33,7 +33,7 @@ } else { -

This full node do not support rescan of the UTXO set

+

This full node does not support rescan of the UTXO set

} From 3051724ad8faa60c6a8cb38a2e0e9c4cad48a990 Mon Sep 17 00:00:00 2001 From: Oleg Semyonov Date: Fri, 17 Apr 2020 02:46:45 +0300 Subject: [PATCH 2/2] Add Argoneum --- .../BTCPayNetworkProvider.Argoneum.cs | 31 ++++++++++++++++++ .../Altcoins/BTCPayNetworkProvider.cs | 1 + BTCPayServer.Rating/Currencies.json | 7 ++++ .../Providers/ArgoneumRateProvider.cs | 27 +++++++++++++++ .../Services/RateProviderFactory.cs | 3 ++ BTCPayServer.Tests/UnitTest1.cs | 6 ++++ BTCPayServer/wwwroot/imlegacy/argoneum.png | Bin 0 -> 8083 bytes 7 files changed, 75 insertions(+) create mode 100644 BTCPayServer.Common/Altcoins/BTCPayNetworkProvider.Argoneum.cs create mode 100644 BTCPayServer.Rating/Providers/ArgoneumRateProvider.cs create mode 100644 BTCPayServer/wwwroot/imlegacy/argoneum.png diff --git a/BTCPayServer.Common/Altcoins/BTCPayNetworkProvider.Argoneum.cs b/BTCPayServer.Common/Altcoins/BTCPayNetworkProvider.Argoneum.cs new file mode 100644 index 000000000..60112eb4d --- /dev/null +++ b/BTCPayServer.Common/Altcoins/BTCPayNetworkProvider.Argoneum.cs @@ -0,0 +1,31 @@ +using NBitcoin; + +namespace BTCPayServer +{ + public partial class BTCPayNetworkProvider + { + public void InitArgoneum() + { + var nbxplorerNetwork = NBXplorerNetworkProvider.GetFromCryptoCode("AGM"); + Add(new BTCPayNetwork() + { + CryptoCode = nbxplorerNetwork.CryptoCode, + DisplayName = "Argoneum", + BlockExplorerLink = NetworkType == NetworkType.Mainnet + ? "https://chainz.cryptoid.info/agm/tx.dws?{0}" + : "https://chainz.cryptoid.info/agm-test/tx.dws?{0}", + NBXplorerNetwork = nbxplorerNetwork, + UriScheme = "argoneum", + DefaultRateRules = new[] + { + "AGM_X = AGM_BTC * BTC_X", + "AGM_BTC = argoneum(AGM_BTC)" + }, + CryptoImagePath = "imlegacy/argoneum.png", + DefaultSettings = BTCPayDefaultSettings.GetDefaultSettings(NetworkType), + CoinType = NetworkType == NetworkType.Mainnet ? new KeyPath("421'") + : new KeyPath("1'") + }); + } + } +} diff --git a/BTCPayServer.Common/Altcoins/BTCPayNetworkProvider.cs b/BTCPayServer.Common/Altcoins/BTCPayNetworkProvider.cs index 6456d6cfd..5b8e7c2a6 100644 --- a/BTCPayServer.Common/Altcoins/BTCPayNetworkProvider.cs +++ b/BTCPayServer.Common/Altcoins/BTCPayNetworkProvider.cs @@ -59,6 +59,7 @@ namespace BTCPayServer InitMonero(); InitPolis(); InitChaincoin(); + InitArgoneum(); // Assume that electrum mappings are same as BTC if not specified foreach (var network in _Networks.Values.OfType()) diff --git a/BTCPayServer.Rating/Currencies.json b/BTCPayServer.Rating/Currencies.json index aff33597f..738274c4e 100644 --- a/BTCPayServer.Rating/Currencies.json +++ b/BTCPayServer.Rating/Currencies.json @@ -1273,6 +1273,13 @@ "symbol":null, "crypto":true }, + { + "name":"Argoneum", + "code":"AGM", + "divisibility":8, + "symbol":null, + "crypto":true + }, { "name":"Satoshis", "code":"SATS", diff --git a/BTCPayServer.Rating/Providers/ArgoneumRateProvider.cs b/BTCPayServer.Rating/Providers/ArgoneumRateProvider.cs new file mode 100644 index 000000000..a0048f734 --- /dev/null +++ b/BTCPayServer.Rating/Providers/ArgoneumRateProvider.cs @@ -0,0 +1,27 @@ +using System; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using BTCPayServer.Rating; +using Newtonsoft.Json.Linq; + +namespace BTCPayServer.Services.Rates +{ + public class ArgoneumRateProvider : IRateProvider + { + private readonly HttpClient _httpClient; + public ArgoneumRateProvider(HttpClient httpClient) + { + _httpClient = httpClient ?? new HttpClient(); + } + + public async Task GetRatesAsync(CancellationToken cancellationToken) + { + // Example result: AGM to BTC rate: {"agm":5000000.000000} + var response = await _httpClient.GetAsync("https://rates.argoneum.net/rates/btc", cancellationToken); + var jobj = await response.Content.ReadAsAsync(cancellationToken); + var value = jobj["agm"].Value(); + return new[] { new PairRate(new CurrencyPair("BTC", "AGM"), new BidAsk(value)) }; + } + } +} diff --git a/BTCPayServer.Rating/Services/RateProviderFactory.cs b/BTCPayServer.Rating/Services/RateProviderFactory.cs index c2c44999d..a2671f1ba 100644 --- a/BTCPayServer.Rating/Services/RateProviderFactory.cs +++ b/BTCPayServer.Rating/Services/RateProviderFactory.cs @@ -81,6 +81,8 @@ namespace BTCPayServer.Services.Rates yield return new AvailableRateProvider("bitfinex", "Bitfinex", "https://api.bitfinex.com/v2/tickers?symbols=tBTCUSD,tLTCUSD,tLTCBTC,tETHUSD,tETHBTC,tETCBTC,tETCUSD,tRRTUSD,tRRTBTC,tZECUSD,tZECBTC,tXMRUSD,tXMRBTC,tDSHUSD,tDSHBTC,tBTCEUR,tBTCJPY,tXRPUSD,tXRPBTC,tIOTUSD,tIOTBTC,tIOTETH,tEOSUSD,tEOSBTC,tEOSETH,tSANUSD,tSANBTC,tSANETH,tOMGUSD,tOMGBTC,tOMGETH,tNEOUSD,tNEOBTC,tNEOETH,tETPUSD,tETPBTC,tETPETH,tQTMUSD,tQTMBTC,tQTMETH,tAVTUSD,tAVTBTC,tAVTETH,tEDOUSD,tEDOBTC,tEDOETH,tBTGUSD,tBTGBTC,tDATUSD,tDATBTC,tDATETH,tQSHUSD,tQSHBTC,tQSHETH,tYYWUSD,tYYWBTC,tYYWETH,tGNTUSD,tGNTBTC,tGNTETH,tSNTUSD,tSNTBTC,tSNTETH,tIOTEUR,tBATUSD,tBATBTC,tBATETH,tMNAUSD,tMNABTC,tMNAETH,tFUNUSD,tFUNBTC,tFUNETH,tZRXUSD,tZRXBTC,tZRXETH,tTNBUSD,tTNBBTC,tTNBETH,tSPKUSD,tSPKBTC,tSPKETH,tTRXUSD,tTRXBTC,tTRXETH,tRCNUSD,tRCNBTC,tRCNETH,tRLCUSD,tRLCBTC,tRLCETH,tAIDUSD,tAIDBTC,tAIDETH,tSNGUSD,tSNGBTC,tSNGETH,tREPUSD,tREPBTC,tREPETH,tELFUSD,tELFBTC,tELFETH,tNECUSD,tNECBTC,tNECETH,tBTCGBP,tETHEUR,tETHJPY,tETHGBP,tNEOEUR,tNEOJPY,tNEOGBP,tEOSEUR,tEOSJPY,tEOSGBP,tIOTJPY,tIOTGBP,tIOSUSD,tIOSBTC,tIOSETH,tAIOUSD,tAIOBTC,tAIOETH,tREQUSD,tREQBTC,tREQETH,tRDNUSD,tRDNBTC,tRDNETH,tLRCUSD,tLRCBTC,tLRCETH,tWAXUSD,tWAXBTC,tWAXETH,tDAIUSD,tDAIBTC,tDAIETH,tAGIUSD,tAGIBTC,tAGIETH,tBFTUSD,tBFTBTC,tBFTETH,tMTNUSD,tMTNBTC,tMTNETH,tODEUSD,tODEBTC,tODEETH,tANTUSD,tANTBTC,tANTETH,tDTHUSD,tDTHBTC,tDTHETH,tMITUSD,tMITBTC,tMITETH,tSTJUSD,tSTJBTC,tSTJETH,tXLMUSD,tXLMEUR,tXLMJPY,tXLMGBP,tXLMBTC,tXLMETH,tXVGUSD,tXVGEUR,tXVGJPY,tXVGGBP,tXVGBTC,tXVGETH,tBCIUSD,tBCIBTC,tMKRUSD,tMKRBTC,tMKRETH,tKNCUSD,tKNCBTC,tKNCETH,tPOAUSD,tPOABTC,tPOAETH,tEVTUSD,tLYMUSD,tLYMBTC,tLYMETH,tUTKUSD,tUTKBTC,tUTKETH,tVEEUSD,tVEEBTC,tVEEETH,tDADUSD,tDADBTC,tDADETH,tORSUSD,tORSBTC,tORSETH,tAUCUSD,tAUCBTC,tAUCETH,tPOYUSD,tPOYBTC,tPOYETH,tFSNUSD,tFSNBTC,tFSNETH,tCBTUSD,tCBTBTC,tCBTETH,tZCNUSD,tZCNBTC,tZCNETH,tSENUSD,tSENBTC,tSENETH,tNCAUSD,tNCABTC,tNCAETH,tCNDUSD,tCNDBTC,tCNDETH,tCTXUSD,tCTXBTC,tCTXETH,tPAIUSD,tPAIBTC,tSEEUSD,tSEEBTC,tSEEETH,tESSUSD,tESSBTC,tESSETH,tATMUSD,tATMBTC,tATMETH,tHOTUSD,tHOTBTC,tHOTETH,tDTAUSD,tDTABTC,tDTAETH,tIQXUSD,tIQXBTC,tIQXEOS,tWPRUSD,tWPRBTC,tWPRETH,tZILUSD,tZILBTC,tZILETH,tBNTUSD,tBNTBTC,tBNTETH,tABSUSD,tABSETH,tXRAUSD,tXRAETH,tMANUSD,tMANETH,tBBNUSD,tBBNETH,tNIOUSD,tNIOETH,tDGXUSD,tDGXETH,tVETUSD,tVETBTC,tVETETH,tUTNUSD,tUTNETH,tTKNUSD,tTKNETH,tGOTUSD,tGOTEUR,tGOTETH,tXTZUSD,tXTZBTC,tCNNUSD,tCNNETH,tBOXUSD,tBOXETH,tTRXEUR,tTRXGBP,tTRXJPY,tMGOUSD,tMGOETH,tRTEUSD,tRTEETH,tYGGUSD,tYGGETH,tMLNUSD,tMLNETH,tWTCUSD,tWTCETH,tCSXUSD,tCSXETH,tOMNUSD,tOMNBTC,tINTUSD,tINTETH,tDRNUSD,tDRNETH,tPNKUSD,tPNKETH,tDGBUSD,tDGBBTC,tBSVUSD,tBSVBTC,tBABUSD,tBABBTC,tWLOUSD,tWLOXLM,tVLDUSD,tVLDETH,tENJUSD,tENJETH,tONLUSD,tONLETH,tRBTUSD,tRBTBTC,tUSTUSD,tEUTEUR,tEUTUSD,tGSDUSD,tUDCUSD,tTSDUSD,tPAXUSD,tRIFUSD,tRIFBTC,tPASUSD,tPASETH,tVSYUSD,tVSYBTC,tZRXDAI,tMKRDAI,tOMGDAI,tBTTUSD,tBTTBTC,tBTCUST,tETHUST,tCLOUSD,tCLOBTC,tIMPUSD,tIMPETH,tLTCUST,tEOSUST,tBABUST,tSCRUSD,tSCRETH,tGNOUSD,tGNOETH,tGENUSD,tGENETH,tATOUSD,tATOBTC,tATOETH,tWBTUSD,tXCHUSD,tEUSUSD,tWBTETH,tXCHETH,tEUSETH,tLEOUSD,tLEOBTC,tLEOUST,tLEOEOS,tLEOETH,tASTUSD,tASTETH,tFOAUSD,tFOAETH,tUFRUSD,tUFRETH,tZBTUSD,tZBTUST,tOKBUSD,tUSKUSD,tGTXUSD,tKANUSD,tOKBUST,tOKBETH,tOKBBTC,tUSKUST,tUSKETH,tUSKBTC,tUSKEOS,tGTXUST,tKANUST,tAMPUSD,tALGUSD,tALGBTC,tALGUST,tBTCXCH,tSWMUSD,tSWMETH,tTRIUSD,tTRIETH,tLOOUSD,tLOOETH,tAMPUST,tDUSK:USD,tDUSK:BTC,tUOSUSD,tUOSBTC,tRRBUSD,tRRBUST,tDTXUSD,tDTXUST,tAMPBTC,tFTTUSD,tFTTUST,tPAXUST,tUDCUST,tTSDUST,tBTC:CNHT,tUST:CNHT,tCNH:CNHT,tCHZUSD,tCHZUST,tBTCF0:USTF0,tETHF0:USTF0"); yield return new AvailableRateProvider("okex", "OKEx", "https://www.okex.com/api/futures/v3/instruments/ticker"); yield return new AvailableRateProvider("coinbasepro", "Coinbase Pro", "https://api.pro.coinbase.com/products"); + + yield return new AvailableRateProvider("argoneum", "Argoneum", "https://rates.argoneum.net/rates"); } void InitExchanges() { @@ -99,6 +101,7 @@ namespace BTCPayServer.Services.Rates Providers.Add("bitpay", new BitpayRateProvider(_httpClientFactory?.CreateClient("EXCHANGE_BITPAY"))); Providers.Add("bitflyer", new BitflyerRateProvider(_httpClientFactory?.CreateClient("EXCHANGE_BITFLYER"))); Providers.Add("polispay", new PolisRateProvider(_httpClientFactory?.CreateClient("EXCHANGE_POLIS"))); + Providers.Add("argoneum", new ArgoneumRateProvider(_httpClientFactory?.CreateClient("EXCHANGE_ARGONEUM"))); // Backward compatibility: coinaverage should be using coingecko to prevent stores from breaking diff --git a/BTCPayServer.Tests/UnitTest1.cs b/BTCPayServer.Tests/UnitTest1.cs index e83befab1..6be6bdc52 100644 --- a/BTCPayServer.Tests/UnitTest1.cs +++ b/BTCPayServer.Tests/UnitTest1.cs @@ -3398,6 +3398,12 @@ normal: e => e.CurrencyPair == new CurrencyPair("BTC", "POLIS") && e.BidAsk.Bid > 1.0m); // 1BTC will always be more than 1 POLIS } + else if (result.ExpectedName == "argoneum") + { + Assert.Contains(exchangeRates.ByExchange[result.ExpectedName], + e => e.CurrencyPair == new CurrencyPair("BTC", "AGM") && + e.BidAsk.Bid > 1.0m); // 1 BTC will always be more than 1 AGM + } else { // This check if the currency pair is using right currency pair diff --git a/BTCPayServer/wwwroot/imlegacy/argoneum.png b/BTCPayServer/wwwroot/imlegacy/argoneum.png new file mode 100644 index 0000000000000000000000000000000000000000..29f8319845816430e416d98c9261c547f578ea7b GIT binary patch literal 8083 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