rebrand(lethean): update branding, ports, and config for Lethean blockchain

- Coin: Zano → Lethean, ticker: ZAN/ZANO → LTHN
- Ports: 11211 → 36941 (mainnet RPC), 46941 (testnet RPC)
- Wallet: 11212 → 36944/46944
- Address prefix: iTHN
- URLs: zano.org → lethean.io
- Explorer links: explorer.lthn.io

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Claude 2026-04-01 22:24:13 +01:00
parent d51d7dc0c0
commit a3869db496
No known key found for this signature in database
GPG key ID: AF404715446AEB41
75 changed files with 699 additions and 699 deletions

View file

@ -13,7 +13,7 @@ permissions:
jobs:
report-coverage:
if: github.repository == 'btcpay-zano/btcpayserver-zano-plugin'
if: github.repository == 'btcpay-lethean/btcpayserver-lethean-plugin'
runs-on: ubuntu-latest
steps:
- name: Download coverage report

View file

@ -29,4 +29,4 @@ jobs:
- name: Code format check
run: |
dotnet tool install -g dotnet-format
dotnet format btcpay-zano-plugin.sln --no-restore --verify-no-changes --exclude submodules/* --verbosity diagnostic
dotnet format btcpay-lethean-plugin.sln --no-restore --verify-no-changes --exclude submodules/* --verbosity diagnostic

View file

@ -36,8 +36,8 @@ jobs:
- name: Deterministic build check
run: |
dotnet tool install --global dotnet-validate --version 0.0.1-preview.537
dotnet pack Plugins/Zano/BTCPayServer.Plugins.Zano.csproj -c Release /p:PackageVersion=1 --no-build -o nuget-packages
dotnet validate package local nuget-packages/BTCPayServer.Plugins.Zano.1.0.0.nupkg
dotnet pack Plugins/Lethean/BTCPayServer.Plugins.Lethean.csproj -c Release /p:PackageVersion=1 --no-build -o nuget-packages
dotnet validate package local nuget-packages/BTCPayServer.Plugins.Lethean.1.0.0.nupkg
- name: Run unit tests
run: |
@ -61,4 +61,4 @@ jobs:
uses: actions/upload-artifact@v4
with:
name: nuget-package-${{ github.sha }}
path: nuget-packages/BTCPayServer.Plugins.Zano.1.0.0.nupkg
path: nuget-packages/BTCPayServer.Plugins.Lethean.1.0.0.nupkg

View file

@ -25,4 +25,4 @@ jobs:
"GitRef": "${{ env.GIT_REF }}"
}'
env:
pluginSlug: "zano-plugin"
pluginSlug: "lethean-plugin"

View file

@ -30,7 +30,7 @@
<ItemGroup>
<ProjectReference Include="..\submodules\btcpayserver\BTCPayServer.Abstractions\BTCPayServer.Abstractions.csproj" />
<ProjectReference Include="..\submodules\btcpayserver\BTCPayServer.Tests\BTCPayServer.Tests.csproj" />
<ProjectReference Include="..\Plugins\Zano\BTCPayServer.Plugins.Zano.csproj" />
<ProjectReference Include="..\Plugins\Lethean\BTCPayServer.Plugins.Lethean.csproj" />
</ItemGroup>
<ItemGroup>

View file

@ -27,9 +27,9 @@ ENV SCREEN_HEIGHT 600 \
COPY . .
ARG CONFIGURATION_NAME=Release
ARG MONERO_PLUGIN_FOLDER=/root/.btcpayserver/Plugins/BTCPayServer.Plugins.Zano/
ARG MONERO_PLUGIN_FOLDER=/root/.btcpayserver/Plugins/BTCPayServer.Plugins.Lethean/
RUN mkdir -p ${MONERO_PLUGIN_FOLDER}
RUN cd Plugins/Zano && dotnet build BTCPayServer.Plugins.Zano.sln --configuration ${CONFIGURATION_NAME} /p:RazorCompileOnBuild=true --output ${MONERO_PLUGIN_FOLDER}
RUN cd Plugins/Lethean && dotnet build BTCPayServer.Plugins.Lethean.sln --configuration ${CONFIGURATION_NAME} /p:RazorCompileOnBuild=true --output ${MONERO_PLUGIN_FOLDER}
RUN cd BTCPayServer.Plugins.IntegrationTests && dotnet build --configuration ${CONFIGURATION_NAME} /p:CI_TESTS=true /p:RazorCompileOnBuild=true
RUN dotnet tool install --global JetBrains.DotCover.CommandLineTools --version 2025.1.6
ENV PATH="$PATH:/root/.dotnet/tools"

View file

@ -10,7 +10,7 @@ using Npgsql;
using static Mono.Unix.Native.Syscall;
namespace BTCPayServer.Plugins.IntegrationTests.Zano;
namespace BTCPayServer.Plugins.IntegrationTests.Lethean;
public static class IntegrationTestUtils
{
@ -19,7 +19,7 @@ public static class IntegrationTestUtils
.CreateLogger("IntegrationTestUtils");
private static readonly string ContainerWalletDir =
Environment.GetEnvironmentVariable("BTCPAY_ZANO_WALLET_DAEMON_WALLETDIR") ?? "/wallet";
Environment.GetEnvironmentVariable("BTCPAY_LTHN_WALLET_DAEMON_WALLETDIR") ?? "/wallet";
public static async Task CleanUpAsync(PlaywrightTester playwrightTester)
{
@ -61,7 +61,7 @@ public static class IntegrationTestUtils
}
}
public static async Task CopyWalletFilesToZanoRpcDirAsync(PlaywrightTester playwrightTester, String walletDir)
public static async Task CopyWalletFilesToLetheanRpcDirAsync(PlaywrightTester playwrightTester, String walletDir)
{
Logger.LogInformation("Starting to copy wallet files");
if (playwrightTester.Server.PayTester.InContainer)
@ -84,7 +84,7 @@ public static class IntegrationTestUtils
}
catch (Exception ex)
{
Logger.LogError(ex, "Failed to copy wallet files to the Zano directory.");
Logger.LogError(ex, "Failed to copy wallet files to the Lethean directory.");
}
}
@ -102,7 +102,7 @@ public static class IntegrationTestUtils
File.Copy(src, dst, overwrite: true);
// zano ownership
// lethean ownership
if (chown(dst, 980, 980) == 0)
{
return;
@ -119,20 +119,20 @@ public static class IntegrationTestUtils
var fullWalletDir = Path.Combine(AppContext.BaseDirectory, "Resources", walletDir);
await RunProcessAsync("docker",
$"cp \"{Path.Combine(fullWalletDir, "wallet")}\" zano_wallet:/wallet/wallet");
$"cp \"{Path.Combine(fullWalletDir, "wallet")}\" lethean_wallet:/wallet/wallet");
await RunProcessAsync("docker",
$"cp \"{Path.Combine(fullWalletDir, "wallet.keys")}\" zano_wallet:/wallet/wallet.keys");
$"cp \"{Path.Combine(fullWalletDir, "wallet.keys")}\" lethean_wallet:/wallet/wallet.keys");
await RunProcessAsync("docker",
$"cp \"{Path.Combine(fullWalletDir, "password")}\" zano_wallet:/wallet/password");
$"cp \"{Path.Combine(fullWalletDir, "password")}\" lethean_wallet:/wallet/password");
await RunProcessAsync("docker",
"exec zano_wallet chown zano:zano /wallet/wallet /wallet/wallet.keys /wallet/password");
"exec lethean_wallet chown lethean:lethean /wallet/wallet /wallet/wallet.keys /wallet/password");
}
catch (Exception ex)
{
Logger.LogError(ex, "Failed to copy wallet files to the Zano directory.");
Logger.LogError(ex, "Failed to copy wallet files to the Lethean directory.");
}
}
@ -163,7 +163,7 @@ public static class IntegrationTestUtils
var removeWalletFromDocker = new ProcessStartInfo
{
FileName = "docker",
Arguments = "exec zano_wallet sh -c \"rm -rf /wallet/*\"",
Arguments = "exec lethean_wallet sh -c \"rm -rf /wallet/*\"",
RedirectStandardOutput = true,
RedirectStandardError = true
};

View file

@ -0,0 +1,25 @@
using BTCPayServer.Tests;
using Xunit.Abstractions;
namespace BTCPayServer.Plugins.IntegrationTests.Lethean
{
public class LetheanAndBitcoinIntegrationTestBase : UnitTestBase
{
public LetheanAndBitcoinIntegrationTestBase(ITestOutputHelper helper) : base(helper)
{
SetDefaultEnv("BTCPAY_LTHN_DAEMON_URI", "http://127.0.0.1:46941");
SetDefaultEnv("BTCPAY_LTHN_WALLET_DAEMON_URI", "http://127.0.0.1:46944");
SetDefaultEnv("BTCPAY_LTHN_WALLET_DAEMON_WALLETDIR", "/wallet");
}
private static void SetDefaultEnv(string key, string defaultValue)
{
if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable(key)))
{
Environment.SetEnvironmentVariable(key, defaultValue);
}
}
}
}

View file

@ -1,4 +1,4 @@
using BTCPayServer.Plugins.Zano.Services;
using BTCPayServer.Plugins.Lethean.Services;
using BTCPayServer.Rating;
using BTCPayServer.Services.Rates;
using BTCPayServer.Tests.Mocks;
@ -6,12 +6,12 @@ using BTCPayServer.Tests.Mocks;
using Xunit;
using Xunit.Abstractions;
namespace BTCPayServer.Plugins.IntegrationTests.Zano;
namespace BTCPayServer.Plugins.IntegrationTests.Lethean;
public class ZanoPluginIntegrationTest(ITestOutputHelper helper) : ZanoAndBitcoinIntegrationTestBase(helper)
public class LetheanPluginIntegrationTest(ITestOutputHelper helper) : LetheanAndBitcoinIntegrationTestBase(helper)
{
[Fact]
public async Task ShouldEnableZanoPluginSuccessfully()
public async Task ShouldEnableLetheanPluginSuccessfully()
{
await using var s = CreatePlaywrightTester();
await s.StartAsync();
@ -24,21 +24,21 @@ public class ZanoPluginIntegrationTest(ITestOutputHelper helper) : ZanoAndBitcoi
var coinAverageMock = new MockRateProvider();
coinAverageMock.ExchangeRates.Add(new PairRate(CurrencyPair.Parse("BTC_USD"), new BidAsk(5000m)));
coinAverageMock.ExchangeRates.Add(new PairRate(CurrencyPair.Parse("BTC_EUR"), new BidAsk(4000m)));
coinAverageMock.ExchangeRates.Add(new PairRate(CurrencyPair.Parse("ZANO_BTC"), new BidAsk(4500m)));
coinAverageMock.ExchangeRates.Add(new PairRate(CurrencyPair.Parse("LTHN_BTC"), new BidAsk(4500m)));
rateProviderFactory.Providers.Add("coingecko", coinAverageMock);
var kraken = new MockRateProvider();
kraken.ExchangeRates.Add(new PairRate(CurrencyPair.Parse("BTC_USD"), new BidAsk(0.1m)));
kraken.ExchangeRates.Add(new PairRate(CurrencyPair.Parse("ZANO_USD"), new BidAsk(0.1m)));
kraken.ExchangeRates.Add(new PairRate(CurrencyPair.Parse("ZANO_BTC"), new BidAsk(0.1m)));
kraken.ExchangeRates.Add(new PairRate(CurrencyPair.Parse("LTHN_USD"), new BidAsk(0.1m)));
kraken.ExchangeRates.Add(new PairRate(CurrencyPair.Parse("LTHN_BTC"), new BidAsk(0.1m)));
rateProviderFactory.Providers.Add("kraken", kraken);
}
await s.RegisterNewUser(true);
await s.CreateNewStore(preferredExchange: "Kraken");
await s.Page.Locator("a.nav-link[href*='zano/ZANO']").ClickAsync();
await s.Page.Locator("a.nav-link[href*='lethean/LTHN']").ClickAsync();
// Enable Zano and configure settlement threshold
// Enable Lethean and configure settlement threshold
await s.Page.CheckAsync("#Enabled");
await s.Page.SelectOptionAsync("#SettlementConfirmationThresholdChoice", "2");
await s.Page.ClickAsync("#SaveButton");
@ -46,7 +46,7 @@ public class ZanoPluginIntegrationTest(ITestOutputHelper helper) : ZanoAndBitcoi
// Set rate provider
await s.Page.Locator("#menu-item-General").ClickAsync();
await s.Page.Locator("#menu-item-Rates").ClickAsync();
await s.Page.FillAsync("#DefaultCurrencyPairs", "BTC_USD,ZANO_USD,ZANO_BTC");
await s.Page.FillAsync("#DefaultCurrencyPairs", "BTC_USD,LTHN_USD,LTHN_BTC");
await s.Page.SelectOptionAsync("#PrimarySource_PreferredExchange", "kraken");
await s.Page.Locator("#page-primary").ClickAsync();
@ -54,7 +54,7 @@ public class ZanoPluginIntegrationTest(ITestOutputHelper helper) : ZanoAndBitcoi
await s.Page.Locator("a.nav-link[href*='invoices']").ClickAsync();
await s.Page.Locator("#page-primary").ClickAsync();
await s.Page.FillAsync("#Amount", "4.20");
await s.Page.FillAsync("#BuyerEmail", "zano@zano.org");
await s.Page.FillAsync("#BuyerEmail", "lethean@lethean.org");
await Task.Delay(TimeSpan.FromSeconds(25)); // wallet-rpc needs some time to sync
await s.Page.Locator("#page-primary").ClickAsync();
@ -72,7 +72,7 @@ public class ZanoPluginIntegrationTest(ITestOutputHelper helper) : ZanoAndBitcoi
// Select confirmation time to 0
await s.Page.GoBackAsync();
await s.Page.Locator("a.nav-link[href*='zano/ZANO']").ClickAsync();
await s.Page.Locator("a.nav-link[href*='lethean/LTHN']").ClickAsync();
await s.Page.SelectOptionAsync("#SettlementConfirmationThresholdChoice", "3");
await s.Page.ClickAsync("#SaveButton");
@ -83,11 +83,11 @@ public class ZanoPluginIntegrationTest(ITestOutputHelper helper) : ZanoAndBitcoi
public async Task ShouldLoadWalletOnStartUpIfExists()
{
await using var s = CreatePlaywrightTester();
await IntegrationTestUtils.CopyWalletFilesToZanoRpcDirAsync(s, "wallet");
await IntegrationTestUtils.CopyWalletFilesToLetheanRpcDirAsync(s, "wallet");
await s.StartAsync();
await s.RegisterNewUser(true);
await s.CreateNewStore();
await s.Page.Locator("a.nav-link[href*='zano/ZANO']").ClickAsync();
await s.Page.Locator("a.nav-link[href*='lethean/LTHN']").ClickAsync();
var walletRpcIsAvailable = await s.Page
.Locator("li.list-group-item:text('Wallet RPC available: True')")
@ -102,11 +102,11 @@ public class ZanoPluginIntegrationTest(ITestOutputHelper helper) : ZanoAndBitcoi
public async Task ShouldLoadWalletWithPasswordOnStartUpIfExists()
{
await using var s = CreatePlaywrightTester();
await IntegrationTestUtils.CopyWalletFilesToZanoRpcDirAsync(s, "wallet_password");
await IntegrationTestUtils.CopyWalletFilesToLetheanRpcDirAsync(s, "wallet_password");
await s.StartAsync();
await s.RegisterNewUser(true);
await s.CreateNewStore();
await s.Page.Locator("a.nav-link[href*='zano/ZANO']").ClickAsync();
await s.Page.Locator("a.nav-link[href*='lethean/LTHN']").ClickAsync();
var walletRpcIsAvailable = await s.Page
.Locator("li.list-group-item:text('Wallet RPC available: True')")

View file

@ -1,25 +0,0 @@
using BTCPayServer.Tests;
using Xunit.Abstractions;
namespace BTCPayServer.Plugins.IntegrationTests.Zano
{
public class ZanoAndBitcoinIntegrationTestBase : UnitTestBase
{
public ZanoAndBitcoinIntegrationTestBase(ITestOutputHelper helper) : base(helper)
{
SetDefaultEnv("BTCPAY_ZANO_DAEMON_URI", "http://127.0.0.1:11211");
SetDefaultEnv("BTCPAY_ZANO_WALLET_DAEMON_URI", "http://127.0.0.1:11212");
SetDefaultEnv("BTCPAY_ZANO_WALLET_DAEMON_WALLETDIR", "/wallet");
}
private static void SetDefaultEnv(string key, string defaultValue)
{
if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable(key)))
{
Environment.SetEnvironmentVariable(key, defaultValue);
}
}
}
}

View file

@ -11,18 +11,18 @@ services:
TESTS_EXPLORER_POSTGRES: User ID=postgres;Include Error Detail=true;Host=postgres;Port=5432;Database=nbxplorer
TESTS_HOSTNAME: tests
TESTS_INCONTAINER: "true"
BTCPAY_ZANO_DAEMON_URI: http://37.27.100.59:10505
BTCPAY_ZANO_WALLET_DAEMON_URI: http://zano_wallet:11212
BTCPAY_ZANO_WALLET_DAEMON_WALLETDIR: /wallet
BTCPAY_LTHN_DAEMON_URI: http://127.0.0.1:46941
BTCPAY_LTHN_WALLET_DAEMON_URI: http://lethean_wallet:46944
BTCPAY_LTHN_WALLET_DAEMON_WALLETDIR: /wallet
depends_on:
- nbxplorer
- postgres
- zano_wallet
- lethean_wallet
extra_hosts:
- "tests:127.0.0.1"
volumes:
- ../coverage:/coverage
- zano_wallet:/wallet
- lethean_wallet:/wallet
# The dev container is not used, it is just handy to run `docker-compose up dev` to start all services
dev:
@ -32,7 +32,7 @@ services:
depends_on:
- nbxplorer
- postgres
- zano_wallet
- lethean_wallet
nbxplorer:
image: nicolasdorier/nbxplorer:2.5.25
@ -89,40 +89,40 @@ services:
volumes:
- "bitcoin_datadir:/data"
zanod:
image: pavelravaga/zano:2.2.0.455
letheand:
image: letheanio/letheand:latest
restart: unless-stopped
container_name: zanod
entrypoint: zanod
container_name: letheand
entrypoint: letheand
command: >
--rpc-bind-ip=0.0.0.0
--rpc-bind-port=11211
--rpc-bind-port=46941
--log-level=2
--data-dir=/data
volumes:
- zano_data:/data
- lethean_data:/data
ports:
- "11211:11211"
- "46941:46941"
zano_wallet:
image: pavelravaga/zano:2.2.0.455
lethean_wallet:
image: letheanio/letheand:latest
restart: unless-stopped
container_name: zano_wallet
container_name: lethean_wallet
entrypoint: /bin/bash
command:
- -c
- |
if [ ! -f /wallet/wallet.keys ]; then
echo "Generating new wallet..."
echo "exit" | simplewallet --generate-new-wallet=/wallet/wallet --password pass --daemon-address=37.27.100.59:10505
echo "exit" | simplewallet --generate-new-wallet=/wallet/wallet --password pass --daemon-address=127.0.0.1:46941
fi
simplewallet --wallet-file=/wallet/wallet --password pass --daemon-address=37.27.100.59:10505 --rpc-bind-ip=0.0.0.0 --rpc-bind-port=11212
simplewallet --wallet-file=/wallet/wallet --password pass --daemon-address=127.0.0.1:46941 --rpc-bind-ip=0.0.0.0 --rpc-bind-port=46944
ports:
- "11212:11212"
- "46944:46944"
volumes:
- zano_wallet:/wallet
- lethean_wallet:/wallet
depends_on:
- zanod
- letheand
postgres:
image: postgres:17.4
@ -136,8 +136,8 @@ services:
volumes:
bitcoin_datadir:
zano_data:
zano_wallet:
lethean_data:
lethean_wallet:
networks:
default:

View file

@ -29,7 +29,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Plugins\Zano\BTCPayServer.Plugins.Zano.csproj" />
<ProjectReference Include="..\Plugins\Lethean\BTCPayServer.Plugins.Lethean.csproj" />
<ProjectReference Include="..\submodules\btcpayserver\BTCPayServer.Abstractions\BTCPayServer.Abstractions.csproj" />
</ItemGroup>

View file

@ -0,0 +1,76 @@
using System;
using BTCPayServer.Plugins.Lethean.Configuration;
using Xunit;
namespace BTCPayServer.Plugins.UnitTests.Lethean.Configuration
{
public class LetheanConfigurationTests
{
[Trait("Category", "Unit")]
[Fact]
public void LetheanConfiguration_ShouldInitializeWithEmptyDictionary()
{
var config = new LetheanConfiguration();
Assert.NotNull(config.LetheanConfigurationItems);
Assert.Empty(config.LetheanConfigurationItems);
}
[Trait("Category", "Unit")]
[Fact]
public void LetheanConfigurationItem_ShouldSetAndGetProperties()
{
var configItem = new LetheanConfigurationItem
{
DaemonRpcUri = new Uri("http://localhost:36941"),
InternalWalletRpcUri = new Uri("http://localhost:36944"),
WalletDirectory = "/wallets"
};
Assert.Equal("http://localhost:36941/", configItem.DaemonRpcUri.ToString());
Assert.Equal("http://localhost:36944/", configItem.InternalWalletRpcUri.ToString());
Assert.Equal("/wallets", configItem.WalletDirectory);
}
[Trait("Category", "Unit")]
[Fact]
public void LetheanConfiguration_ShouldAddAndRetrieveItems()
{
var config = new LetheanConfiguration();
var configItem = new LetheanConfigurationItem
{
DaemonRpcUri = new Uri("http://localhost:36941"),
InternalWalletRpcUri = new Uri("http://localhost:36944"),
WalletDirectory = "/wallets"
};
config.LetheanConfigurationItems.Add("LTHN", configItem);
Assert.Single(config.LetheanConfigurationItems);
Assert.True(config.LetheanConfigurationItems.ContainsKey("LTHN"));
Assert.Equal(configItem, config.LetheanConfigurationItems["LTHN"]);
}
[Trait("Category", "Unit")]
[Fact]
public void LetheanConfiguration_ShouldHandleDuplicateKeys()
{
var config = new LetheanConfiguration();
var configItem1 = new LetheanConfigurationItem
{
DaemonRpcUri = new Uri("http://localhost:36941")
};
var configItem2 = new LetheanConfigurationItem
{
DaemonRpcUri = new Uri("http://localhost:36944")
};
config.LetheanConfigurationItems.Add("LTHN", configItem1);
Assert.Throws<ArgumentException>(() =>
config.LetheanConfigurationItems.Add("LTHN", configItem2));
}
}
}

View file

@ -1,16 +1,16 @@
using BTCPayServer.Plugins.Zano.Payments;
using BTCPayServer.Plugins.Lethean.Payments;
using Xunit;
namespace BTCPayServer.Plugins.UnitTests.Zano.Payments
namespace BTCPayServer.Plugins.UnitTests.Lethean.Payments
{
public class ZanoPaymentDataTests
public class LetheanPaymentDataTests
{
[Trait("Category", "Unit")]
[Fact]
public void DefaultValues_ShouldBeCorrect()
{
var paymentData = new ZanoPaymentData();
var paymentData = new LetheanPaymentData();
Assert.Null(paymentData.PaymentId);
Assert.Equal(0, paymentData.BlockHeight);
@ -24,7 +24,7 @@ namespace BTCPayServer.Plugins.UnitTests.Zano.Payments
[Fact]
public void Properties_ShouldBeSettable()
{
var paymentData = new ZanoPaymentData();
var paymentData = new LetheanPaymentData();
paymentData.PaymentId = "abcdef0123456789";
paymentData.BlockHeight = 100;

View file

@ -0,0 +1,28 @@
using BTCPayServer.Plugins.Lethean.RPC;
using Xunit;
namespace BTCPayServer.Plugins.UnitTests.Lethean.RPC
{
public class LetheanPollEventTest
{
[Fact]
public void DefaultInitialization_ShouldHaveNullCryptoCode()
{
var pollEvent = new LetheanPollEvent();
Assert.Null(pollEvent.CryptoCode);
}
[Fact]
public void PropertyAssignment_ShouldSetAndRetrieveValues()
{
var pollEvent = new LetheanPollEvent
{
CryptoCode = "LTHN"
};
Assert.Equal("LTHN", pollEvent.CryptoCode);
}
}
}

View file

@ -1,10 +1,10 @@
using BTCPayServer.Plugins.Zano.RPC.Models;
using BTCPayServer.Plugins.Lethean.RPC.Models;
using Newtonsoft.Json;
using Xunit;
namespace BTCPayServer.Plugins.UnitTests.Zano.RPC.Models
namespace BTCPayServer.Plugins.UnitTests.Lethean.RPC.Models
{
public class ParseStringConverterTests
{

View file

@ -1,12 +1,12 @@
using System.Globalization;
using BTCPayServer.Plugins.Zano.Utils;
using BTCPayServer.Plugins.Lethean.Utils;
using Xunit;
namespace BTCPayServer.Plugins.UnitTests.Zano.Utils
namespace BTCPayServer.Plugins.UnitTests.Lethean.Utils
{
public class ZanoMoneyTests
public class LetheanMoneyTests
{
[Trait("Category", "Unit")]
[Theory]
@ -16,7 +16,7 @@ namespace BTCPayServer.Plugins.UnitTests.Zano.Utils
public void Convert_LongToDecimal_ReturnsExpectedValue(long atomicUnits, string expectedString)
{
decimal expected = decimal.Parse(expectedString, CultureInfo.InvariantCulture);
decimal result = ZanoMoney.Convert(atomicUnits);
decimal result = LetheanMoney.Convert(atomicUnits);
Assert.Equal(expected, result);
}
@ -25,10 +25,10 @@ namespace BTCPayServer.Plugins.UnitTests.Zano.Utils
[InlineData("0.000000000001", 1)]
[InlineData("0.123456789012", 123456789012)]
[InlineData("1.000000000000", 1000000000000)]
public void Convert_DecimalToLong_ReturnsExpectedValue(string zanoString, long expectedAtomicUnits)
public void Convert_DecimalToLong_ReturnsExpectedValue(string letheanString, long expectedAtomicUnits)
{
decimal zano = decimal.Parse(zanoString, CultureInfo.InvariantCulture);
long result = ZanoMoney.Convert(zano);
decimal lethean = decimal.Parse(letheanString, CultureInfo.InvariantCulture);
long result = LetheanMoney.Convert(lethean);
Assert.Equal(expectedAtomicUnits, result);
}
@ -39,8 +39,8 @@ namespace BTCPayServer.Plugins.UnitTests.Zano.Utils
[InlineData(1000000000000)]
public void RoundTripConversion_LongToDecimalToLong_ReturnsOriginalValue(long atomicUnits)
{
decimal zano = ZanoMoney.Convert(atomicUnits);
long convertedBack = ZanoMoney.Convert(zano);
decimal lethean = LetheanMoney.Convert(atomicUnits);
long convertedBack = LetheanMoney.Convert(lethean);
Assert.Equal(atomicUnits, convertedBack);
}
}

View file

@ -1,27 +1,27 @@
using BTCPayServer.Payments;
using BTCPayServer.Plugins.Zano.ViewModels;
using BTCPayServer.Plugins.Lethean.ViewModels;
using Xunit;
namespace BTCPayServer.Plugins.UnitTests.Zano.ViewModels
namespace BTCPayServer.Plugins.UnitTests.Lethean.ViewModels
{
public class ZanoPaymentViewModelTests
public class LetheanPaymentViewModelTests
{
[Trait("Category", "Unit")]
[Fact]
public void ZanoPaymentViewModel_SetGetProperties_ReturnsCorrectValues()
public void LetheanPaymentViewModel_SetGetProperties_ReturnsCorrectValues()
{
var viewModel = new ZanoPaymentViewModel();
var viewModel = new LetheanPaymentViewModel();
var paymentMethodId = new PaymentMethodId("ZANO");
var paymentMethodId = new PaymentMethodId("LTHN");
var confirmations = "3";
var depositAddress = "zanoaddress";
var depositAddress = "letheanaddress";
var amount = "100.5";
var transactionId = "tx123";
var receivedTime = DateTimeOffset.UtcNow;
var transactionLink = "https://explorer.zano.com/tx/tx123";
var currency = "ZANO";
var transactionLink = "https://explorer.lethean.com/tx/tx123";
var currency = "LTHN";
viewModel.PaymentMethodId = paymentMethodId;
viewModel.Confirmations = confirmations;

View file

@ -1,76 +0,0 @@
using System;
using BTCPayServer.Plugins.Zano.Configuration;
using Xunit;
namespace BTCPayServer.Plugins.UnitTests.Zano.Configuration
{
public class ZanoConfigurationTests
{
[Trait("Category", "Unit")]
[Fact]
public void ZanoConfiguration_ShouldInitializeWithEmptyDictionary()
{
var config = new ZanoConfiguration();
Assert.NotNull(config.ZanoConfigurationItems);
Assert.Empty(config.ZanoConfigurationItems);
}
[Trait("Category", "Unit")]
[Fact]
public void ZanoConfigurationItem_ShouldSetAndGetProperties()
{
var configItem = new ZanoConfigurationItem
{
DaemonRpcUri = new Uri("http://localhost:11211"),
InternalWalletRpcUri = new Uri("http://localhost:11212"),
WalletDirectory = "/wallets"
};
Assert.Equal("http://localhost:11211/", configItem.DaemonRpcUri.ToString());
Assert.Equal("http://localhost:11212/", configItem.InternalWalletRpcUri.ToString());
Assert.Equal("/wallets", configItem.WalletDirectory);
}
[Trait("Category", "Unit")]
[Fact]
public void ZanoConfiguration_ShouldAddAndRetrieveItems()
{
var config = new ZanoConfiguration();
var configItem = new ZanoConfigurationItem
{
DaemonRpcUri = new Uri("http://localhost:11211"),
InternalWalletRpcUri = new Uri("http://localhost:11212"),
WalletDirectory = "/wallets"
};
config.ZanoConfigurationItems.Add("ZANO", configItem);
Assert.Single(config.ZanoConfigurationItems);
Assert.True(config.ZanoConfigurationItems.ContainsKey("ZANO"));
Assert.Equal(configItem, config.ZanoConfigurationItems["ZANO"]);
}
[Trait("Category", "Unit")]
[Fact]
public void ZanoConfiguration_ShouldHandleDuplicateKeys()
{
var config = new ZanoConfiguration();
var configItem1 = new ZanoConfigurationItem
{
DaemonRpcUri = new Uri("http://localhost:11211")
};
var configItem2 = new ZanoConfigurationItem
{
DaemonRpcUri = new Uri("http://localhost:11212")
};
config.ZanoConfigurationItems.Add("ZANO", configItem1);
Assert.Throws<ArgumentException>(() =>
config.ZanoConfigurationItems.Add("ZANO", configItem2));
}
}
}

View file

@ -1,28 +0,0 @@
using BTCPayServer.Plugins.Zano.RPC;
using Xunit;
namespace BTCPayServer.Plugins.UnitTests.Zano.RPC
{
public class ZanoPollEventTest
{
[Fact]
public void DefaultInitialization_ShouldHaveNullCryptoCode()
{
var pollEvent = new ZanoPollEvent();
Assert.Null(pollEvent.CryptoCode);
}
[Fact]
public void PropertyAssignment_ShouldSetAndRetrieveValues()
{
var pollEvent = new ZanoPollEvent
{
CryptoCode = "ZANO"
};
Assert.Equal("ZANO", pollEvent.CryptoCode);
}
}
}

View file

@ -3,4 +3,4 @@
We follow the [Ruby Community's Code of Conduct](https://www.ruby-lang.org/en/conduct/) to ensure a safe, respectful, and productive environment for all participants.
If you experience or witness any violations of this Code of Conduct, please reach out to us in our [Matrix room](https://matrix.to/#/#btcpay-zano:matrix.org) so we can address the issue promptly.
If you experience or witness any violations of this Code of Conduct, please reach out to us in our [Matrix room](https://matrix.to/#/#btcpay-lethean:matrix.org) so we can address the issue promptly.

View file

@ -6,8 +6,8 @@
<!-- Plugin specific properties -->
<PropertyGroup>
<Product>Zano</Product>
<Description>This plugin extends BTCPay Server to enable users to receive payments via Zano.</Description>
<Product>Lethean</Product>
<Description>This plugin extends BTCPay Server to enable users to receive payments via Lethean.</Description>
<Version>1.0.0</Version>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
</PropertyGroup>
@ -66,7 +66,7 @@
-->
<ItemGroup>
<None Update="BTCPayServer.Plugins.Zano.json">
<None Update="BTCPayServer.Plugins.Lethean.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
@ -75,6 +75,6 @@
<ProjectReference Include="..\..\submodules\btcpayserver\BTCPayServer\BTCPayServer.csproj" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="zano.svg" />
<EmbeddedResource Include="lethean.svg" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,17 @@
{
"Identifier": "BTCPayServer.Plugins.Lethean",
"Name": "Lethean",
"Version": "1.0.0",
"Description": "Accept Lethean (LTHN) payments in BTCPay Server. Privacy-focused cryptocurrency with confidential transactions.",
"SystemPlugin": false,
"Dependencies": [
{
"Identifier": "BTCPayServer",
"Condition": ">=2.1.0"
}
],
"Documentation": "https://github.com/lethean-io/btcpayserver-lethean-plugin",
"Source": "https://github.com/lethean-io/btcpayserver-lethean-plugin",
"Author": "Lethean",
"AuthorLink": "https://lethean.io"
}

View file

@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BTCPayServer.Plugins.Zano", "BTCPayServer.Plugins.Zano.csproj", "{440E70AD-BA40-449C-8315-6A13D95B6ED7}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BTCPayServer.Plugins.Lethean", "BTCPayServer.Plugins.Lethean.csproj", "{440E70AD-BA40-449C-8315-6A13D95B6ED7}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution

View file

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
namespace BTCPayServer.Plugins.Lethean.Configuration
{
public class LetheanConfiguration
{
public Dictionary<string, LetheanConfigurationItem> LetheanConfigurationItems { get; set; } = [];
}
public class LetheanConfigurationItem
{
public Uri DaemonRpcUri { get; set; }
public Uri InternalWalletRpcUri { get; set; }
public string WalletDirectory { get; set; }
}
}

View file

@ -8,9 +8,9 @@ using BTCPayServer.Abstractions.Constants;
using BTCPayServer.Client;
using BTCPayServer.Data;
using BTCPayServer.Payments;
using BTCPayServer.Plugins.Zano.Configuration;
using BTCPayServer.Plugins.Zano.Payments;
using BTCPayServer.Plugins.Zano.Services;
using BTCPayServer.Plugins.Lethean.Configuration;
using BTCPayServer.Plugins.Lethean.Payments;
using BTCPayServer.Plugins.Lethean.Services;
using BTCPayServer.Services.Invoices;
using BTCPayServer.Services.Stores;
@ -18,58 +18,58 @@ using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Localization;
namespace BTCPayServer.Plugins.Zano.Controllers
namespace BTCPayServer.Plugins.Lethean.Controllers
{
[Route("stores/{storeId}/zano")]
[Route("stores/{storeId}/lethean")]
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie)]
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
[Authorize(Policy = Policies.CanModifyServerSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
public class UIZanoStoreController : Controller
public class UILetheanStoreController : Controller
{
private readonly ZanoConfiguration _zanoConfiguration;
private readonly LetheanConfiguration _letheanConfiguration;
private readonly StoreRepository _storeRepository;
private readonly ZanoRpcProvider _zanoRpcProvider;
private readonly LetheanRpcProvider _letheanRpcProvider;
private readonly PaymentMethodHandlerDictionary _handlers;
private IStringLocalizer StringLocalizer { get; }
public UIZanoStoreController(ZanoConfiguration zanoConfiguration,
StoreRepository storeRepository, ZanoRpcProvider zanoRpcProvider,
public UILetheanStoreController(LetheanConfiguration letheanConfiguration,
StoreRepository storeRepository, LetheanRpcProvider letheanRpcProvider,
PaymentMethodHandlerDictionary handlers,
IStringLocalizer stringLocalizer)
{
_zanoConfiguration = zanoConfiguration;
_letheanConfiguration = letheanConfiguration;
_storeRepository = storeRepository;
_zanoRpcProvider = zanoRpcProvider;
_letheanRpcProvider = letheanRpcProvider;
_handlers = handlers;
StringLocalizer = stringLocalizer;
}
public StoreData StoreData => HttpContext.GetStoreData();
private ZanoPaymentMethodViewModel GetZanoPaymentMethodViewModel(
private LetheanPaymentMethodViewModel GetLetheanPaymentMethodViewModel(
StoreData storeData, string cryptoCode,
IPaymentFilter excludeFilters)
{
var zano = storeData.GetPaymentMethodConfigs(_handlers)
.Where(s => s.Value is ZanoPaymentPromptDetails)
.Select(s => (PaymentMethodId: s.Key, Details: (ZanoPaymentPromptDetails)s.Value));
var lethean = storeData.GetPaymentMethodConfigs(_handlers)
.Where(s => s.Value is LetheanPaymentPromptDetails)
.Select(s => (PaymentMethodId: s.Key, Details: (LetheanPaymentPromptDetails)s.Value));
var pmi = PaymentTypes.CHAIN.GetPaymentMethodId(cryptoCode);
var settings = zano.Where(method => method.PaymentMethodId == pmi).Select(m => m.Details).SingleOrDefault();
_zanoRpcProvider.Summaries.TryGetValue(cryptoCode, out var summary);
var settings = lethean.Where(method => method.PaymentMethodId == pmi).Select(m => m.Details).SingleOrDefault();
_letheanRpcProvider.Summaries.TryGetValue(cryptoCode, out var summary);
var settlementThresholdChoice = ZanoSettlementThresholdChoice.StoreSpeedPolicy;
var settlementThresholdChoice = LetheanSettlementThresholdChoice.StoreSpeedPolicy;
if (settings != null && settings.InvoiceSettledConfirmationThreshold is { } confirmations)
{
settlementThresholdChoice = confirmations switch
{
0 => ZanoSettlementThresholdChoice.ZeroConfirmation,
1 => ZanoSettlementThresholdChoice.AtLeastOne,
10 => ZanoSettlementThresholdChoice.AtLeastTen,
_ => ZanoSettlementThresholdChoice.Custom
0 => LetheanSettlementThresholdChoice.ZeroConfirmation,
1 => LetheanSettlementThresholdChoice.AtLeastOne,
10 => LetheanSettlementThresholdChoice.AtLeastTen,
_ => LetheanSettlementThresholdChoice.Custom
};
}
return new ZanoPaymentMethodViewModel()
return new LetheanPaymentMethodViewModel()
{
Enabled =
settings != null &&
@ -79,32 +79,32 @@ namespace BTCPayServer.Plugins.Zano.Controllers
SettlementConfirmationThresholdChoice = settlementThresholdChoice,
CustomSettlementConfirmationThreshold =
settings != null &&
settlementThresholdChoice is ZanoSettlementThresholdChoice.Custom
settlementThresholdChoice is LetheanSettlementThresholdChoice.Custom
? settings.InvoiceSettledConfirmationThreshold
: null
};
}
[HttpGet("{cryptoCode}")]
public IActionResult GetStoreZanoPaymentMethod(string cryptoCode)
public IActionResult GetStoreLetheanPaymentMethod(string cryptoCode)
{
cryptoCode = cryptoCode.ToUpperInvariant();
if (!_zanoConfiguration.ZanoConfigurationItems.ContainsKey(cryptoCode))
if (!_letheanConfiguration.LetheanConfigurationItems.ContainsKey(cryptoCode))
{
return NotFound();
}
var vm = GetZanoPaymentMethodViewModel(StoreData, cryptoCode,
var vm = GetLetheanPaymentMethodViewModel(StoreData, cryptoCode,
StoreData.GetStoreBlob().GetExcludedPaymentMethods());
return View("/Views/Zano/GetStoreZanoPaymentMethod.cshtml", vm);
return View("/Views/Lethean/GetStoreLetheanPaymentMethod.cshtml", vm);
}
[HttpPost("{cryptoCode}")]
[DisableRequestSizeLimit]
public async Task<IActionResult> GetStoreZanoPaymentMethod(ZanoPaymentMethodViewModel viewModel, string cryptoCode)
public async Task<IActionResult> GetStoreLetheanPaymentMethod(LetheanPaymentMethodViewModel viewModel, string cryptoCode)
{
cryptoCode = cryptoCode.ToUpperInvariant();
if (!_zanoConfiguration.ZanoConfigurationItems.TryGetValue(cryptoCode,
if (!_letheanConfiguration.LetheanConfigurationItems.TryGetValue(cryptoCode,
out _))
{
return NotFound();
@ -112,24 +112,24 @@ namespace BTCPayServer.Plugins.Zano.Controllers
if (!ModelState.IsValid)
{
var vm = GetZanoPaymentMethodViewModel(StoreData, cryptoCode,
var vm = GetLetheanPaymentMethodViewModel(StoreData, cryptoCode,
StoreData.GetStoreBlob().GetExcludedPaymentMethods());
vm.Enabled = viewModel.Enabled;
vm.SettlementConfirmationThresholdChoice = viewModel.SettlementConfirmationThresholdChoice;
vm.CustomSettlementConfirmationThreshold = viewModel.CustomSettlementConfirmationThreshold;
return View("/Views/Zano/GetStoreZanoPaymentMethod.cshtml", vm);
return View("/Views/Lethean/GetStoreLetheanPaymentMethod.cshtml", vm);
}
var storeData = StoreData;
var blob = storeData.GetStoreBlob();
storeData.SetPaymentMethodConfig(_handlers[PaymentTypes.CHAIN.GetPaymentMethodId(cryptoCode)], new ZanoPaymentPromptDetails()
storeData.SetPaymentMethodConfig(_handlers[PaymentTypes.CHAIN.GetPaymentMethodId(cryptoCode)], new LetheanPaymentPromptDetails()
{
InvoiceSettledConfirmationThreshold = viewModel.SettlementConfirmationThresholdChoice switch
{
ZanoSettlementThresholdChoice.ZeroConfirmation => 0,
ZanoSettlementThresholdChoice.AtLeastOne => 1,
ZanoSettlementThresholdChoice.AtLeastTen => 10,
ZanoSettlementThresholdChoice.Custom when viewModel.CustomSettlementConfirmationThreshold is { } custom => custom,
LetheanSettlementThresholdChoice.ZeroConfirmation => 0,
LetheanSettlementThresholdChoice.AtLeastOne => 1,
LetheanSettlementThresholdChoice.AtLeastTen => 10,
LetheanSettlementThresholdChoice.Custom when viewModel.CustomSettlementConfirmationThreshold is { } custom => custom,
_ => null
}
});
@ -137,23 +137,23 @@ namespace BTCPayServer.Plugins.Zano.Controllers
blob.SetExcluded(PaymentTypes.CHAIN.GetPaymentMethodId(viewModel.CryptoCode), !viewModel.Enabled);
storeData.SetStoreBlob(blob);
await _storeRepository.UpdateStore(storeData);
return RedirectToAction("GetStoreZanoPaymentMethod", new { cryptoCode });
return RedirectToAction("GetStoreLetheanPaymentMethod", new { cryptoCode });
}
public class ZanoPaymentMethodViewModel : IValidatableObject
public class LetheanPaymentMethodViewModel : IValidatableObject
{
public ZanoRpcProvider.ZanoSummary Summary { get; set; }
public LetheanRpcProvider.LetheanSummary Summary { get; set; }
public string CryptoCode { get; set; }
public bool Enabled { get; set; }
[Display(Name = "Consider the invoice settled when the payment transaction ...")]
public ZanoSettlementThresholdChoice SettlementConfirmationThresholdChoice { get; set; }
public LetheanSettlementThresholdChoice SettlementConfirmationThresholdChoice { get; set; }
[Display(Name = "Required Confirmations"), Range(0, 100)]
public long? CustomSettlementConfirmationThreshold { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (SettlementConfirmationThresholdChoice is ZanoSettlementThresholdChoice.Custom
if (SettlementConfirmationThresholdChoice is LetheanSettlementThresholdChoice.Custom
&& CustomSettlementConfirmationThreshold is null)
{
yield return new ValidationResult(
@ -163,7 +163,7 @@ namespace BTCPayServer.Plugins.Zano.Controllers
}
}
public enum ZanoSettlementThresholdChoice
public enum LetheanSettlementThresholdChoice
{
[Display(Name = "Store Speed Policy", Description = "Use the store's speed policy")]
StoreSpeedPolicy,

View file

@ -7,9 +7,9 @@ using BTCPayServer.Abstractions.Models;
using BTCPayServer.Configuration;
using BTCPayServer.Hosting;
using BTCPayServer.Payments;
using BTCPayServer.Plugins.Zano.Configuration;
using BTCPayServer.Plugins.Zano.Payments;
using BTCPayServer.Plugins.Zano.Services;
using BTCPayServer.Plugins.Lethean.Configuration;
using BTCPayServer.Plugins.Lethean.Payments;
using BTCPayServer.Plugins.Lethean.Services;
using BTCPayServer.Services;
using Microsoft.Extensions.Configuration;
@ -20,9 +20,9 @@ using NBitcoin;
using NBXplorer;
namespace BTCPayServer.Plugins.Zano;
namespace BTCPayServer.Plugins.Lethean;
public class ZanoPlugin : BaseBTCPayServerPlugin
public class LetheanPlugin : BaseBTCPayServerPlugin
{
public override IBTCPayServerPlugin.PluginDependency[] Dependencies { get; } =
{
@ -35,44 +35,44 @@ public class ZanoPlugin : BaseBTCPayServerPlugin
var prov = pluginServices.BootstrapServices.GetRequiredService<NBXplorerNetworkProvider>();
var chainName = prov.NetworkType;
var network = new ZanoSpecificBtcPayNetwork()
var network = new LetheanSpecificBtcPayNetwork()
{
CryptoCode = "ZANO",
DisplayName = "Zano",
CryptoCode = "LTHN",
DisplayName = "Lethean",
Divisibility = 12,
DefaultRateRules = new[]
{
"ZANO_X = ZANO_BTC * BTC_X",
"ZANO_BTC = coingecko(ZANO_BTC)"
"LTHN_X = LTHN_BTC * BTC_X",
"LTHN_BTC = coingecko(LTHN_BTC)"
},
CryptoImagePath = "zano.svg",
UriScheme = "zano"
CryptoImagePath = "lethean.svg",
UriScheme = "lethean"
};
var blockExplorerLink = chainName == ChainName.Mainnet
? "https://explorer.zano.org/transaction/{0}"
: "https://testnet-explorer.zano.org/transaction/{0}";
var pmi = PaymentTypes.CHAIN.GetPaymentMethodId("ZANO");
? "https://explorer.lethean.org/transaction/{0}"
: "https://testnet-explorer.lethean.org/transaction/{0}";
var pmi = PaymentTypes.CHAIN.GetPaymentMethodId("LTHN");
services.AddDefaultPrettyName(pmi, network.DisplayName);
services.AddBTCPayNetwork(network)
.AddTransactionLinkProvider(pmi, new SimpleTransactionLinkProvider(blockExplorerLink));
services.AddSingleton(provider =>
ConfigureZanoConfiguration(provider));
services.AddHttpClient("ZanoClient");
services.AddSingleton<ZanoRpcProvider>();
services.AddHostedService<ZanoSummaryUpdaterHostedService>();
services.AddHostedService<ZanoListener>();
services.AddHostedService<ZanoLoadUpService>();
ConfigureLetheanConfiguration(provider));
services.AddHttpClient("LetheanClient");
services.AddSingleton<LetheanRpcProvider>();
services.AddHostedService<LetheanSummaryUpdaterHostedService>();
services.AddHostedService<LetheanListener>();
services.AddHostedService<LetheanLoadUpService>();
services.AddSingleton(provider =>
(IPaymentMethodHandler)ActivatorUtilities.CreateInstance(provider, typeof(ZanoPaymentMethodHandler), network));
(IPaymentMethodHandler)ActivatorUtilities.CreateInstance(provider, typeof(LetheanPaymentMethodHandler), network));
services.AddSingleton(provider =>
(IPaymentLinkExtension)ActivatorUtilities.CreateInstance(provider, typeof(ZanoPaymentLinkExtension), network, pmi));
(IPaymentLinkExtension)ActivatorUtilities.CreateInstance(provider, typeof(LetheanPaymentLinkExtension), network, pmi));
services.AddSingleton(provider =>
(ICheckoutModelExtension)ActivatorUtilities.CreateInstance(provider, typeof(ZanoCheckoutModelExtension), network, pmi));
(ICheckoutModelExtension)ActivatorUtilities.CreateInstance(provider, typeof(LetheanCheckoutModelExtension), network, pmi));
services.AddUIExtension("store-wallets-nav", "/Views/Zano/StoreWalletsNavZanoExtension.cshtml");
services.AddUIExtension("store-invoices-payments", "/Views/Zano/ViewZanoPaymentData.cshtml");
services.AddSingleton<ISyncSummaryProvider, ZanoSyncSummaryProvider>();
services.AddUIExtension("store-wallets-nav", "/Views/Lethean/StoreWalletsNavLetheanExtension.cshtml");
services.AddUIExtension("store-invoices-payments", "/Views/Lethean/ViewLetheanPaymentData.cshtml");
services.AddSingleton<ISyncSummaryProvider, LetheanSyncSummaryProvider>();
}
class SimpleTransactionLinkProvider : DefaultTransactionLinkProvider
{
@ -90,30 +90,30 @@ public class ZanoPlugin : BaseBTCPayServerPlugin
}
}
private static ZanoConfiguration ConfigureZanoConfiguration(IServiceProvider serviceProvider)
private static LetheanConfiguration ConfigureLetheanConfiguration(IServiceProvider serviceProvider)
{
var configuration = serviceProvider.GetService<IConfiguration>();
var btcPayNetworkProvider = serviceProvider.GetService<BTCPayNetworkProvider>();
var result = new ZanoConfiguration();
var result = new LetheanConfiguration();
var supportedNetworks = btcPayNetworkProvider.GetAll()
.OfType<ZanoSpecificBtcPayNetwork>();
.OfType<LetheanSpecificBtcPayNetwork>();
foreach (var zanoNetwork in supportedNetworks)
foreach (var letheanNetwork in supportedNetworks)
{
var daemonUri =
configuration.GetOrDefault<Uri>($"{zanoNetwork.CryptoCode}_daemon_uri",
configuration.GetOrDefault<Uri>($"{letheanNetwork.CryptoCode}_daemon_uri",
null);
var walletDaemonUri =
configuration.GetOrDefault<Uri>(
$"{zanoNetwork.CryptoCode}_wallet_daemon_uri", null);
$"{letheanNetwork.CryptoCode}_wallet_daemon_uri", null);
var walletDaemonWalletDirectory =
configuration.GetOrDefault<string>(
$"{zanoNetwork.CryptoCode}_wallet_daemon_walletdir", null);
$"{letheanNetwork.CryptoCode}_wallet_daemon_walletdir", null);
if (daemonUri == null || walletDaemonUri == null)
{
var logger = serviceProvider.GetRequiredService<ILogger<ZanoPlugin>>();
var cryptoCode = zanoNetwork.CryptoCode.ToUpperInvariant();
var logger = serviceProvider.GetRequiredService<ILogger<LetheanPlugin>>();
var cryptoCode = letheanNetwork.CryptoCode.ToUpperInvariant();
if (daemonUri is null)
{
logger.LogWarning("BTCPAY_{CryptoCode}_DAEMON_URI is not configured", cryptoCode);
@ -126,7 +126,7 @@ public class ZanoPlugin : BaseBTCPayServerPlugin
}
else
{
result.ZanoConfigurationItems.Add(zanoNetwork.CryptoCode, new ZanoConfigurationItem
result.LetheanConfigurationItems.Add(letheanNetwork.CryptoCode, new LetheanConfigurationItem
{
DaemonRpcUri = daemonUri,
InternalWalletRpcUri = walletDaemonUri,

View file

@ -0,0 +1,7 @@
namespace BTCPayServer.Plugins.Lethean;
public class LetheanSpecificBtcPayNetwork : BTCPayNetworkBase
{
public int MaxTrackedConfirmation = 10;
public string UriScheme { get; set; }
}

View file

@ -3,18 +3,18 @@ using System.Linq;
using BTCPayServer.Payments;
using BTCPayServer.Payments.Bitcoin;
using BTCPayServer.Plugins.Zano.Services;
using BTCPayServer.Plugins.Lethean.Services;
using BTCPayServer.Services.Invoices;
namespace BTCPayServer.Plugins.Zano.Payments
namespace BTCPayServer.Plugins.Lethean.Payments
{
public class ZanoCheckoutModelExtension : ICheckoutModelExtension
public class LetheanCheckoutModelExtension : ICheckoutModelExtension
{
private readonly BTCPayNetworkBase _network;
private readonly PaymentMethodHandlerDictionary _handlers;
private readonly IPaymentLinkExtension paymentLinkExtension;
public ZanoCheckoutModelExtension(
public LetheanCheckoutModelExtension(
PaymentMethodId paymentMethodId,
IEnumerable<IPaymentLinkExtension> paymentLinkExtensions,
BTCPayNetworkBase network,
@ -32,19 +32,19 @@ namespace BTCPayServer.Plugins.Zano.Payments
public void ModifyCheckoutModel(CheckoutModelContext context)
{
if (context is not { Handler: ZanoPaymentMethodHandler handler })
if (context is not { Handler: LetheanPaymentMethodHandler handler })
{
return;
}
context.Model.CheckoutBodyComponentName = BitcoinCheckoutModelExtension.CheckoutBodyComponentName;
var details = context.InvoiceEntity.GetPayments(true)
.Select(p => p.GetDetails<ZanoPaymentData>(handler))
.Select(p => p.GetDetails<LetheanPaymentData>(handler))
.Where(p => p is not null)
.FirstOrDefault();
if (details is not null)
{
context.Model.ReceivedConfirmations = details.ConfirmationCount;
context.Model.RequiredConfirmations = (int)ZanoListener.ConfirmationsRequired(details, context.InvoiceEntity.SpeedPolicy);
context.Model.RequiredConfirmations = (int)LetheanListener.ConfirmationsRequired(details, context.InvoiceEntity.SpeedPolicy);
}
context.Model.InvoiceBitcoinUrl = paymentLinkExtension.GetPaymentLink(context.Prompt, context.UrlHelper);

View file

@ -1,6 +1,6 @@
namespace BTCPayServer.Plugins.Zano.Payments
namespace BTCPayServer.Plugins.Lethean.Payments
{
public class ZanoOnChainPaymentMethodDetails
public class LetheanOnChainPaymentMethodDetails
{
public string PaymentId { get; set; }
public long? InvoiceSettledConfirmationThreshold { get; set; }

View file

@ -1,6 +1,6 @@
namespace BTCPayServer.Plugins.Zano.Payments
namespace BTCPayServer.Plugins.Lethean.Payments
{
public class ZanoPaymentData
public class LetheanPaymentData
{
public string PaymentId { get; set; }
public long BlockHeight { get; set; }

View file

@ -6,13 +6,13 @@ using BTCPayServer.Services.Invoices;
using Microsoft.AspNetCore.Mvc;
namespace BTCPayServer.Plugins.Zano.Payments
namespace BTCPayServer.Plugins.Lethean.Payments
{
public class ZanoPaymentLinkExtension : IPaymentLinkExtension
public class LetheanPaymentLinkExtension : IPaymentLinkExtension
{
private readonly ZanoSpecificBtcPayNetwork _network;
private readonly LetheanSpecificBtcPayNetwork _network;
public ZanoPaymentLinkExtension(PaymentMethodId paymentMethodId, ZanoSpecificBtcPayNetwork network)
public LetheanPaymentLinkExtension(PaymentMethodId paymentMethodId, LetheanSpecificBtcPayNetwork network)
{
PaymentMethodId = paymentMethodId;
_network = network;

View file

@ -4,36 +4,36 @@ using System.Threading.Tasks;
using BTCPayServer.Data;
using BTCPayServer.Payments;
using BTCPayServer.Plugins.Zano.RPC.Models;
using BTCPayServer.Plugins.Zano.Services;
using BTCPayServer.Plugins.Zano.Utils;
using BTCPayServer.Plugins.Lethean.RPC.Models;
using BTCPayServer.Plugins.Lethean.Services;
using BTCPayServer.Plugins.Lethean.Utils;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace BTCPayServer.Plugins.Zano.Payments
namespace BTCPayServer.Plugins.Lethean.Payments
{
public class ZanoPaymentMethodHandler : IPaymentMethodHandler
public class LetheanPaymentMethodHandler : IPaymentMethodHandler
{
private readonly ZanoSpecificBtcPayNetwork _network;
public ZanoSpecificBtcPayNetwork Network => _network;
private readonly LetheanSpecificBtcPayNetwork _network;
public LetheanSpecificBtcPayNetwork Network => _network;
public JsonSerializer Serializer { get; }
private readonly ZanoRpcProvider _zanoRpcProvider;
private readonly LetheanRpcProvider _letheanRpcProvider;
public PaymentMethodId PaymentMethodId { get; }
// Fixed fee: 0.01 ZANO in atomic units (12 decimals)
// Fixed fee: 0.01 LTHN in atomic units (12 decimals)
private const long FixedFeeAtomicUnits = 10_000_000_000;
public ZanoPaymentMethodHandler(ZanoSpecificBtcPayNetwork network, ZanoRpcProvider zanoRpcProvider)
public LetheanPaymentMethodHandler(LetheanSpecificBtcPayNetwork network, LetheanRpcProvider letheanRpcProvider)
{
PaymentMethodId = PaymentTypes.CHAIN.GetPaymentMethodId(network.CryptoCode);
_network = network;
Serializer = BlobSerializer.CreateSerializer().Serializer;
_zanoRpcProvider = zanoRpcProvider;
_letheanRpcProvider = letheanRpcProvider;
}
bool IsReady() => _zanoRpcProvider.IsConfigured(_network.CryptoCode) && _zanoRpcProvider.IsAvailable(_network.CryptoCode);
bool IsReady() => _letheanRpcProvider.IsConfigured(_network.CryptoCode) && _letheanRpcProvider.IsAvailable(_network.CryptoCode);
public Task BeforeFetchingRates(PaymentMethodContext context)
{
@ -44,7 +44,7 @@ namespace BTCPayServer.Plugins.Zano.Payments
try
{
var paymentId = GeneratePaymentId();
var walletClient = _zanoRpcProvider.WalletRpcClients[_network.CryptoCode];
var walletClient = _letheanRpcProvider.WalletRpcClients[_network.CryptoCode];
context.State = new Prepare()
{
ReserveAddress = walletClient.SendCommandAsync<MakeIntegratedAddressRequest, MakeIntegratedAddressResponse>(
@ -63,32 +63,32 @@ namespace BTCPayServer.Plugins.Zano.Payments
public async Task ConfigurePrompt(PaymentMethodContext context)
{
if (!_zanoRpcProvider.IsConfigured(_network.CryptoCode))
if (!_letheanRpcProvider.IsConfigured(_network.CryptoCode))
{
throw new PaymentMethodUnavailableException("BTCPAY_ZANO_WALLET_DAEMON_URI or BTCPAY_ZANO_DAEMON_URI isn't configured");
throw new PaymentMethodUnavailableException("BTCPAY_LTHN_WALLET_DAEMON_URI or BTCPAY_LTHN_DAEMON_URI isn't configured");
}
if (!_zanoRpcProvider.IsAvailable(_network.CryptoCode) || context.State is not Prepare zanoPrepare)
if (!_letheanRpcProvider.IsAvailable(_network.CryptoCode) || context.State is not Prepare letheanPrepare)
{
throw new PaymentMethodUnavailableException("Node or wallet not available");
}
var address = await zanoPrepare.ReserveAddress;
var address = await letheanPrepare.ReserveAddress;
var details = new ZanoOnChainPaymentMethodDetails()
var details = new LetheanOnChainPaymentMethodDetails()
{
PaymentId = zanoPrepare.PaymentId,
PaymentId = letheanPrepare.PaymentId,
InvoiceSettledConfirmationThreshold = ParsePaymentMethodConfig(context.PaymentMethodConfig).InvoiceSettledConfirmationThreshold
};
context.Prompt.Destination = address.IntegratedAddress;
context.Prompt.PaymentMethodFee = ZanoMoney.Convert(FixedFeeAtomicUnits);
context.Prompt.PaymentMethodFee = LetheanMoney.Convert(FixedFeeAtomicUnits);
context.Prompt.Details = JObject.FromObject(details, Serializer);
context.TrackedDestinations.Add(address.IntegratedAddress);
}
private ZanoPaymentPromptDetails ParsePaymentMethodConfig(JToken config)
private LetheanPaymentPromptDetails ParsePaymentMethodConfig(JToken config)
{
return config.ToObject<ZanoPaymentPromptDetails>(Serializer) ?? throw new FormatException($"Invalid {nameof(ZanoPaymentMethodHandler)}");
return config.ToObject<LetheanPaymentPromptDetails>(Serializer) ?? throw new FormatException($"Invalid {nameof(LetheanPaymentMethodHandler)}");
}
object IPaymentMethodHandler.ParsePaymentMethodConfig(JToken config)
{
@ -101,18 +101,18 @@ namespace BTCPayServer.Plugins.Zano.Payments
public string PaymentId;
}
public ZanoOnChainPaymentMethodDetails ParsePaymentPromptDetails(JToken details)
public LetheanOnChainPaymentMethodDetails ParsePaymentPromptDetails(JToken details)
{
return details.ToObject<ZanoOnChainPaymentMethodDetails>(Serializer);
return details.ToObject<LetheanOnChainPaymentMethodDetails>(Serializer);
}
object IPaymentMethodHandler.ParsePaymentPromptDetails(JToken details)
{
return ParsePaymentPromptDetails(details);
}
public ZanoPaymentData ParsePaymentDetails(JToken details)
public LetheanPaymentData ParsePaymentDetails(JToken details)
{
return details.ToObject<ZanoPaymentData>(Serializer) ?? throw new FormatException($"Invalid {nameof(ZanoPaymentMethodHandler)}");
return details.ToObject<LetheanPaymentData>(Serializer) ?? throw new FormatException($"Invalid {nameof(LetheanPaymentMethodHandler)}");
}
object IPaymentMethodHandler.ParsePaymentDetails(JToken details)
{

View file

@ -0,0 +1,7 @@
namespace BTCPayServer.Plugins.Lethean.Payments
{
public class LetheanPaymentPromptDetails
{
public long? InvoiceSettledConfirmationThreshold { get; set; }
}
}

View file

@ -8,7 +8,7 @@ using System.Threading.Tasks;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
namespace BTCPayServer.Plugins.Zano.RPC
namespace BTCPayServer.Plugins.Lethean.RPC
{
public class JsonRpcClient
{

View file

@ -0,0 +1,7 @@
namespace BTCPayServer.Plugins.Lethean.RPC
{
public class LetheanPollEvent
{
public string CryptoCode { get; set; }
}
}

View file

@ -1,6 +1,6 @@
using Newtonsoft.Json;
namespace BTCPayServer.Plugins.Zano.RPC.Models
namespace BTCPayServer.Plugins.Lethean.RPC.Models
{
public class ErrorResponse
{

View file

@ -1,6 +1,6 @@
using Newtonsoft.Json;
namespace BTCPayServer.Plugins.Zano.RPC.Models;
namespace BTCPayServer.Plugins.Lethean.RPC.Models;
public class GetBalanceResponse
{

View file

@ -2,7 +2,7 @@ using System.Collections.Generic;
using Newtonsoft.Json;
namespace BTCPayServer.Plugins.Zano.RPC.Models
namespace BTCPayServer.Plugins.Lethean.RPC.Models
{
public class GetBulkPaymentsRequest
{

View file

@ -2,7 +2,7 @@ using System.Collections.Generic;
using Newtonsoft.Json;
namespace BTCPayServer.Plugins.Zano.RPC.Models
namespace BTCPayServer.Plugins.Lethean.RPC.Models
{
public class GetBulkPaymentsResponse
{

View file

@ -1,6 +1,6 @@
using Newtonsoft.Json;
namespace BTCPayServer.Plugins.Zano.RPC.Models
namespace BTCPayServer.Plugins.Lethean.RPC.Models
{
public class GetInfoResponse
{

View file

@ -1,6 +1,6 @@
using Newtonsoft.Json;
namespace BTCPayServer.Plugins.Zano.RPC.Models
namespace BTCPayServer.Plugins.Lethean.RPC.Models
{
public class GetWalletInfoResponse
{

View file

@ -1,6 +1,6 @@
using Newtonsoft.Json;
namespace BTCPayServer.Plugins.Zano.RPC.Models
namespace BTCPayServer.Plugins.Lethean.RPC.Models
{
public class MakeIntegratedAddressRequest
{

View file

@ -1,6 +1,6 @@
using Newtonsoft.Json;
namespace BTCPayServer.Plugins.Zano.RPC.Models
namespace BTCPayServer.Plugins.Lethean.RPC.Models
{
public class MakeIntegratedAddressResponse
{

View file

@ -1,6 +1,6 @@
using Newtonsoft.Json;
namespace BTCPayServer.Plugins.Zano.RPC.Models
namespace BTCPayServer.Plugins.Lethean.RPC.Models
{
public class OpenWalletRequest
{

View file

@ -1,6 +1,6 @@
using Newtonsoft.Json;
namespace BTCPayServer.Plugins.Zano.RPC.Models
namespace BTCPayServer.Plugins.Lethean.RPC.Models
{
public class OpenWalletResponse
{

View file

@ -3,7 +3,7 @@ using System.Globalization;
using Newtonsoft.Json;
namespace BTCPayServer.Plugins.Zano.RPC.Models
namespace BTCPayServer.Plugins.Lethean.RPC.Models
{
public class ParseStringConverter : JsonConverter
{

View file

@ -9,11 +9,11 @@ using BTCPayServer.Data;
using BTCPayServer.Events;
using BTCPayServer.HostedServices;
using BTCPayServer.Payments;
using BTCPayServer.Plugins.Zano.Configuration;
using BTCPayServer.Plugins.Zano.Payments;
using BTCPayServer.Plugins.Zano.RPC;
using BTCPayServer.Plugins.Zano.RPC.Models;
using BTCPayServer.Plugins.Zano.Utils;
using BTCPayServer.Plugins.Lethean.Configuration;
using BTCPayServer.Plugins.Lethean.Payments;
using BTCPayServer.Plugins.Lethean.RPC;
using BTCPayServer.Plugins.Lethean.RPC.Models;
using BTCPayServer.Plugins.Lethean.Utils;
using BTCPayServer.Services;
using BTCPayServer.Services.Invoices;
@ -21,34 +21,34 @@ using Microsoft.Extensions.Logging;
using Newtonsoft.Json.Linq;
namespace BTCPayServer.Plugins.Zano.Services
namespace BTCPayServer.Plugins.Lethean.Services
{
public class ZanoListener : EventHostedServiceBase
public class LetheanListener : EventHostedServiceBase
{
private readonly InvoiceRepository _invoiceRepository;
private readonly EventAggregator _eventAggregator;
private readonly ZanoRpcProvider _zanoRpcProvider;
private readonly ZanoConfiguration _zanoConfiguration;
private readonly LetheanRpcProvider _letheanRpcProvider;
private readonly LetheanConfiguration _letheanConfiguration;
private readonly BTCPayNetworkProvider _networkProvider;
private readonly ILogger<ZanoListener> _logger;
private readonly ILogger<LetheanListener> _logger;
private readonly PaymentMethodHandlerDictionary _handlers;
private readonly InvoiceActivator _invoiceActivator;
private readonly PaymentService _paymentService;
public ZanoListener(InvoiceRepository invoiceRepository,
public LetheanListener(InvoiceRepository invoiceRepository,
EventAggregator eventAggregator,
ZanoRpcProvider zanoRpcProvider,
ZanoConfiguration zanoConfiguration,
LetheanRpcProvider letheanRpcProvider,
LetheanConfiguration letheanConfiguration,
BTCPayNetworkProvider networkProvider,
ILogger<ZanoListener> logger,
ILogger<LetheanListener> logger,
PaymentMethodHandlerDictionary handlers,
InvoiceActivator invoiceActivator,
PaymentService paymentService) : base(eventAggregator, logger)
{
_invoiceRepository = invoiceRepository;
_eventAggregator = eventAggregator;
_zanoRpcProvider = zanoRpcProvider;
_zanoConfiguration = zanoConfiguration;
_letheanRpcProvider = letheanRpcProvider;
_letheanConfiguration = letheanConfiguration;
_networkProvider = networkProvider;
_logger = logger;
_handlers = handlers;
@ -59,29 +59,29 @@ namespace BTCPayServer.Plugins.Zano.Services
protected override void SubscribeToEvents()
{
base.SubscribeToEvents();
Subscribe<ZanoPollEvent>();
Subscribe<ZanoRpcProvider.ZanoDaemonStateChange>();
Subscribe<LetheanPollEvent>();
Subscribe<LetheanRpcProvider.LetheanDaemonStateChange>();
}
protected override async Task ProcessEvent(object evt, CancellationToken cancellationToken)
{
if (evt is ZanoRpcProvider.ZanoDaemonStateChange stateChange)
if (evt is LetheanRpcProvider.LetheanDaemonStateChange stateChange)
{
if (_zanoRpcProvider.IsAvailable(stateChange.CryptoCode))
if (_letheanRpcProvider.IsAvailable(stateChange.CryptoCode))
{
_logger.LogInformation("{CryptoCode} just became available", stateChange.CryptoCode);
_ = UpdateAnyPendingZanoPayment(stateChange.CryptoCode);
_ = UpdateAnyPendingLetheanPayment(stateChange.CryptoCode);
}
else
{
_logger.LogInformation("{CryptoCode} just became unavailable", stateChange.CryptoCode);
}
}
else if (evt is ZanoPollEvent pollEvent)
else if (evt is LetheanPollEvent pollEvent)
{
if (_zanoRpcProvider.IsAvailable(pollEvent.CryptoCode))
if (_letheanRpcProvider.IsAvailable(pollEvent.CryptoCode))
{
await UpdateAnyPendingZanoPayment(pollEvent.CryptoCode);
await UpdateAnyPendingLetheanPayment(pollEvent.CryptoCode);
}
}
}
@ -114,14 +114,14 @@ namespace BTCPayServer.Plugins.Zano.Services
return;
}
var walletRpcClient = _zanoRpcProvider.WalletRpcClients[cryptoCode];
var walletRpcClient = _letheanRpcProvider.WalletRpcClients[cryptoCode];
var network = _networkProvider.GetNetwork(cryptoCode);
var paymentMethodId = PaymentTypes.CHAIN.GetPaymentMethodId(network.CryptoCode);
var handler = (ZanoPaymentMethodHandler)_handlers[paymentMethodId];
var handler = (LetheanPaymentMethodHandler)_handlers[paymentMethodId];
// Get current daemon height for confirmation calculation
long currentHeight = 0;
if (_zanoRpcProvider.Summaries.TryGetValue(cryptoCode, out var summary))
if (_letheanRpcProvider.Summaries.TryGetValue(cryptoCode, out var summary))
{
currentHeight = summary.CurrentHeight;
}
@ -129,7 +129,7 @@ namespace BTCPayServer.Plugins.Zano.Services
// Collect all payment_ids from pending invoices
var expandedInvoices = invoices.Select(entity => (
Invoice: entity,
ExistingPayments: GetAllZanoPayments(entity, cryptoCode),
ExistingPayments: GetAllLetheanPayments(entity, cryptoCode),
Prompt: entity.GetPaymentPrompt(paymentMethodId),
PaymentMethodDetails: handler.ParsePaymentPromptDetails(entity.GetPaymentPrompt(paymentMethodId).Details)))
.ToList();
@ -174,7 +174,7 @@ namespace BTCPayServer.Plugins.Zano.Services
var updatedPaymentEntities = new List<(PaymentEntity Payment, InvoiceEntity invoice)>();
var processingTasks = new List<Task>();
// Deduplicate: Zano returns both confirmed (block_height>0) and mempool (block_height=0)
// Deduplicate: Lethean returns both confirmed (block_height>0) and mempool (block_height=0)
// entries for the same tx. Keep the confirmed entry when available.
var dedupedPayments = result.Payments
.GroupBy(p => $"{p.TxHash}#{p.PaymentId}")
@ -222,7 +222,7 @@ namespace BTCPayServer.Plugins.Zano.Services
}
private async Task UpdateExistingPaymentConfirmations(string cryptoCode, InvoiceEntity[] invoices,
ZanoPaymentMethodHandler handler, long currentHeight)
LetheanPaymentMethodHandler handler, long currentHeight)
{
if (currentHeight <= 0)
{
@ -233,7 +233,7 @@ namespace BTCPayServer.Plugins.Zano.Services
foreach (var invoice in invoices)
{
var existingPayments = GetAllZanoPayments(invoice, cryptoCode);
var existingPayments = GetAllLetheanPayments(invoice, cryptoCode);
foreach (var payment in existingPayments)
{
var data = handler.ParsePaymentDetails(payment.Details);
@ -268,9 +268,9 @@ namespace BTCPayServer.Plugins.Zano.Services
{
var network = _networkProvider.GetNetwork(cryptoCode);
var pmi = PaymentTypes.CHAIN.GetPaymentMethodId(network.CryptoCode);
var handler = (ZanoPaymentMethodHandler)_handlers[pmi];
var handler = (LetheanPaymentMethodHandler)_handlers[pmi];
var promptDetails = handler.ParsePaymentPromptDetails(invoice.GetPaymentPrompt(pmi).Details);
var details = new ZanoPaymentData()
var details = new LetheanPaymentData()
{
PaymentId = paymentId,
TransactionId = txId,
@ -283,7 +283,7 @@ namespace BTCPayServer.Plugins.Zano.Services
var paymentData = new PaymentData()
{
Status = status,
Amount = ZanoMoney.Convert(totalAmount),
Amount = LetheanMoney.Convert(totalAmount),
Created = DateTimeOffset.UtcNow,
Id = $"{txId}#{paymentId}",
Currency = network.CryptoCode,
@ -291,7 +291,7 @@ namespace BTCPayServer.Plugins.Zano.Services
}.Set(invoice, handler, details);
// Check if this tx exists as a payment to this invoice already
var alreadyExistingPayment = GetAllZanoPayments(invoice, cryptoCode)
var alreadyExistingPayment = GetAllLetheanPayments(invoice, cryptoCode)
.SingleOrDefault(c => c.Id == paymentData.Id && c.PaymentMethodId == pmi);
if (alreadyExistingPayment == null)
@ -311,10 +311,10 @@ namespace BTCPayServer.Plugins.Zano.Services
}
}
private bool GetStatus(ZanoPaymentData details, SpeedPolicy speedPolicy)
private bool GetStatus(LetheanPaymentData details, SpeedPolicy speedPolicy)
=> ConfirmationsRequired(details, speedPolicy) <= details.ConfirmationCount;
public static long ConfirmationsRequired(ZanoPaymentData details, SpeedPolicy speedPolicy)
public static long ConfirmationsRequired(LetheanPaymentData details, SpeedPolicy speedPolicy)
=> (details, speedPolicy) switch
{
(_, _) when details.ConfirmationCount < details.LockTime =>
@ -327,7 +327,7 @@ namespace BTCPayServer.Plugins.Zano.Services
_ => 6,
};
private async Task UpdateAnyPendingZanoPayment(string cryptoCode)
private async Task UpdateAnyPendingLetheanPayment(string cryptoCode)
{
var paymentMethodId = PaymentTypes.CHAIN.GetPaymentMethodId(cryptoCode);
var invoices = await _invoiceRepository.GetMonitoredInvoices(paymentMethodId);
@ -339,7 +339,7 @@ namespace BTCPayServer.Plugins.Zano.Services
await UpdatePaymentStates(cryptoCode, invoices);
}
private IEnumerable<PaymentEntity> GetAllZanoPayments(InvoiceEntity invoice, string cryptoCode)
private IEnumerable<PaymentEntity> GetAllLetheanPayments(InvoiceEntity invoice, string cryptoCode)
{
return invoice.GetPayments(false)
.Where(p => p.PaymentMethodId == PaymentTypes.CHAIN.GetPaymentMethodId(cryptoCode));

View file

@ -3,25 +3,25 @@ using System.IO;
using System.Threading;
using System.Threading.Tasks;
using BTCPayServer.Plugins.Zano.Configuration;
using BTCPayServer.Plugins.Zano.RPC.Models;
using BTCPayServer.Plugins.Lethean.Configuration;
using BTCPayServer.Plugins.Lethean.RPC.Models;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace BTCPayServer.Plugins.Zano.Services;
namespace BTCPayServer.Plugins.Lethean.Services;
public class ZanoLoadUpService : IHostedService
public class LetheanLoadUpService : IHostedService
{
private const string CryptoCode = "ZANO";
private readonly ILogger<ZanoLoadUpService> _logger;
private readonly ZanoRpcProvider _zanoRpcProvider;
private readonly ZanoConfiguration _zanoConfiguration;
private const string CryptoCode = "LTHN";
private readonly ILogger<LetheanLoadUpService> _logger;
private readonly LetheanRpcProvider _letheanRpcProvider;
private readonly LetheanConfiguration _letheanConfiguration;
public ZanoLoadUpService(ILogger<ZanoLoadUpService> logger, ZanoRpcProvider zanoRpcProvider, ZanoConfiguration zanoConfiguration)
public LetheanLoadUpService(ILogger<LetheanLoadUpService> logger, LetheanRpcProvider letheanRpcProvider, LetheanConfiguration letheanConfiguration)
{
_zanoRpcProvider = zanoRpcProvider;
_zanoConfiguration = zanoConfiguration;
_letheanRpcProvider = letheanRpcProvider;
_letheanConfiguration = letheanConfiguration;
_logger = logger;
}
@ -29,31 +29,31 @@ public class ZanoLoadUpService : IHostedService
{
try
{
if (!_zanoConfiguration.ZanoConfigurationItems.TryGetValue(CryptoCode, out var configItem))
if (!_letheanConfiguration.LetheanConfigurationItems.TryGetValue(CryptoCode, out var configItem))
{
_logger.LogInformation("No Zano configuration found, skipping wallet load");
_logger.LogInformation("No Lethean configuration found, skipping wallet load");
return;
}
var walletDir = configItem.WalletDirectory;
if (!string.IsNullOrEmpty(walletDir))
{
_logger.LogInformation("Attempting to load existing Zano wallet");
_logger.LogInformation("Attempting to load existing Lethean wallet");
string password = await TryToGetPassword(walletDir, cancellationToken);
await _zanoRpcProvider.WalletRpcClients[CryptoCode]
await _letheanRpcProvider.WalletRpcClients[CryptoCode]
.SendCommandAsync<OpenWalletRequest, OpenWalletResponse>("open_wallet",
new OpenWalletRequest { Filename = "wallet", Password = password }, cancellationToken);
await _zanoRpcProvider.UpdateSummary(CryptoCode);
_logger.LogInformation("Existing Zano wallet successfully loaded");
await _letheanRpcProvider.UpdateSummary(CryptoCode);
_logger.LogInformation("Existing Lethean wallet successfully loaded");
}
else
{
_logger.LogInformation("No wallet directory configured. Wallet should be pre-opened in simplewallet RPC mode.");
// Still try to update summary — wallet may already be running in RPC mode
await _zanoRpcProvider.UpdateSummary(CryptoCode);
await _letheanRpcProvider.UpdateSummary(CryptoCode);
}
}
catch (Exception ex)

View file

@ -4,33 +4,33 @@ using System.Collections.Immutable;
using System.Net.Http;
using System.Threading.Tasks;
using BTCPayServer.Plugins.Zano.Configuration;
using BTCPayServer.Plugins.Zano.RPC;
using BTCPayServer.Plugins.Zano.RPC.Models;
using BTCPayServer.Plugins.Lethean.Configuration;
using BTCPayServer.Plugins.Lethean.RPC;
using BTCPayServer.Plugins.Lethean.RPC.Models;
namespace BTCPayServer.Plugins.Zano.Services
namespace BTCPayServer.Plugins.Lethean.Services
{
public class ZanoRpcProvider
public class LetheanRpcProvider
{
private readonly ZanoConfiguration _zanoConfiguration;
private readonly LetheanConfiguration _letheanConfiguration;
private readonly EventAggregator _eventAggregator;
public ImmutableDictionary<string, JsonRpcClient> DaemonRpcClients;
public ImmutableDictionary<string, JsonRpcClient> WalletRpcClients;
public ConcurrentDictionary<string, ZanoSummary> Summaries { get; } = new();
public ConcurrentDictionary<string, LetheanSummary> Summaries { get; } = new();
public ZanoRpcProvider(ZanoConfiguration zanoConfiguration,
public LetheanRpcProvider(LetheanConfiguration letheanConfiguration,
EventAggregator eventAggregator,
IHttpClientFactory httpClientFactory)
{
_zanoConfiguration = zanoConfiguration;
_letheanConfiguration = letheanConfiguration;
_eventAggregator = eventAggregator;
DaemonRpcClients =
_zanoConfiguration.ZanoConfigurationItems.ToImmutableDictionary(pair => pair.Key,
_letheanConfiguration.LetheanConfigurationItems.ToImmutableDictionary(pair => pair.Key,
pair => new JsonRpcClient(pair.Value.DaemonRpcUri,
httpClientFactory.CreateClient($"{pair.Key}client")));
WalletRpcClients =
_zanoConfiguration.ZanoConfigurationItems.ToImmutableDictionary(pair => pair.Key,
_letheanConfiguration.LetheanConfigurationItems.ToImmutableDictionary(pair => pair.Key,
pair => new JsonRpcClient(pair.Value.InternalWalletRpcUri,
httpClientFactory.CreateClient($"{pair.Key}client")));
}
@ -42,13 +42,13 @@ namespace BTCPayServer.Plugins.Zano.Services
return Summaries.ContainsKey(cryptoCode) && IsAvailable(Summaries[cryptoCode]);
}
private bool IsAvailable(ZanoSummary summary)
private bool IsAvailable(LetheanSummary summary)
{
return summary.Synced &&
summary.WalletAvailable;
}
public async Task<ZanoSummary> UpdateSummary(string cryptoCode)
public async Task<LetheanSummary> UpdateSummary(string cryptoCode)
{
if (!DaemonRpcClients.TryGetValue(cryptoCode.ToUpperInvariant(), out var daemonRpcClient) ||
!WalletRpcClients.TryGetValue(cryptoCode.ToUpperInvariant(), out var walletRpcClient))
@ -56,7 +56,7 @@ namespace BTCPayServer.Plugins.Zano.Services
return null;
}
var summary = new ZanoSummary();
var summary = new LetheanSummary();
try
{
var daemonResult =
@ -93,19 +93,19 @@ namespace BTCPayServer.Plugins.Zano.Services
Summaries[cryptoCode] = summary;
if (changed)
{
_eventAggregator.Publish(new ZanoDaemonStateChange() { Summary = summary, CryptoCode = cryptoCode });
_eventAggregator.Publish(new LetheanDaemonStateChange() { Summary = summary, CryptoCode = cryptoCode });
}
return summary;
}
public class ZanoDaemonStateChange
public class LetheanDaemonStateChange
{
public string CryptoCode { get; set; }
public ZanoSummary Summary { get; set; }
public LetheanSummary Summary { get; set; }
}
public class ZanoSummary
public class LetheanSummary
{
public bool Synced { get; set; }
public long CurrentHeight { get; set; }

View file

@ -3,31 +3,31 @@ using System.Threading;
using System.Threading.Tasks;
using BTCPayServer.Logging;
using BTCPayServer.Plugins.Zano.Configuration;
using BTCPayServer.Plugins.Zano.RPC;
using BTCPayServer.Plugins.Lethean.Configuration;
using BTCPayServer.Plugins.Lethean.RPC;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace BTCPayServer.Plugins.Zano.Services
namespace BTCPayServer.Plugins.Lethean.Services
{
public class ZanoSummaryUpdaterHostedService : IHostedService
public class LetheanSummaryUpdaterHostedService : IHostedService
{
private readonly ZanoRpcProvider _zanoRpcProvider;
private readonly ZanoConfiguration _zanoConfiguration;
private readonly LetheanRpcProvider _letheanRpcProvider;
private readonly LetheanConfiguration _letheanConfiguration;
private readonly EventAggregator _eventAggregator;
public Logs Logs { get; }
private CancellationTokenSource _cts;
public ZanoSummaryUpdaterHostedService(ZanoRpcProvider zanoRpcProvider,
ZanoConfiguration zanoConfiguration,
public LetheanSummaryUpdaterHostedService(LetheanRpcProvider letheanRpcProvider,
LetheanConfiguration letheanConfiguration,
EventAggregator eventAggregator,
Logs logs)
{
_zanoRpcProvider = zanoRpcProvider;
_zanoConfiguration = zanoConfiguration;
_letheanRpcProvider = letheanRpcProvider;
_letheanConfiguration = letheanConfiguration;
_eventAggregator = eventAggregator;
Logs = logs;
}
@ -35,7 +35,7 @@ namespace BTCPayServer.Plugins.Zano.Services
public Task StartAsync(CancellationToken cancellationToken)
{
_cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
foreach (var configItem in _zanoConfiguration.ZanoConfigurationItems)
foreach (var configItem in _letheanConfiguration.LetheanConfigurationItems)
{
_ = StartSummaryLoop(_cts.Token, configItem.Key);
_ = StartPollingLoop(_cts.Token, configItem.Key);
@ -45,15 +45,15 @@ namespace BTCPayServer.Plugins.Zano.Services
private async Task StartSummaryLoop(CancellationToken cancellation, string cryptoCode)
{
Logs.PayServer.LogInformation("Starting Zano daemon summary updater ({CryptoCode})", cryptoCode);
Logs.PayServer.LogInformation("Starting Lethean daemon summary updater ({CryptoCode})", cryptoCode);
try
{
while (!cancellation.IsCancellationRequested)
{
try
{
await _zanoRpcProvider.UpdateSummary(cryptoCode);
if (_zanoRpcProvider.IsAvailable(cryptoCode))
await _letheanRpcProvider.UpdateSummary(cryptoCode);
if (_letheanRpcProvider.IsAvailable(cryptoCode))
{
await Task.Delay(TimeSpan.FromMinutes(1), cancellation);
}
@ -77,16 +77,16 @@ namespace BTCPayServer.Plugins.Zano.Services
private async Task StartPollingLoop(CancellationToken cancellation, string cryptoCode)
{
Logs.PayServer.LogInformation("Starting Zano payment polling loop ({CryptoCode})", cryptoCode);
Logs.PayServer.LogInformation("Starting Lethean payment polling loop ({CryptoCode})", cryptoCode);
try
{
while (!cancellation.IsCancellationRequested)
{
try
{
if (_zanoRpcProvider.IsAvailable(cryptoCode))
if (_letheanRpcProvider.IsAvailable(cryptoCode))
{
_eventAggregator.Publish(new ZanoPollEvent { CryptoCode = cryptoCode });
_eventAggregator.Publish(new LetheanPollEvent { CryptoCode = cryptoCode });
}
await Task.Delay(TimeSpan.FromSeconds(15), cancellation);
}

View file

@ -0,0 +1,47 @@
using System.Collections.Generic;
using System.Linq;
using BTCPayServer.Abstractions.Contracts;
using BTCPayServer.Client.Models;
using BTCPayServer.Payments;
namespace BTCPayServer.Plugins.Lethean.Services
{
public class LetheanSyncSummaryProvider : ISyncSummaryProvider
{
private readonly LetheanRpcProvider _letheanRpcProvider;
public LetheanSyncSummaryProvider(LetheanRpcProvider letheanRpcProvider)
{
_letheanRpcProvider = letheanRpcProvider;
}
public bool AllAvailable()
{
return _letheanRpcProvider.Summaries.All(pair => pair.Value.DaemonAvailable);
}
public string Partial { get; } = "/Views/Lethean/LetheanSyncSummary.cshtml";
public IEnumerable<ISyncStatus> GetStatuses()
{
return _letheanRpcProvider.Summaries.Select(pair => new LetheanSyncStatus()
{
Summary = pair.Value,
PaymentMethodId = PaymentMethodId.Parse(pair.Key).ToString()
});
}
}
public class LetheanSyncStatus : SyncStatus, ISyncStatus
{
public override bool Available
{
get
{
return Summary?.WalletAvailable ?? false;
}
}
public LetheanRpcProvider.LetheanSummary Summary { get; set; }
}
}

View file

@ -1,8 +1,8 @@
using System.Globalization;
namespace BTCPayServer.Plugins.Zano.Utils
namespace BTCPayServer.Plugins.Lethean.Utils
{
public static class ZanoMoney
public static class LetheanMoney
{
public static decimal Convert(long atomicUnits)
{
@ -12,9 +12,9 @@ namespace BTCPayServer.Plugins.Zano.Utils
return decimal.Parse(amt, CultureInfo.InvariantCulture);
}
public static long Convert(decimal zano)
public static long Convert(decimal lethean)
{
return System.Convert.ToInt64(zano * 1000000000000);
return System.Convert.ToInt64(lethean * 1000000000000);
}
}
}

View file

@ -2,9 +2,9 @@ using System;
using BTCPayServer.Payments;
namespace BTCPayServer.Plugins.Zano.ViewModels
namespace BTCPayServer.Plugins.Lethean.ViewModels
{
public class ZanoPaymentViewModel
public class LetheanPaymentViewModel
{
public PaymentMethodId PaymentMethodId { get; set; }
public string Confirmations { get; set; }

View file

@ -1,6 +1,6 @@
@using ZanoPaymentMethodViewModel = BTCPayServer.Plugins.Zano.Controllers.UIZanoStoreController.ZanoPaymentMethodViewModel
@using ZanoSettlementThresholdChoice = BTCPayServer.Plugins.Zano.Controllers.UIZanoStoreController.ZanoSettlementThresholdChoice;
@model ZanoPaymentMethodViewModel
@using LetheanPaymentMethodViewModel = BTCPayServer.Plugins.Lethean.Controllers.UILetheanStoreController.LetheanPaymentMethodViewModel
@using LetheanSettlementThresholdChoice = BTCPayServer.Plugins.Lethean.Controllers.UILetheanStoreController.LetheanSettlementThresholdChoice;
@model LetheanPaymentMethodViewModel
@{
ViewData.SetActivePage(Model.CryptoCode, StringLocalizer["{0} Settings", Model.CryptoCode], Model.CryptoCode);
@ -30,13 +30,13 @@
@if (Model.Summary?.WalletAvailable != true)
{
<div class="alert alert-info mt-4">
<p>To accept Zano payments, you need a Zano wallet running in RPC mode.</p>
<p>Start <code>simplewallet</code> with <code>--rpc-bind-port</code> and configure <code>BTCPAY_ZANO_WALLET_DAEMON_URI</code> to point to it.</p>
<p>To accept Lethean payments, you need a Lethean wallet running in RPC mode.</p>
<p>Start <code>simplewallet</code> with <code>--rpc-bind-port</code> and configure <code>BTCPAY_LTHN_WALLET_DAEMON_URI</code> to point to it.</p>
<p>For view-only monitoring, create a watch-only wallet using <code>simplewallet --save_watch_only</code> first.</p>
</div>
}
<form method="post" asp-action="GetStoreZanoPaymentMethod"
<form method="post" asp-action="GetStoreLetheanPaymentMethod"
asp-route-storeId="@Context.GetRouteValue("storeId")"
asp-route-cryptoCode="@Context.GetRouteValue("cryptoCode")"
class="mt-4">
@ -56,20 +56,20 @@
</a>
<select
asp-for="SettlementConfirmationThresholdChoice"
asp-items="Html.GetEnumSelectList<ZanoSettlementThresholdChoice>()"
asp-items="Html.GetEnumSelectList<LetheanSettlementThresholdChoice>()"
class="form-select w-auto"
onchange="
document.getElementById('unconfirmed-warning').hidden = this.value !== '@((int)ZanoSettlementThresholdChoice.ZeroConfirmation)';
document.getElementById('custom-confirmation-value').hidden = this.value !== '@((int)ZanoSettlementThresholdChoice.Custom)';">
document.getElementById('unconfirmed-warning').hidden = this.value !== '@((int)LetheanSettlementThresholdChoice.ZeroConfirmation)';
document.getElementById('custom-confirmation-value').hidden = this.value !== '@((int)LetheanSettlementThresholdChoice.Custom)';">
</select>
<span asp-validation-for="SettlementConfirmationThresholdChoice" class="text-danger"></span>
<p class="info-note my-3 text-warning" id="unconfirmed-warning" role="alert" hidden="@(Model.SettlementConfirmationThresholdChoice is not ZanoSettlementThresholdChoice.ZeroConfirmation)">
<p class="info-note my-3 text-warning" id="unconfirmed-warning" role="alert" hidden="@(Model.SettlementConfirmationThresholdChoice is not LetheanSettlementThresholdChoice.ZeroConfirmation)">
<vc:icon symbol="warning" />
<span text-translate="true">Choosing to accept an unconfirmed invoice can lead to double-spending and is strongly discouraged.</span>
</p>
</div>
<div class="form-group" id="custom-confirmation-value" hidden="@(Model.SettlementConfirmationThresholdChoice is not ZanoSettlementThresholdChoice.Custom)">
<div class="form-group" id="custom-confirmation-value" hidden="@(Model.SettlementConfirmationThresholdChoice is not LetheanSettlementThresholdChoice.Custom)">
<label asp-for="CustomSettlementConfirmationThreshold" class="form-label"></label>
<input
asp-for="CustomSettlementConfirmationThreshold"

View file

@ -1,10 +1,10 @@
@using BTCPayServer.Plugins.Zano.Services
@inject ZanoRpcProvider ZanoRpcProvider
@using BTCPayServer.Plugins.Lethean.Services
@inject LetheanRpcProvider LetheanRpcProvider
@inject SignInManager<ApplicationUser> SignInManager;
@if (SignInManager.IsSignedIn(User) && User.IsInRole(Roles.ServerAdmin) && ZanoRpcProvider.Summaries.Any())
@if (SignInManager.IsSignedIn(User) && User.IsInRole(Roles.ServerAdmin) && LetheanRpcProvider.Summaries.Any())
{
@foreach (var summary in ZanoRpcProvider.Summaries)
@foreach (var summary in LetheanRpcProvider.Summaries)
{
@if (summary.Value != null)
{

View file

@ -1,42 +1,42 @@
@using BTCPayServer.Plugins.Zano.Configuration
@using BTCPayServer.Plugins.Zano.Controllers
@using BTCPayServer.Plugins.Lethean.Configuration
@using BTCPayServer.Plugins.Lethean.Controllers
@using BTCPayServer.Abstractions.Contracts
@using BTCPayServer.Payments
@using BTCPayServer.Plugins.Zano.Payments
@using BTCPayServer.Plugins.Lethean.Payments
@using BTCPayServer.Services.Invoices
@inject SignInManager<ApplicationUser> SignInManager;
@inject ZanoConfiguration ZanoConfiguration;
@inject LetheanConfiguration LetheanConfiguration;
@inject IScopeProvider ScopeProvider
@inject PaymentMethodHandlerDictionary Handlers
@{
var storeId = ScopeProvider.GetCurrentStoreId();
}
@if (SignInManager.IsSignedIn(User) && User.IsInRole(Roles.ServerAdmin) && ZanoConfiguration.ZanoConfigurationItems.Any())
@if (SignInManager.IsSignedIn(User) && User.IsInRole(Roles.ServerAdmin) && LetheanConfiguration.LetheanConfigurationItems.Any())
{
var store = Context.GetStoreData();
var excludeFilters = store.GetStoreBlob().GetExcludedPaymentMethods();
foreach (var configItem in ZanoConfiguration.ZanoConfigurationItems)
foreach (var configItem in LetheanConfiguration.LetheanConfigurationItems)
{
var cryptoCode = configItem.Key;
var pmi = PaymentTypes.CHAIN.GetPaymentMethodId(cryptoCode);
var settings = store.GetPaymentMethodConfigs(Handlers)
.Where(s => s.Value is ZanoPaymentPromptDetails && s.Key == pmi)
.Where(s => s.Value is LetheanPaymentPromptDetails && s.Key == pmi)
.Select(s => s.Value)
.SingleOrDefault();
var enabled = settings != null && !excludeFilters.Match(pmi);
var isActive = !string.IsNullOrEmpty(storeId) && ViewContext.RouteData.Values.TryGetValue("Controller", out var controller) && controller is not null &&
nameof(UIZanoStoreController).StartsWith(controller.ToString() ?? string.Empty, StringComparison.InvariantCultureIgnoreCase) &&
nameof(UILetheanStoreController).StartsWith(controller.ToString() ?? string.Empty, StringComparison.InvariantCultureIgnoreCase) &&
ViewContext.RouteData.Values.TryGetValue("cryptoCode", out var routeCryptoCode) && routeCryptoCode is not null && routeCryptoCode.ToString() == cryptoCode;
<li class="nav-item">
<a class="nav-link @(isActive ? "active" : "")"
asp-route-cryptoCode="@cryptoCode"
asp-route-storeId="@storeId"
asp-action="GetStoreZanoPaymentMethod"
asp-controller="UIZanoStore">
asp-action="GetStoreLetheanPaymentMethod"
asp-controller="UILetheanStore">
<span class="me-2 btcpay-status btcpay-status--@(enabled ? "enabled" : "pending")"></span>
<span>@(cryptoCode == "ZANO" ? "Zano" : cryptoCode + " Wallet")</span>
<span>@(cryptoCode == "LTHN" ? "Lethean" : cryptoCode + " Wallet")</span>
</a>
</li>
}

View file

@ -1,7 +1,7 @@
@using System.Globalization
@using BTCPayServer.Plugins.Zano.Payments
@using BTCPayServer.Plugins.Zano.Services
@using BTCPayServer.Plugins.Zano.ViewModels
@using BTCPayServer.Plugins.Lethean.Payments
@using BTCPayServer.Plugins.Lethean.Services
@using BTCPayServer.Plugins.Lethean.ViewModels
@using BTCPayServer.Services
@using BTCPayServer.Services.Invoices
@inject DisplayFormatter DisplayFormatter
@ -12,15 +12,15 @@
@{
var payments = Model.Payments.Select(payment =>
{
if (!handlers.TryGetValue(payment.PaymentMethodId, out var h) || h is not ZanoPaymentMethodHandler handler)
if (!handlers.TryGetValue(payment.PaymentMethodId, out var h) || h is not LetheanPaymentMethodHandler handler)
return null;
var m = new ZanoPaymentViewModel();
var m = new LetheanPaymentViewModel();
var onChainPaymentData = handler.ParsePaymentDetails(payment.Details);
m.PaymentMethodId = handler.PaymentMethodId;
m.DepositAddress = payment.Destination;
m.Amount = payment.Value.ToString(CultureInfo.InvariantCulture);
var confReq = ZanoListener.ConfirmationsRequired(onChainPaymentData, payment.InvoiceEntity.SpeedPolicy);
var confReq = LetheanListener.ConfirmationsRequired(onChainPaymentData, payment.InvoiceEntity.SpeedPolicy);
var confCount = onChainPaymentData.ConfirmationCount;
confCount = Math.Min(confReq, confCount);
m.Confirmations = $"{confCount} / {confReq}";
@ -37,7 +37,7 @@
@if (payments.Any())
{
<section>
<h5>Zano Payments</h5>
<h5>Lethean Payments</h5>
<table class="table table-hover">
<thead>
<tr>

View file

@ -1,42 +1,42 @@
# Generate a Zano primary address and obtain its private view key using zano-wallet-cli
# Generate a Lethean primary address and obtain its private view key using lethean-wallet-cli
## Overview
Note: This tutorial includes instructions for Windows, macOS, and Linux. Skip any steps that do not apply to your operating system.
In this tutorial, you will generate a Zano primary address and retrieve its associated private view key. These are commonly used when setting up a store or service that needs to receive Zano payments and monitor incoming transactions.
In this tutorial, you will generate a Lethean primary address and retrieve its associated private view key. These are commonly used when setting up a store or service that needs to receive Lethean payments and monitor incoming transactions.
To accomplish this, we will use the Zano command-line wallet `zano-wallet-cli`. This tool is required to create the address and keys, and all wallet creation steps can be completed offline.
To accomplish this, we will use the Lethean command-line wallet `lethean-wallet-cli`. This tool is required to create the address and keys, and all wallet creation steps can be completed offline.
To begin, open a terminal window (Mac/Linux) or command prompt (Windows) and type the following commands:
```bash
# LINUX: Download the Linux 64-bit command line client and extract it
wget https://downloads.getzano.org/linux64
wget https://downloads.getlethean.org/linux64
tar -xvf linux64
cd zano-x86_64-linux-gnu-*
cd lethean-x86_64-linux-gnu-*
# MAC: Download the Mac command line client and extract it
wget https://downloads.getzano.org/mac64
wget https://downloads.getlethean.org/mac64
tar -xvf mac64
cd zano-x86_64-apple-*
cd lethean-x86_64-apple-*
# WINDOWS: Create a new folder with Windows File Explorer, and use your web browser to download the following file to the new folder
https://downloads.getzano.org/win64
https://downloads.getlethean.org/win64
# You are strongly advised to verify the hashes of the archive you downloaded:
# https://www.getzano.org/downloads/#cli
# https://www.getlethean.org/downloads/#cli
# WINDOWS: Double click the Zano zip file that has been downloaded to extract it. Then open Command Prompt. Use the 'cd' command to navigate to your new folder
cd zano-x86_64-w64-*
# WINDOWS: Double click the Lethean zip file that has been downloaded to extract it. Then open Command Prompt. Use the 'cd' command to navigate to your new folder
cd lethean-x86_64-w64-*
# Run the Zano command line wallet.
# Run the Lethean command line wallet.
# LINUX/MAC:
./zano-wallet-cli --offline
./lethean-wallet-cli --offline
# WINDOWS:
zano-wallet-cli.exe --offline
lethean-wallet-cli.exe --offline
# Follow the instructions displayed to create a new wallet. When told the 25 word SEED, write this down on paper and keep it in a very safe place. Even if you forget your passwords, the 25 word SEED can be used to recreate your wallet from any machine and have complete control over your Zano funds. Sample output from the Zano wallet is below:
# Follow the instructions displayed to create a new wallet. When told the 25 word SEED, write this down on paper and keep it in a very safe place. Even if you forget your passwords, the 25 word SEED can be used to recreate your wallet from any machine and have complete control over your Lethean funds. Sample output from the Lethean wallet is below:
# Important: The wallet address, seed phrase, and keys shown below were generated solely for this tutorial and contain no funds. Do not reuse these values.
@ -69,7 +69,7 @@ To start synchronizing with the daemon, use the "refresh" command.
Use the "help" command to see a simplified list of available commands.
Use "help all" command to see the list of all available commands.
Use "help <command>" to see a command's documentation.
Always use the "exit" command when closing zano-wallet-cli to save
Always use the "exit" command when closing lethean-wallet-cli to save
your current session's state. Otherwise, you might need to synchronize
your wallet again (your wallet keys are NOT at risk in any case).
@ -82,14 +82,14 @@ kiwi locker audio react inquest benches oyster present fountain
**********************************************************************
The daemon is not set up to background mine.
With background mining enabled, the daemon will mine when idle and not on battery.
Enabling this supports the network you are using, and makes you eligible for receiving new zano
Enabling this supports the network you are using, and makes you eligible for receiving new lethean
Do you want to do it now? (Y/Yes/N/No): : n
Background mining not enabled. Set setup-background-mining to 1 to change.
If you are new to Zano, type "welcome" for a brief overview.
If you are new to Lethean, type "welcome" for a brief overview.
Error: wallet failed to connect to daemon, because it is set to offline mode
Background refresh thread started
# Type "address" to see your public wallet address. You can give this address to anyone, and they will be able to send you Zano. However, NEVER give anyone your 25 word SEED.
# Type "address" to see your public wallet address. You can give this address to anyone, and they will be able to send you Lethean. However, NEVER give anyone your 25 word SEED.
[wallet 45zK2W]: address
0 45zK2WxTctfc7h6qFVwoN4eJH1Wcu9spwDk2cCuivssre9sCu7uVEEmCziCkYvGyDwHHM1KNyrbid7zvWZ5XKzmJ5yMPTcE Primary address
@ -102,4 +102,4 @@ secret: 8df34dd4b56bd13a69544d849bd0bce2a675bfc600d9e776ad801a6e9867580d
public: 1f562cd2ab81d247b6a5492b619d166157b30ca3d60ffe29d79ffd3fde1bbd2c
```
Congratulations! You have generated a Zano primary address along with its private view key, which can be used to create a view wallet and track incoming payments for your store.
Congratulations! You have generated a Lethean primary address along with its private view key, which can be used to create a view wallet and track incoming payments for your store.

View file

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View file

@ -1,17 +0,0 @@
{
"Identifier": "BTCPayServer.Plugins.Zano",
"Name": "Zano",
"Version": "1.0.0",
"Description": "Accept Zano (ZANO) payments in BTCPay Server. Privacy-focused cryptocurrency with confidential transactions.",
"SystemPlugin": false,
"Dependencies": [
{
"Identifier": "BTCPayServer",
"Condition": ">=2.1.0"
}
],
"Documentation": "https://github.com/hyle-team/btcpayserver-zano-plugin",
"Source": "https://github.com/hyle-team/btcpayserver-zano-plugin",
"Author": "Hyle Team",
"AuthorLink": "https://github.com/hyle-team"
}

View file

@ -1,17 +0,0 @@
using System;
using System.Collections.Generic;
namespace BTCPayServer.Plugins.Zano.Configuration
{
public class ZanoConfiguration
{
public Dictionary<string, ZanoConfigurationItem> ZanoConfigurationItems { get; set; } = [];
}
public class ZanoConfigurationItem
{
public Uri DaemonRpcUri { get; set; }
public Uri InternalWalletRpcUri { get; set; }
public string WalletDirectory { get; set; }
}
}

View file

@ -1,7 +0,0 @@
namespace BTCPayServer.Plugins.Zano.Payments
{
public class ZanoPaymentPromptDetails
{
public long? InvoiceSettledConfirmationThreshold { get; set; }
}
}

View file

@ -1,7 +0,0 @@
namespace BTCPayServer.Plugins.Zano.RPC
{
public class ZanoPollEvent
{
public string CryptoCode { get; set; }
}
}

View file

@ -1,47 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using BTCPayServer.Abstractions.Contracts;
using BTCPayServer.Client.Models;
using BTCPayServer.Payments;
namespace BTCPayServer.Plugins.Zano.Services
{
public class ZanoSyncSummaryProvider : ISyncSummaryProvider
{
private readonly ZanoRpcProvider _zanoRpcProvider;
public ZanoSyncSummaryProvider(ZanoRpcProvider zanoRpcProvider)
{
_zanoRpcProvider = zanoRpcProvider;
}
public bool AllAvailable()
{
return _zanoRpcProvider.Summaries.All(pair => pair.Value.DaemonAvailable);
}
public string Partial { get; } = "/Views/Zano/ZanoSyncSummary.cshtml";
public IEnumerable<ISyncStatus> GetStatuses()
{
return _zanoRpcProvider.Summaries.Select(pair => new ZanoSyncStatus()
{
Summary = pair.Value,
PaymentMethodId = PaymentMethodId.Parse(pair.Key).ToString()
});
}
}
public class ZanoSyncStatus : SyncStatus, ISyncStatus
{
public override bool Available
{
get
{
return Summary?.WalletAvailable ?? false;
}
}
public ZanoRpcProvider.ZanoSummary Summary { get; set; }
}
}

View file

@ -1,7 +0,0 @@
namespace BTCPayServer.Plugins.Zano;
public class ZanoSpecificBtcPayNetwork : BTCPayNetworkBase
{
public int MaxTrackedConfirmation = 10;
public string UriScheme { get; set; }
}

View file

@ -1,9 +1,9 @@
# Zano BTCPay Server Plugin
# Lethean BTCPay Server Plugin
Accept [Zano](https://zano.org) payments in BTCPay Server. Privacy-focused cryptocurrency with confidential transactions and integrated addresses.
Accept [Lethean](https://lethean.org) payments in BTCPay Server. Privacy-focused cryptocurrency with confidential transactions and integrated addresses.
> [!WARNING]
> This plugin shares a single Zano wallet across all stores in the BTCPay Server instance. Use this plugin only if you are not sharing your instance.
> This plugin shares a single Lethean wallet across all stores in the BTCPay Server instance. Use this plugin only if you are not sharing your instance.
## How it works
@ -13,27 +13,27 @@ When a customer creates an invoice, the plugin generates a unique **integrated a
| Environment variable | Required | Description | Example |
| --- | --- | --- | --- |
| `BTCPAY_ZANO_DAEMON_URI` | Yes | URI of the zanod RPC interface | `http://127.0.0.1:11211` |
| `BTCPAY_ZANO_WALLET_DAEMON_URI` | Yes | URI of the simplewallet RPC interface | `http://127.0.0.1:11212` |
| `BTCPAY_ZANO_WALLET_DAEMON_WALLETDIR` | No | Directory where wallet files are stored (for auto-loading on startup) | `/wallet` |
| `BTCPAY_LTHN_DAEMON_URI` | Yes | URI of the letheand RPC interface | `http://127.0.0.1:36941` |
| `BTCPAY_LTHN_WALLET_DAEMON_URI` | Yes | URI of the simplewallet RPC interface | `http://127.0.0.1:36944` |
| `BTCPAY_LTHN_WALLET_DAEMON_WALLETDIR` | No | Directory where wallet files are stored (for auto-loading on startup) | `/wallet` |
## Setup
### 1. Run Zano daemon
### 1. Run Lethean daemon
```bash
zanod --rpc-bind-ip=0.0.0.0 --rpc-bind-port=11211
letheand --rpc-bind-ip=0.0.0.0 --rpc-bind-port=36941
```
### 2. Run Zano wallet in RPC mode
### 2. Run Lethean wallet in RPC mode
Create or open a wallet, then start simplewallet with RPC enabled:
```bash
simplewallet --wallet-file /path/to/wallet \
--password "your-password" \
--daemon-address 127.0.0.1:11211 \
--rpc-bind-port 11212
--daemon-address 127.0.0.1:36941 \
--rpc-bind-port 36944
```
For receive-only setups, create a watch-only wallet first:
@ -46,37 +46,37 @@ save_watch_only /path/to/watch-only-wallet password
### 3. Install the plugin
In BTCPay Server, go to **Server Settings > Plugins** and install the Zano plugin. Then configure the environment variables above and restart.
In BTCPay Server, go to **Server Settings > Plugins** and install the Lethean plugin. Then configure the environment variables above and restart.
### 4. Enable Zano for your store
### 4. Enable Lethean for your store
Go to **Store Settings > Zano** to enable it and set your preferred confirmation threshold.
Go to **Store Settings > Lethean** to enable it and set your preferred confirmation threshold.
## Docker
A Docker image with zanod and simplewallet is available:
A Docker image with letheand and simplewallet is available:
```bash
docker pull pavelravaga/zano:2.2.0.455
docker pull letheanio/letheand:latest
```
Run the daemon:
```bash
docker run -d --name zanod \
-p 11211:11211 \
-v zano_data:/data \
pavelravaga/zano:2.2.0.455
docker run -d --name letheand \
-p 36941:36941 \
-v lethean_data:/data \
letheanio/letheand:latest
```
Run the wallet:
```bash
docker run -d --name zano_wallet \
-p 11212:11212 \
-v zano_wallet:/wallet \
docker run -d --name lethean_wallet \
-p 36944:36944 \
-v lethean_wallet:/wallet \
--entrypoint simplewallet \
pavelravaga/zano:2.2.0.455 \
--rpc-bind-ip=0.0.0.0 --rpc-bind-port=11212 \
--daemon-address=zanod:11211 \
letheanio/letheand:latest \
--rpc-bind-ip=0.0.0.0 --rpc-bind-port=36944 \
--daemon-address=letheand:36941 \
--wallet-file=/wallet/wallet --password=""
```
@ -91,9 +91,9 @@ docker run -d --name zano_wallet \
### Clone and build
```bash
git clone --recurse-submodules https://github.com/hyle-team/btcpayserver-zano-plugin
cd btcpayserver-zano-plugin
dotnet build btcpay-zano-plugin.sln
git clone --recurse-submodules https://github.com/lethean-io/btcpayserver-lethean-plugin
cd btcpayserver-lethean-plugin
dotnet build btcpay-lethean-plugin.sln
```
### Run unit tests
@ -121,9 +121,9 @@ Create `appsettings.dev.json` in `btcpayserver/BTCPayServer`:
```json
{
"DEBUG_PLUGINS": "../../Plugins/Zano/bin/Debug/net8.0/BTCPayServer.Plugins.Zano.dll",
"ZANO_DAEMON_URI": "http://127.0.0.1:11211",
"ZANO_WALLET_DAEMON_URI": "http://127.0.0.1:11212"
"DEBUG_PLUGINS": "../../Plugins/Lethean/bin/Debug/net8.0/BTCPayServer.Plugins.Lethean.dll",
"LTHN_DAEMON_URI": "http://127.0.0.1:36941",
"LTHN_WALLET_DAEMON_URI": "http://127.0.0.1:36944"
}
```
@ -133,9 +133,9 @@ Then run BTCPay Server with the plugin loaded.
- **Address generation**: Integrated addresses with random 8-byte payment IDs (unique per invoice)
- **Payment detection**: Polls `get_bulk_payments` every 15 seconds
- **Fee**: Fixed 0.01 ZANO per transaction
- **Fee**: Fixed 0.01 LTHN per transaction
- **Divisibility**: 12 decimal places
- **Rate source**: CoinGecko (`ZANO_BTC`)
- **Rate source**: CoinGecko (`LTHN_BTC`)
- **Confirmations**: Configurable (0, 1, 10, or custom)
## License

View file

@ -2,7 +2,7 @@
## Supported Versions
We currently support the following versions of the Zano plugin for BTCPayServer:
We currently support the following versions of the Lethean plugin for BTCPayServer:
| Version | Supported |
|---------|-------------|
@ -17,7 +17,7 @@ Please **do not** create public GitHub issues or pull requests for security-rela
Instead, report it privately by contacting repository admins:
- Matrix: [#btcpay-zano:matrix.org](https://matrix.to/#/#btcpay-zano:matrix.org)
- Matrix: [#btcpay-lethean:matrix.org](https://matrix.to/#/#btcpay-lethean:matrix.org)
Include as much information as possible:
- Vulnerability description

View file

@ -23,7 +23,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BTCPayServer.Tests", "submo
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Plugins", "Plugins", "{C9628212-0A00-4BF2-AF84-21797124579F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BTCPayServer.Plugins.Zano", "Plugins\Zano\BTCPayServer.Plugins.Zano.csproj", "{319C8C91-952F-4CF6-A251-058DFC66D70F}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BTCPayServer.Plugins.Lethean", "Plugins\Lethean\BTCPayServer.Plugins.Lethean.csproj", "{319C8C91-952F-4CF6-A251-058DFC66D70F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BTCPayServer.Plugins.IntegrationTests", "BTCPayServer.Plugins.IntegrationTests\BTCPayServer.Plugins.IntegrationTests.csproj", "{DA3599F0-C2DD-4323-A52F-DED5F86722ED}"
EndProject

View file

@ -6,11 +6,11 @@ RUN apt-get update && apt-get install -y \
pkg-config && \
rm -rf /var/lib/apt/lists/*
ARG ZANO_BRANCH=master
RUN git clone --depth 1 --branch ${ZANO_BRANCH} --recursive \
https://github.com/hyle-team/zano.git /zano
ARG LTHN_BRANCH=master
RUN git clone --depth 1 --branch ${LTHN_BRANCH} --recursive \
https://github.com/lethean-io/lethean.git /lethean
WORKDIR /zano/build
WORKDIR /lethean/build
RUN cmake .. -DCMAKE_BUILD_TYPE=Release && \
make -j$(nproc) daemon simplewallet
@ -25,14 +25,14 @@ RUN apt-get update && apt-get install -y \
libssl3 libunwind8 curl && \
rm -rf /var/lib/apt/lists/*
COPY --from=builder /zano/build/src/zanod /usr/local/bin/zanod
COPY --from=builder /zano/build/src/simplewallet /usr/local/bin/simplewallet
COPY --from=builder /lethean/build/src/letheand /usr/local/bin/letheand
COPY --from=builder /lethean/build/src/simplewallet /usr/local/bin/simplewallet
RUN mkdir -p /data /wallet
VOLUME ["/data", "/wallet"]
EXPOSE 11211 11212
EXPOSE 36941 36942
ENTRYPOINT ["zanod"]
CMD ["--rpc-bind-ip=0.0.0.0", "--rpc-bind-port=11211", "--data-dir=/data"]
ENTRYPOINT ["letheand"]
CMD ["--rpc-bind-ip=0.0.0.0", "--rpc-bind-port=36941", "--data-dir=/data"]

View file

@ -11,15 +11,15 @@ RUN apt-get update && apt-get install -y \
libssl3 libunwind8 curl && \
rm -rf /var/lib/apt/lists/*
COPY zanod /usr/local/bin/zanod
COPY letheand /usr/local/bin/letheand
COPY simplewallet /usr/local/bin/simplewallet
RUN chmod +x /usr/local/bin/zanod /usr/local/bin/simplewallet && \
RUN chmod +x /usr/local/bin/letheand /usr/local/bin/simplewallet && \
mkdir -p /data /wallet
VOLUME ["/data", "/wallet"]
EXPOSE 11211 11212
EXPOSE 36941 36942
ENTRYPOINT ["zanod"]
CMD ["--rpc-bind-ip=0.0.0.0", "--rpc-bind-port=11211", "--data-dir=/data"]
ENTRYPOINT ["letheand"]
CMD ["--rpc-bind-ip=0.0.0.0", "--rpc-bind-port=36941", "--data-dir=/data"]

View file

@ -1,21 +1,21 @@
# BTCPay Server Zano Plugin — Testing Guide
# BTCPay Server Lethean Plugin — Testing Guide
## What is this?
A BTCPay Server plugin that lets merchants accept Zano (ZANO) payments. When a customer pays an invoice, the plugin:
A BTCPay Server plugin that lets merchants accept Lethean (LTHN) payments. When a customer pays an invoice, the plugin:
1. Generates a unique **integrated address** (embeds a payment ID so each invoice gets its own address)
2. Shows a checkout page with QR code, address, and "Pay in wallet" button
3. Polls the Zano wallet every 15 seconds via `get_bulk_payments` RPC to detect incoming payments
3. Polls the Lethean wallet every 15 seconds via `get_bulk_payments` RPC to detect incoming payments
4. Tracks confirmations and settles the invoice based on the merchant's threshold setting
Built by forking the Monero BTCPay plugin and adapting it for Zano's RPC API.
Built by forking the Monero BTCPay plugin and adapting it for Lethean's RPC API.
## QA Instance
- **URL**: https://btcpay.zano.me
- **URL**: https://btcpay.lethean.me
- **Server**: obscura (157.180.1.140)
- **Daemon**: Zano testnet remote node (37.27.100.59:10505)
- **Daemon**: Lethean testnet remote node (37.27.100.59:10505)
- **Wallet**: freshly generated testnet wallet inside Docker
---
@ -24,15 +24,15 @@ Built by forking the Monero BTCPay plugin and adapting it for Zano's RPC API.
### Prerequisites
- A Zano testnet wallet with some balance (get testnet ZANO from https://faucet.testnet.zano.org)
- A Lethean testnet wallet with some balance (get testnet LTHN from https://faucet.testnet.lethean.org)
- A browser
### Test 1: Setup & Enable Zano
### Test 1: Setup & Enable Lethean
1. Go to https://btcpay.zano.me
1. Go to https://btcpay.lethean.me
2. Register a new admin account (first user becomes admin)
3. Create a store (any name)
4. In the left sidebar under **Wallets**, click **Zano**
4. In the left sidebar under **Wallets**, click **Lethean**
5. **Verify**: The settings page shows:
- Node available: True
- Wallet RPC available: True
@ -41,21 +41,21 @@ Built by forking the Monero BTCPay plugin and adapting it for Zano's RPC API.
7. Set "Consider the invoice settled when the payment transaction..." to **Zero Confirmation**
8. Click **Save**
**Expected**: Green success message, Zano stays enabled on page reload.
**Expected**: Green success message, Lethean stays enabled on page reload.
### Test 2: Create Invoice with Zano
### Test 2: Create Invoice with Lethean
1. Go to **Invoices** in the sidebar
2. Click **Create Invoice**
3. Set Amount: `1`, Currency: `ZANO` (USD won't work without a rate source configured)
3. Set Amount: `1`, Currency: `LTHN` (USD won't work without a rate source configured)
4. Click **Create**
5. Click the invoice link to open the checkout page
**Expected**:
- Shows amount: `1.010000000000 ZANO` (1 ZANO + 0.01 network fee)
- Shows an integrated address starting with `iZ`
- Shows amount: `1.010000000000 LTHN` (1 LTHN + 0.01 network fee)
- Shows an integrated address starting with `iTHN`
- QR code is displayed
- "Pay in wallet" button is present (opens `zano:` URI)
- "Pay in wallet" button is present (opens `lethean:` URI)
- "View Details" shows the full address and amount breakdown
### Test 3: Pay an Invoice
@ -66,7 +66,7 @@ Built by forking the Monero BTCPay plugin and adapting it for Zano's RPC API.
```
transfer <integrated_address> <amount_in_atomic_units> 10
```
For 1.01 ZANO: amount = 1010000000000 atomic units
For 1.01 LTHN: amount = 1010000000000 atomic units
4. Wait up to 30 seconds on the checkout page
**Expected**:
@ -76,7 +76,7 @@ Built by forking the Monero BTCPay plugin and adapting it for Zano's RPC API.
### Test 4: Confirmation Tracking
1. Set confirmation threshold to **At Least One** in Zano settings
1. Set confirmation threshold to **At Least One** in Lethean settings
2. Create and pay a new invoice
3. Watch the invoice status
@ -95,15 +95,15 @@ Built by forking the Monero BTCPay plugin and adapting it for Zano's RPC API.
### Test 6: Underpayment
1. Create an invoice for 2 ZANO
2. Send only 1 ZANO to the integrated address
1. Create an invoice for 2 LTHN
2. Send only 1 LTHN to the integrated address
**Expected**: Invoice remains in "New" or shows partial payment, doesn't settle.
### Test 7: Settings Persistence
1. Enable Zano, set Custom confirmation threshold to 5
2. Save, navigate away, come back to Zano settings
1. Enable Lethean, set Custom confirmation threshold to 5
2. Save, navigate away, come back to Lethean settings
**Expected**: Settings are preserved (Enabled, Custom, value 5).
@ -116,16 +116,16 @@ Built by forking the Monero BTCPay plugin and adapting it for Zano's RPC API.
- Node.js with `npx tsx` available
- Playwright installed (`npm install playwright` in `~/.claude/scripts/`)
- BTCPay Server running locally or accessible URL
- Zano wallet RPC accessible
- Lethean wallet RPC accessible
### Running the test
```bash
# Against local instance
npx tsx ~/.claude/scripts/btcpay-zano-e2e.ts
npx tsx ~/.claude/scripts/btcpay-lethean-e2e.ts
# Against QA instance (edit BASE_URL in the script first)
BASE_URL=https://btcpay.zano.me npx tsx ~/.claude/scripts/btcpay-zano-e2e.ts
BASE_URL=https://btcpay.lethean.me npx tsx ~/.claude/scripts/btcpay-lethean-e2e.ts
```
### What the script tests
@ -134,29 +134,29 @@ BASE_URL=https://btcpay.zano.me npx tsx ~/.claude/scripts/btcpay-zano-e2e.ts
|------|--------|-------------|
| 1 | Register admin account | Redirects to dashboard |
| 2 | Create store "Test Store" | Store appears in nav |
| 3 | Navigate to Zano settings | Shows Node/Wallet status |
| 4 | Enable Zano + Zero Confirmation | Settings saved successfully |
| 5 | Create 1 ZANO invoice | Invoice created, ID assigned |
| 6 | Open checkout page | Integrated address (iZ...) displayed, QR code rendered |
| 3 | Navigate to Lethean settings | Shows Node/Wallet status |
| 4 | Enable Lethean + Zero Confirmation | Settings saved successfully |
| 5 | Create 1 LTHN invoice | Invoice created, ID assigned |
| 6 | Open checkout page | Integrated address (iTHN...) displayed, QR code rendered |
| 7 | Attempt payment via wallet RPC | Transfer call made (may fail if wallet has no funds) |
| 8 | Check invoice status | Status page accessible |
### Screenshots
Saved to `/tmp/btcpay-zano-e2e/`:
Saved to `/tmp/btcpay-lethean-e2e/`:
- `01-landing.png` — Initial page
- `06-zano-settings.png` — Zano settings with node status
- `07-zano-settings-configured.png` — Enabled + threshold set
- `06-lethean-settings.png` — Lethean settings with node status
- `07-lethean-settings-configured.png` — Enabled + threshold set
- `12-checkout-page.png` — Checkout with QR code and address
- `14-invoice-details.png` — Invoice admin view
### Adapting the script
Key variables at the top of `btcpay-zano-e2e.ts`:
Key variables at the top of `btcpay-lethean-e2e.ts`:
```typescript
const BASE_URL = 'http://127.0.0.1:23002'; // BTCPay Server URL
const WALLET_RPC = 'http://127.0.0.1:11212/json_rpc'; // Zano wallet RPC
const WALLET_RPC = 'http://127.0.0.1:36944/json_rpc'; // Lethean wallet RPC
const EMAIL = 'admin@test.com';
const PASSWORD = 'Test1234!Test1234!';
```
@ -167,14 +167,14 @@ const PASSWORD = 'Test1234!Test1234!';
| Symptom | Likely cause |
|---------|-------------|
| Zano not in sidebar | Plugin not loaded — check BTCPay logs for "Plugins.Zano" |
| "Node available: False" | Daemon RPC unreachable — check BTCPAY_ZANO_DAEMON_URI |
| Lethean not in sidebar | Plugin not loaded — check BTCPay logs for "Plugins.Lethean" |
| "Node available: False" | Daemon RPC unreachable — check BTCPAY_LTHN_DAEMON_URI |
| "Wallet RPC available: False" | simplewallet not running or wrong port |
| "Synced: False" | Daemon still syncing — wait or use a synced remote node |
| Invoice shows no Zano option | Zano not enabled for this store |
| Invoice shows no Lethean option | Lethean not enabled for this store |
| Payment not detected | Check wallet is connected to same daemon, check get_bulk_payments response |
| Wrong amount displayed | Divisibility issue (should be 12 decimal places) |
| Address doesn't start with iZ | Integrated address generation failed — check make_integrated_address RPC |
| Address doesn't start with iTHN | Integrated address generation failed — check make_integrated_address RPC |
## Architecture

View file

@ -1,6 +1,6 @@
{
"sdk": {
"version": "8.0.416",
"version": "8.0.125",
"rollForward": "latestFeature"
}
}

View file

@ -14,7 +14,7 @@
<defs
id="defs291" />
<title
id="title282">btcpay-zano</title>
id="title282">btcpay-lethean</title>
<g
id="g1503"
transform="matrix(0.3368777,0,0,0.3368777,-0.08027797,0)">
@ -36,7 +36,7 @@
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:title>btcpay-zano</dc:title>
<dc:title>btcpay-lethean</dc:title>
</cc:Work>
</rdf:RDF>
</metadata>

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB