Add .NET SDK standardized code format check (#29)

This commit is contained in:
napoly 2025-06-13 05:16:20 +02:00
parent d327426e9a
commit bbada8c2d6
61 changed files with 586 additions and 130 deletions

364
.editorconfig Normal file
View file

@ -0,0 +1,364 @@
root = true
# All files
[*]
indent_style = space
# Xml files
[*.xml]
indent_size = 2
# C# files
[*.cs]
#### Core EditorConfig Options ####
# Indentation and spacing
indent_size = 4
tab_width = 4
# New line preferences
end_of_line = crlf
insert_final_newline = false
#### .NET Coding Conventions ####
[*.{cs,vb}]
# Organize usings
dotnet_separate_import_directive_groups = true
dotnet_sort_system_directives_first = true
file_header_template = unset
# this. and Me. preferences
dotnet_style_qualification_for_event = false:silent
dotnet_style_qualification_for_field = false:silent
dotnet_style_qualification_for_method = false:silent
dotnet_style_qualification_for_property = false:silent
# Language keywords vs BCL types preferences
dotnet_style_predefined_type_for_locals_parameters_members = true:silent
dotnet_style_predefined_type_for_member_access = true:silent
# Parentheses preferences
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent
dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent
dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent
# Modifier preferences
dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent
# Expression-level preferences
dotnet_style_coalesce_expression = true:suggestion
dotnet_style_collection_initializer = true:suggestion
dotnet_style_explicit_tuple_names = true:suggestion
dotnet_style_null_propagation = true:suggestion
dotnet_style_object_initializer = true:suggestion
dotnet_style_operator_placement_when_wrapping = beginning_of_line
dotnet_style_prefer_auto_properties = true:suggestion
dotnet_style_prefer_compound_assignment = true:suggestion
dotnet_style_prefer_conditional_expression_over_assignment = true:suggestion
dotnet_style_prefer_conditional_expression_over_return = true:suggestion
dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
dotnet_style_prefer_inferred_tuple_names = true:suggestion
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
dotnet_style_prefer_simplified_boolean_expressions = true:suggestion
dotnet_style_prefer_simplified_interpolation = true:suggestion
# Field preferences
dotnet_style_readonly_field = true:warning
# Parameter preferences
dotnet_code_quality_unused_parameters = all:suggestion
# Suppression preferences
dotnet_remove_unnecessary_suppression_exclusions = none
#### C# Coding Conventions ####
[*.cs]
# var preferences
csharp_style_var_elsewhere = false:silent
csharp_style_var_for_built_in_types = false:silent
csharp_style_var_when_type_is_apparent = false:silent
# Expression-bodied members
csharp_style_expression_bodied_accessors = true:silent
csharp_style_expression_bodied_constructors = false:silent
csharp_style_expression_bodied_indexers = true:silent
csharp_style_expression_bodied_lambdas = true:suggestion
csharp_style_expression_bodied_local_functions = false:silent
csharp_style_expression_bodied_methods = false:silent
csharp_style_expression_bodied_operators = false:silent
csharp_style_expression_bodied_properties = true:silent
# Pattern matching preferences
csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
csharp_style_prefer_not_pattern = true:suggestion
csharp_style_prefer_pattern_matching = true:silent
csharp_style_prefer_switch_expression = true:suggestion
# Null-checking preferences
csharp_style_conditional_delegate_call = true:suggestion
# Modifier preferences
csharp_prefer_static_local_function = true:warning
csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:silent
# Code-block preferences
csharp_prefer_braces = true:silent
csharp_prefer_simple_using_statement = true:suggestion
# Expression-level preferences
csharp_prefer_simple_default_expression = true:suggestion
csharp_style_deconstructed_variable_declaration = true:suggestion
csharp_style_inlined_variable_declaration = true:suggestion
csharp_style_pattern_local_over_anonymous_function = true:suggestion
csharp_style_prefer_index_operator = true:suggestion
csharp_style_prefer_range_operator = true:suggestion
csharp_style_throw_expression = true:suggestion
csharp_style_unused_value_assignment_preference = discard_variable:suggestion
csharp_style_unused_value_expression_statement_preference = discard_variable:silent
# 'using' directive preferences
csharp_using_directive_placement = outside_namespace:silent
#### C# Formatting Rules ####
# New line preferences
csharp_new_line_before_catch = true
csharp_new_line_before_else = true
csharp_new_line_before_finally = true
csharp_new_line_before_members_in_anonymous_types = true
csharp_new_line_before_members_in_object_initializers = true
csharp_new_line_before_open_brace = all
csharp_new_line_between_query_expression_clauses = true
# Indentation preferences
csharp_indent_block_contents = true
csharp_indent_braces = false
csharp_indent_case_contents = true
csharp_indent_case_contents_when_block = true
csharp_indent_labels = one_less_than_current
csharp_indent_switch_labels = true
# Space preferences
csharp_space_after_cast = false
csharp_space_after_colon_in_inheritance_clause = true
csharp_space_after_comma = true
csharp_space_after_dot = false
csharp_space_after_keywords_in_control_flow_statements = true
csharp_space_after_semicolon_in_for_statement = true
csharp_space_around_binary_operators = before_and_after
csharp_space_around_declaration_statements = false
csharp_space_before_colon_in_inheritance_clause = true
csharp_space_before_comma = false
csharp_space_before_dot = false
csharp_space_before_open_square_brackets = false
csharp_space_before_semicolon_in_for_statement = false
csharp_space_between_empty_square_brackets = false
csharp_space_between_method_call_empty_parameter_list_parentheses = false
csharp_space_between_method_call_name_and_opening_parenthesis = false
csharp_space_between_method_call_parameter_list_parentheses = false
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
csharp_space_between_method_declaration_name_and_open_parenthesis = false
csharp_space_between_method_declaration_parameter_list_parentheses = false
csharp_space_between_parentheses = false
csharp_space_between_square_brackets = false
# Wrapping preferences
csharp_preserve_single_line_blocks = true
csharp_preserve_single_line_statements = true
#### Naming styles ####
[*.{cs,vb}]
# Naming rules
dotnet_naming_rule.types_and_namespaces_should_be_pascalcase.severity = suggestion
dotnet_naming_rule.types_and_namespaces_should_be_pascalcase.symbols = types_and_namespaces
dotnet_naming_rule.types_and_namespaces_should_be_pascalcase.style = pascalcase
dotnet_naming_rule.interfaces_should_be_ipascalcase.severity = suggestion
dotnet_naming_rule.interfaces_should_be_ipascalcase.symbols = interfaces
dotnet_naming_rule.interfaces_should_be_ipascalcase.style = ipascalcase
dotnet_naming_rule.type_parameters_should_be_tpascalcase.severity = suggestion
dotnet_naming_rule.type_parameters_should_be_tpascalcase.symbols = type_parameters
dotnet_naming_rule.type_parameters_should_be_tpascalcase.style = tpascalcase
dotnet_naming_rule.methods_should_be_pascalcase.severity = suggestion
dotnet_naming_rule.methods_should_be_pascalcase.symbols = methods
dotnet_naming_rule.methods_should_be_pascalcase.style = pascalcase
dotnet_naming_rule.properties_should_be_pascalcase.severity = suggestion
dotnet_naming_rule.properties_should_be_pascalcase.symbols = properties
dotnet_naming_rule.properties_should_be_pascalcase.style = pascalcase
dotnet_naming_rule.events_should_be_pascalcase.severity = suggestion
dotnet_naming_rule.events_should_be_pascalcase.symbols = events
dotnet_naming_rule.events_should_be_pascalcase.style = pascalcase
dotnet_naming_rule.local_variables_should_be_camelcase.severity = suggestion
dotnet_naming_rule.local_variables_should_be_camelcase.symbols = local_variables
dotnet_naming_rule.local_variables_should_be_camelcase.style = camelcase
dotnet_naming_rule.local_constants_should_be_camelcase.severity = suggestion
dotnet_naming_rule.local_constants_should_be_camelcase.symbols = local_constants
dotnet_naming_rule.local_constants_should_be_camelcase.style = camelcase
dotnet_naming_rule.parameters_should_be_camelcase.severity = suggestion
dotnet_naming_rule.parameters_should_be_camelcase.symbols = parameters
dotnet_naming_rule.parameters_should_be_camelcase.style = camelcase
dotnet_naming_rule.public_fields_should_be_pascalcase.severity = suggestion
dotnet_naming_rule.public_fields_should_be_pascalcase.symbols = public_fields
dotnet_naming_rule.public_fields_should_be_pascalcase.style = pascalcase
dotnet_naming_rule.private_fields_should_be__camelcase.severity = suggestion
dotnet_naming_rule.private_fields_should_be__camelcase.symbols = private_fields
dotnet_naming_rule.private_fields_should_be__camelcase.style = _camelcase
dotnet_naming_rule.private_static_fields_should_be_s_camelcase.severity = suggestion
dotnet_naming_rule.private_static_fields_should_be_s_camelcase.symbols = private_static_fields
dotnet_naming_rule.private_static_fields_should_be_s_camelcase.style = s_camelcase
dotnet_naming_rule.public_constant_fields_should_be_pascalcase.severity = suggestion
dotnet_naming_rule.public_constant_fields_should_be_pascalcase.symbols = public_constant_fields
dotnet_naming_rule.public_constant_fields_should_be_pascalcase.style = pascalcase
dotnet_naming_rule.private_constant_fields_should_be_pascalcase.severity = suggestion
dotnet_naming_rule.private_constant_fields_should_be_pascalcase.symbols = private_constant_fields
dotnet_naming_rule.private_constant_fields_should_be_pascalcase.style = pascalcase
dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.severity = suggestion
dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.symbols = public_static_readonly_fields
dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.style = pascalcase
dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.severity = suggestion
dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.symbols = private_static_readonly_fields
dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.style = pascalcase
dotnet_naming_rule.enums_should_be_pascalcase.severity = suggestion
dotnet_naming_rule.enums_should_be_pascalcase.symbols = enums
dotnet_naming_rule.enums_should_be_pascalcase.style = pascalcase
dotnet_naming_rule.local_functions_should_be_pascalcase.severity = suggestion
dotnet_naming_rule.local_functions_should_be_pascalcase.symbols = local_functions
dotnet_naming_rule.local_functions_should_be_pascalcase.style = pascalcase
dotnet_naming_rule.non_field_members_should_be_pascalcase.severity = suggestion
dotnet_naming_rule.non_field_members_should_be_pascalcase.symbols = non_field_members
dotnet_naming_rule.non_field_members_should_be_pascalcase.style = pascalcase
# Symbol specifications
dotnet_naming_symbols.interfaces.applicable_kinds = interface
dotnet_naming_symbols.interfaces.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.interfaces.required_modifiers =
dotnet_naming_symbols.enums.applicable_kinds = enum
dotnet_naming_symbols.enums.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.enums.required_modifiers =
dotnet_naming_symbols.events.applicable_kinds = event
dotnet_naming_symbols.events.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.events.required_modifiers =
dotnet_naming_symbols.methods.applicable_kinds = method
dotnet_naming_symbols.methods.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.methods.required_modifiers =
dotnet_naming_symbols.properties.applicable_kinds = property
dotnet_naming_symbols.properties.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.properties.required_modifiers =
dotnet_naming_symbols.public_fields.applicable_kinds = field
dotnet_naming_symbols.public_fields.applicable_accessibilities = public, internal
dotnet_naming_symbols.public_fields.required_modifiers =
dotnet_naming_symbols.private_fields.applicable_kinds = field
dotnet_naming_symbols.private_fields.applicable_accessibilities = private, protected, protected_internal, private_protected
dotnet_naming_symbols.private_fields.required_modifiers =
dotnet_naming_symbols.private_static_fields.applicable_kinds = field
dotnet_naming_symbols.private_static_fields.applicable_accessibilities = private, protected, protected_internal, private_protected
dotnet_naming_symbols.private_static_fields.required_modifiers = static
dotnet_naming_symbols.types_and_namespaces.applicable_kinds = namespace, class, struct, interface, enum
dotnet_naming_symbols.types_and_namespaces.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.types_and_namespaces.required_modifiers =
dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.non_field_members.required_modifiers =
dotnet_naming_symbols.type_parameters.applicable_kinds = namespace
dotnet_naming_symbols.type_parameters.applicable_accessibilities = *
dotnet_naming_symbols.type_parameters.required_modifiers =
dotnet_naming_symbols.private_constant_fields.applicable_kinds = field
dotnet_naming_symbols.private_constant_fields.applicable_accessibilities = private, protected, protected_internal, private_protected
dotnet_naming_symbols.private_constant_fields.required_modifiers = const
dotnet_naming_symbols.local_variables.applicable_kinds = local
dotnet_naming_symbols.local_variables.applicable_accessibilities = local
dotnet_naming_symbols.local_variables.required_modifiers =
dotnet_naming_symbols.local_constants.applicable_kinds = local
dotnet_naming_symbols.local_constants.applicable_accessibilities = local
dotnet_naming_symbols.local_constants.required_modifiers = const
dotnet_naming_symbols.parameters.applicable_kinds = parameter
dotnet_naming_symbols.parameters.applicable_accessibilities = *
dotnet_naming_symbols.parameters.required_modifiers =
dotnet_naming_symbols.public_constant_fields.applicable_kinds = field
dotnet_naming_symbols.public_constant_fields.applicable_accessibilities = public, internal
dotnet_naming_symbols.public_constant_fields.required_modifiers = const
dotnet_naming_symbols.public_static_readonly_fields.applicable_kinds = field
dotnet_naming_symbols.public_static_readonly_fields.applicable_accessibilities = public, internal
dotnet_naming_symbols.public_static_readonly_fields.required_modifiers = readonly, static
dotnet_naming_symbols.private_static_readonly_fields.applicable_kinds = field
dotnet_naming_symbols.private_static_readonly_fields.applicable_accessibilities = private, protected, protected_internal, private_protected
dotnet_naming_symbols.private_static_readonly_fields.required_modifiers = readonly, static
dotnet_naming_symbols.local_functions.applicable_kinds = local_function
dotnet_naming_symbols.local_functions.applicable_accessibilities = *
dotnet_naming_symbols.local_functions.required_modifiers =
# Naming styles
dotnet_naming_style.pascalcase.required_prefix =
dotnet_naming_style.pascalcase.required_suffix =
dotnet_naming_style.pascalcase.word_separator =
dotnet_naming_style.pascalcase.capitalization = pascal_case
dotnet_naming_style.ipascalcase.required_prefix = I
dotnet_naming_style.ipascalcase.required_suffix =
dotnet_naming_style.ipascalcase.word_separator =
dotnet_naming_style.ipascalcase.capitalization = pascal_case
dotnet_naming_style.tpascalcase.required_prefix = T
dotnet_naming_style.tpascalcase.required_suffix =
dotnet_naming_style.tpascalcase.word_separator =
dotnet_naming_style.tpascalcase.capitalization = pascal_case
dotnet_naming_style._camelcase.required_prefix = _
dotnet_naming_style._camelcase.required_suffix =
dotnet_naming_style._camelcase.word_separator =
dotnet_naming_style._camelcase.capitalization = camel_case
dotnet_naming_style.camelcase.required_prefix =
dotnet_naming_style.camelcase.required_suffix =
dotnet_naming_style.camelcase.word_separator =
dotnet_naming_style.camelcase.capitalization = camel_case
dotnet_naming_style.s_camelcase.required_prefix = s_
dotnet_naming_style.s_camelcase.required_suffix =
dotnet_naming_style.s_camelcase.word_separator =
dotnet_naming_style.s_camelcase.capitalization = camel_case

5
.gitattributes vendored Normal file
View file

@ -0,0 +1,5 @@
# Windows CRLF endings expected on server
* text eol=crlf
# Explicitly set Unix LF endings for .sh files
*.sh text eol=lf

32
.github/workflows/dotnet-format.yml vendored Normal file
View file

@ -0,0 +1,32 @@
name: Code format check
on:
push:
branches:
- "master"
pull_request:
branches:
- "master"
workflow_dispatch:
jobs:
lint:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.0.x
- name: Restore dependencies
run: dotnet restore
- name: Code format check
run: |
dotnet tool install -g dotnet-format
dotnet format btcpay-monero-plugin.sln --no-restore --verify-no-changes --exclude submodules/* --verbosity diagnostic

1
.gitignore vendored
View file

@ -5,3 +5,4 @@ Plugins/packed
.vs/
coverage
nuget-packages
**/.AssemblyAttributes

View file

@ -1,6 +1,7 @@
using Xunit.Abstractions;
using BTCPayServer.Tests;
using Xunit.Abstractions;
namespace BTCPayServer.Plugins.IntegrationTests.Monero
{
public class MoneroAndBitcoinIntegrationTestBase : UnitTestBase

View file

@ -1,4 +1,5 @@
using BTCPayServer.Tests;
using Xunit;
using Xunit.Abstractions;

View file

@ -1,4 +1,5 @@
using BTCPayServer.Plugins.Monero.Configuration;
using Xunit;
namespace BTCPayServer.Plugins.UnitTests.Monero.Configuration

View file

@ -1,4 +1,5 @@
using BTCPayServer.Plugins.Monero.Payments;
using Xunit;
namespace BTCPayServer.Plugins.UnitTests.Monero.Payments

View file

@ -1,5 +1,7 @@
using BTCPayServer.Plugins.Monero.RPC.Models;
using Newtonsoft.Json;
using Xunit;
namespace BTCPayServer.Plugins.UnitTests.Monero.RPC.Models

View file

@ -1,4 +1,5 @@
using BTCPayServer.Plugins.Monero.RPC;
using Xunit;
namespace BTCPayServer.Plugins.UnitTests.Monero.RPC

View file

@ -1,7 +1,9 @@
using System.Globalization;
using Xunit;
using BTCPayServer.Plugins.Monero.Utils;
using Xunit;
namespace BTCPayServer.Plugins.UnitTests.Monero.Utils
{
public class MoneroMoneyTests

View file

@ -1,5 +1,6 @@
using BTCPayServer.Payments;
using BTCPayServer.Plugins.Monero.ViewModels;
using Xunit;
namespace BTCPayServer.Plugins.UnitTests.Monero.ViewModels

View file

@ -1,5 +1,6 @@
using BTCPayServer.Filters;
using BTCPayServer.Plugins.Monero.RPC;
using Microsoft.AspNetCore.Mvc;
namespace BTCPayServer.Plugins.Monero.Controllers

View file

@ -6,6 +6,7 @@ using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using BTCPayServer.Abstractions.Constants;
using BTCPayServer.Abstractions.Extensions;
using BTCPayServer.Abstractions.Models;
@ -19,6 +20,7 @@ using BTCPayServer.Plugins.Monero.RPC.Models;
using BTCPayServer.Plugins.Monero.Services;
using BTCPayServer.Services.Invoices;
using BTCPayServer.Services.Stores;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;

View file

@ -1,24 +1,27 @@
using BTCPayServer.Abstractions.Contracts;
using System.Net.Http;
using System.Net;
using BTCPayServer.Hosting;
using BTCPayServer.Payments;
using Microsoft.Extensions.DependencyInjection;
using NBitcoin;
using BTCPayServer.Configuration;
using System.Linq;
using System;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Net.Http;
using BTCPayServer.Abstractions.Contracts;
using BTCPayServer.Abstractions.Models;
using BTCPayServer.Plugins.Altcoins;
using BTCPayServer.Configuration;
using BTCPayServer.Hosting;
using BTCPayServer.Payments;
using BTCPayServer.Plugins.Monero.Configuration;
using BTCPayServer.Plugins.Monero.Payments;
using BTCPayServer.Plugins.Monero.Services;
using BTCPayServer.Services;
using Microsoft.Extensions.Configuration;
using NBXplorer;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using NBitcoin;
using NBXplorer;
namespace BTCPayServer.Plugins.Monero;
public class MoneroPlugin : BaseBTCPayServerPlugin
@ -99,7 +102,9 @@ public class MoneroPlugin : BaseBTCPayServerPlugin
public override string GetTransactionLink(string paymentId)
{
if (string.IsNullOrEmpty(BlockExplorerLink))
{
return null;
}
return string.Format(CultureInfo.InvariantCulture, BlockExplorerLink, paymentId);
}
}

View file

@ -1,8 +1,8 @@
using System.Collections.Generic;
using System.Linq;
using BTCPayServer.Payments;
using BTCPayServer.Payments.Bitcoin;
using BTCPayServer.Payments.Lightning;
using BTCPayServer.Plugins.Monero.Services;
using BTCPayServer.Services.Invoices;
@ -33,7 +33,9 @@ namespace BTCPayServer.Plugins.Monero.Payments
public void ModifyCheckoutModel(CheckoutModelContext context)
{
if (context is not { Handler: MoneroLikePaymentMethodHandler handler })
{
return;
}
context.Model.CheckoutBodyComponentName = BitcoinCheckoutModelExtension.CheckoutBodyComponentName;
var details = context.InvoiceEntity.GetPayments(true)
.Select(p => p.GetDetails<MoneroLikePaymentData>(handler))

View file

@ -1,24 +1,12 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading.Tasks;
using AngleSharp.Dom;
using BTCPayServer.Abstractions.Extensions;
using BTCPayServer.BIP78.Sender;
using BTCPayServer.Data;
using BTCPayServer.Logging;
using BTCPayServer.Models;
using BTCPayServer.Models.InvoicingModels;
using BTCPayServer.Payments;
using BTCPayServer.Plugins.Altcoins;
using BTCPayServer.Rating;
using BTCPayServer.Plugins.Monero.RPC.Models;
using BTCPayServer.Plugins.Monero.Services;
using BTCPayServer.Plugins.Monero.Utils;
using BTCPayServer.Plugins.Monero.RPC.Models;
using BTCPayServer.Services.Invoices;
using BTCPayServer.Services.Rates;
using NBitcoin;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
@ -71,7 +59,9 @@ namespace BTCPayServer.Plugins.Monero.Payments
public async Task ConfigurePrompt(PaymentMethodContext context)
{
if (!_moneroRpcProvider.IsConfigured(_network.CryptoCode))
{
throw new PaymentMethodUnavailableException($"BTCPAY_XMR_WALLET_DAEMON_URI or BTCPAY_XMR_DAEMON_URI isn't configured");
}
if (!_moneroRpcProvider.IsAvailable(_network.CryptoCode) || context.State is not Prepare moneroPrepare)
throw new PaymentMethodUnavailableException($"Node or wallet not available");
var invoice = context.InvoiceEntity;
@ -107,11 +97,11 @@ namespace BTCPayServer.Plugins.Monero.Payments
public long AccountIndex { get; internal set; }
}
public MoneroLikeOnChainPaymentMethodDetails ParsePaymentPromptDetails(Newtonsoft.Json.Linq.JToken details)
public MoneroLikeOnChainPaymentMethodDetails ParsePaymentPromptDetails(JToken details)
{
return details.ToObject<MoneroLikeOnChainPaymentMethodDetails>(Serializer);
}
object IPaymentMethodHandler.ParsePaymentPromptDetails(Newtonsoft.Json.Linq.JToken details)
object IPaymentMethodHandler.ParsePaymentPromptDetails(JToken details)
{
return ParsePaymentPromptDetails(details);
}

View file

@ -1,8 +1,10 @@
#nullable enable
using System.Globalization;
using BTCPayServer.Payments;
using BTCPayServer.Plugins.Altcoins;
using BTCPayServer.Services.Invoices;
using Microsoft.AspNetCore.Mvc;
namespace BTCPayServer.Plugins.Monero.Payments

View file

@ -1,4 +1,5 @@
using BTCPayServer.Payments;
using Newtonsoft.Json;
namespace BTCPayServer.Plugins.Monero.Payments

View file

@ -4,6 +4,7 @@ using System.Net.Http.Headers;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
@ -74,10 +75,10 @@ namespace BTCPayServer.Plugins.Monero.RPC
public class NoRequestModel
{
public static NoRequestModel Instance = new NoRequestModel();
public static readonly NoRequestModel Instance = new NoRequestModel();
}
internal class JsonRpcApiException : Exception
public class JsonRpcApiException : Exception
{
public JsonRpcResultError Error { get; set; }

View file

@ -1,4 +1,5 @@
using System.Collections.Generic;
using Newtonsoft.Json;
namespace BTCPayServer.Plugins.Monero.RPC.Models

View file

@ -1,4 +1,5 @@
using System.Collections.Generic;
using Newtonsoft.Json;
namespace BTCPayServer.Plugins.Monero.RPC.Models

View file

@ -1,4 +1,5 @@
using System.Collections.Generic;
using Newtonsoft.Json;
namespace BTCPayServer.Plugins.Monero.RPC.Models

View file

@ -1,4 +1,5 @@
using System.Collections.Generic;
using Newtonsoft.Json;
namespace BTCPayServer.Plugins.Monero.RPC.Models

View file

@ -1,4 +1,5 @@
using System.Collections.Generic;
using Newtonsoft.Json;
namespace BTCPayServer.Plugins.Monero.RPC.Models

View file

@ -1,5 +1,6 @@
using System;
using System.Globalization;
using Newtonsoft.Json;
namespace BTCPayServer.Plugins.Monero.RPC.Models
@ -11,7 +12,9 @@ namespace BTCPayServer.Plugins.Monero.RPC.Models
public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
{
return null;
}
var value = serializer.Deserialize<string>(reader);
long l;
if (Int64.TryParse(value, out l))

View file

@ -1,9 +1,11 @@
using System;
using System.Threading.Tasks;
using BTCPayServer.Payments;
using BTCPayServer.Plugins.Altcoins;
using BTCPayServer.Plugins.Monero.RPC;
using BTCPayServer.Plugins.Monero.RPC.Models;
using NBitcoin;
namespace BTCPayServer.Plugins.Monero.Services;
@ -43,7 +45,8 @@ public class MoneroCheckoutCheatModeExtension : ICheckoutCheatModeExtension
Amount = (long)amount,
Address = payInvoiceContext.PaymentPrompt.Destination
}
}});
}
});
return new ICheckoutCheatModeExtension.PayInvoiceResult(result.TransactionHash);
}

View file

@ -1,8 +1,10 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using BTCPayServer.Logging;
using BTCPayServer.Plugins.Monero.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
@ -64,6 +66,7 @@ namespace BTCPayServer.Plugins.Monero.Services
public Task StopAsync(CancellationToken cancellationToken)
{
_Cts?.Cancel();
_Cts?.Dispose();
return Task.CompletedTask;
}
}

View file

@ -1,27 +1,26 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Channels;
using System.Threading.Tasks;
using BTCPayServer.Client.Models;
using BTCPayServer.Data;
using BTCPayServer.Events;
using BTCPayServer.HostedServices;
using BTCPayServer.Payments;
using BTCPayServer.Plugins.Altcoins;
using BTCPayServer.Plugins.Monero.Configuration;
using BTCPayServer.Plugins.Monero.Payments;
using BTCPayServer.Plugins.Monero.RPC;
using BTCPayServer.Plugins.Monero.RPC.Models;
using BTCPayServer.Plugins.Monero.Utils;
using BTCPayServer.Services;
using BTCPayServer.Plugins.Monero.RPC;
using BTCPayServer.Services.Invoices;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using NBitcoin;
using NBXplorer;
using Newtonsoft.Json.Linq;
namespace BTCPayServer.Plugins.Monero.Services
@ -83,7 +82,9 @@ namespace BTCPayServer.Plugins.Monero.Services
else if (evt is MoneroEvent moneroEvent)
{
if (!_moneroRpcProvider.IsAvailable(moneroEvent.CryptoCode))
{
return;
}
if (!string.IsNullOrEmpty(moneroEvent.BlockHash))
{
@ -223,7 +224,7 @@ namespace BTCPayServer.Plugins.Monero.Services
{
if (valueTuples.Any())
{
_eventAggregator.Publish(new Events.InvoiceNeedUpdateEvent(valueTuples.Key.Id));
_eventAggregator.Publish(new InvoiceNeedUpdateEvent(valueTuples.Key.Id));
}
}
}
@ -240,7 +241,9 @@ namespace BTCPayServer.Plugins.Monero.Services
var paymentMethodId = PaymentTypes.CHAIN.GetPaymentMethodId(cryptoCode);
var transfer = await GetTransferByTxId(cryptoCode, transactionHash, this.CancellationToken);
if (transfer is null)
{
return;
}
var paymentsToUpdate = new List<(PaymentEntity Payment, InvoiceEntity invoice)>();
//group all destinations of the tx together and loop through the sets
@ -249,7 +252,9 @@ namespace BTCPayServer.Plugins.Monero.Services
//find the invoice corresponding to this address, else skip
var invoice = await _invoiceRepository.GetInvoiceFromAddress(paymentMethodId, destination.Key);
if (invoice == null)
{
continue;
}
var index = destination.First().SubaddrIndex;
@ -271,7 +276,7 @@ namespace BTCPayServer.Plugins.Monero.Services
{
if (valueTuples.Any())
{
_eventAggregator.Publish(new Events.InvoiceNeedUpdateEvent(valueTuples.Key.Id));
_eventAggregator.Publish(new InvoiceNeedUpdateEvent(valueTuples.Key.Id));
}
}
}
@ -286,7 +291,9 @@ namespace BTCPayServer.Plugins.Monero.Services
.Select(a => new long?(a.AccountIndex))
.ToList();
if (accountIndexes.Count is 0)
{
accountIndexes.Add(null);
}
var req = accountIndexes
.Select(i => GetTransferByTxId(cryptoCode, transactionHash, i))
.ToArray();
@ -294,8 +301,10 @@ namespace BTCPayServer.Plugins.Monero.Services
{
var result = await task;
if (result != null)
{
return result;
}
}
return null;
}
@ -340,7 +349,7 @@ namespace BTCPayServer.Plugins.Monero.Services
InvoiceSettledConfirmationThreshold = promptDetails.InvoiceSettledConfirmationThreshold
};
var status = GetStatus(details, invoice.SpeedPolicy) ? PaymentStatus.Settled : PaymentStatus.Processing;
var paymentData = new Data.PaymentData()
var paymentData = new PaymentData()
{
Status = status,
Amount = MoneroMoney.Convert(totalAmount),
@ -360,8 +369,10 @@ namespace BTCPayServer.Plugins.Monero.Services
{
var payment = await _paymentService.AddPayment(paymentData, [txId]);
if (payment != null)
{
await ReceivedPayment(invoice, payment);
}
}
else
{
//else update it with the new data
@ -393,7 +404,9 @@ namespace BTCPayServer.Plugins.Monero.Services
var paymentMethodId = PaymentTypes.CHAIN.GetPaymentMethodId(cryptoCode);
var invoices = await _invoiceRepository.GetMonitoredInvoices(paymentMethodId);
if (!invoices.Any())
{
return;
}
invoices = invoices.Where(entity => entity.GetPaymentPrompt(paymentMethodId)?.Activated is true).ToArray();
await UpdatePaymentStates(cryptoCode, invoices);
}

View file

@ -4,12 +4,14 @@ using System.Collections.Immutable;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Amazon.Runtime;
using BTCPayServer.Plugins.Monero.Configuration;
using BTCPayServer.Plugins.Monero.RPC;
using BTCPayServer.Plugins.Monero.RPC.Models;
using BTCPayServer.Services;
using Microsoft.Extensions.Logging;
using NBitcoin;
namespace BTCPayServer.Plugins.Monero.Services
@ -152,7 +154,9 @@ namespace BTCPayServer.Plugins.Monero.Services
(await cashcow.SendCommandAsync<JsonRpcClient.NoRequestModel, GetBalanceResponse>("get_balance",
JsonRpcClient.NoRequestModel.Instance));
if (balance.UnlockedBalance != 0)
{
return;
}
_logger.LogInformation("Mining blocks for the cashcow...");
var address = (await cashcow.SendCommandAsync<GetAddressRequest, GetAddressResponse>("get_address", new()
{

View file

@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Linq;
using BTCPayServer.Abstractions.Contracts;
using BTCPayServer.Client.Models;
using BTCPayServer.Payments;
@ -25,7 +26,8 @@ namespace BTCPayServer.Plugins.Monero.Services
{
return _moneroRpcProvider.Summaries.Select(pair => new MoneroSyncStatus()
{
Summary = pair.Value, PaymentMethodId = PaymentMethodId.Parse(pair.Key).ToString()
Summary = pair.Value,
PaymentMethodId = PaymentMethodId.Parse(pair.Key).ToString()
});
}
}

View file

@ -1,4 +1,5 @@
using System;
using BTCPayServer.Payments;
namespace BTCPayServer.Plugins.Monero.ViewModels

View file

@ -35,16 +35,16 @@ BTCPay Server's Docker deployment simplifies the setup by automatically configur
## Building and testing
## 🧑‍💻 Local Development Setup
## Local Development Setup
If you're contributing to this plugin or running a local development instance of BTCPay Server with the Monero plugin, follow these steps.
## 1) Requirements
## 1. Requirements
- .NET 8.0 SDK or later
- JetBrains Rider (recommended) or Visual Studio Code with C# support
- Git
- Docker and Docker Compose
## 2) Clone the Repositories
## 2. Clone the Repositories
Create a working directory and clone both the BTCPay Server and Monero plugin repositories side by side:
If you are a developer maintaining this plugin, in order to maintain this plugin, you need to clone this repository with `--recurse-submodules`:
@ -63,7 +63,7 @@ To build and run unit tests, run the following commands:
```bash
dotnet build btcpay-monero-plugin.sln
dotnet test BTCPayServer.Plugins.Monero.UnitTests --verbosity normal
dotnet test BTCPayServer.Plugins.UnitTests --verbosity normal
```
To run unit tests with coverage, install JetBrains dotCover CLI:
@ -83,7 +83,13 @@ dotnet build btcpay-monero-plugin.sln
docker compose -f BTCPayServer.Plugins.IntegrationTests/docker-compose.yml run tests
```
**BTCPAY_XMR_CASHCOW_WALLET_DAEMON_URI** | **Optional**. The URI of the [monero-wallet-rpc](https://getmonero.dev/interacting/monero-wallet-rpc.html) interface for the cashcow wallet. This is used to create a second wallet for testing purposes in regtest mode. | http://
**BTCPAY_XMR_CASHCOW_WALLET_DAEMON_URI** | **Optional**. The URI of the [monero-wallet-rpc](https://getmonero.dev/interacting/monero-wallet-rpc.html) interface for the cashcow wallet. This is used to create a second wallet for testing purposes in regtest mode.
## Code formatting
We use the **unmodified** standardized `.editorconfig` from .NET SDK. Run `dotnet new editorconfig --force` to apply the latest version.
To enforce formatting for the whole project, run `dotnet format btcpay-monero-plugin.sln --exclude submodules/* --verbosity diagnostic`
## 4. Configure BTCPay Server to Load the Plugin

@ -1 +1 @@
Subproject commit 276bb1b165279f39d2f2f7efb614949ae4c3b5b3
Subproject commit 46aafe7b8d31afb37350e10589af71a706097c90