diff --git a/src/Nethermind/Chains/xdc-testnet.json b/src/Nethermind/Chains/xdc-testnet.json
index 4c1dbc14afa..f189a095b71 100644
--- a/src/Nethermind/Chains/xdc-testnet.json
+++ b/src/Nethermind/Chains/xdc-testnet.json
@@ -142,7 +142,7 @@
"masternodeVotingContract": "0x0000000000000000000000000000000000000088",
"blockSignerContract": "0x0000000000000000000000000000000000000089",
"randomizeSMCBinary": "0x0000000000000000000000000000000000000090",
- "XDCXAddrBinary": "0x0000000000000000000000000000000000000091",
+ "XDCXAddressBinary": "0x0000000000000000000000000000000000000091",
"tradingStateAddressBinary": "0x0000000000000000000000000000000000000092",
"XDCXLendingAddressBinary": "0x0000000000000000000000000000000000000093",
"XDCXLendingFinalizedTradeAddressBinary": "0x0000000000000000000000000000000000000094"
diff --git a/src/Nethermind/Nethermind.Xdc.Test/XdcChainSpecTests.cs b/src/Nethermind/Nethermind.Xdc.Test/XdcChainSpecTests.cs
new file mode 100644
index 00000000000..56a726fc4a0
--- /dev/null
+++ b/src/Nethermind/Nethermind.Xdc.Test/XdcChainSpecTests.cs
@@ -0,0 +1,68 @@
+// SPDX-FileCopyrightText: 2026 Demerzel Solutions Limited
+// SPDX-License-Identifier: LGPL-3.0-only
+
+using Nethermind.Core;
+using Nethermind.Logging;
+using Nethermind.Serialization.Json;
+using Nethermind.Specs.ChainSpecStyle;
+using Nethermind.Xdc.Spec;
+using NUnit.Framework;
+
+namespace Nethermind.Xdc.Test;
+
+///
+/// Pins what our chain specs produce for the XDCX special-transaction path on both networks.
+///
+[TestFixture, Parallelizable(ParallelScope.All)]
+public class XdcChainSpecTests
+{
+ [TestCase("xdc.json")]
+ [TestCase("xdc-testnet.json")]
+ public void System_contract_addresses_are_deserialized(string chainSpecFile)
+ {
+ XdcChainSpecEngineParameters engineParameters = EngineParameters(LoadChainSpec(chainSpecFile));
+
+ Assert.Multiple(() =>
+ {
+ Assert.That(engineParameters.MasternodeVotingContract, Is.EqualTo(new Address("0x0000000000000000000000000000000000000088")));
+ Assert.That(engineParameters.BlockSignerContract, Is.EqualTo(new Address("0x0000000000000000000000000000000000000089")));
+ Assert.That(engineParameters.RandomizeSMCBinary, Is.EqualTo(new Address("0x0000000000000000000000000000000000000090")));
+ Assert.That(engineParameters.XDCXAddressBinary, Is.EqualTo(new Address("0x0000000000000000000000000000000000000091")));
+ Assert.That(engineParameters.TradingStateAddressBinary, Is.EqualTo(new Address("0x0000000000000000000000000000000000000092")));
+ Assert.That(engineParameters.XDCXLendingAddressBinary, Is.EqualTo(new Address("0x0000000000000000000000000000000000000093")));
+ Assert.That(engineParameters.XDCXLendingFinalizedTradeAddressBinary, Is.EqualTo(new Address("0x0000000000000000000000000000000000000094")));
+ });
+ }
+
+ [TestCase("xdc.json")]
+ [TestCase("xdc-testnet.json")]
+ public void XDCX_flags_flip_on_their_own_blocks(string chainSpecFile)
+ {
+ ChainSpec chainSpec = LoadChainSpec(chainSpecFile);
+ XdcChainSpecEngineParameters engineParameters = EngineParameters(chainSpec);
+ XdcChainSpecBasedSpecProvider specProvider = new(chainSpec, engineParameters, LimboLogs.Instance);
+
+ ulong activation = engineParameters.TipXDCX!.Value;
+ ulong minerDisable = engineParameters.TIPXDCXMinerDisable!.Value;
+ ulong receiverDisable = engineParameters.TIPXDCXReceiverDisable!.Value;
+
+ Assert.Multiple(() =>
+ {
+ Assert.That(specProvider.GetXdcSpec(activation - 1).IsTIPXDCXMiner, Is.False);
+ Assert.That(specProvider.GetXdcSpec(activation).IsTIPXDCXMiner, Is.True);
+ Assert.That(specProvider.GetXdcSpec(minerDisable - 1).IsTIPXDCXMiner, Is.True);
+ Assert.That(specProvider.GetXdcSpec(minerDisable).IsTIPXDCXMiner, Is.False);
+
+ Assert.That(specProvider.GetXdcSpec(activation - 1).IsTIPXDCXReceiver, Is.False);
+ Assert.That(specProvider.GetXdcSpec(activation).IsTIPXDCXReceiver, Is.True);
+ Assert.That(specProvider.GetXdcSpec(receiverDisable - 1).IsTIPXDCXReceiver, Is.True);
+ Assert.That(specProvider.GetXdcSpec(receiverDisable).IsTIPXDCXReceiver, Is.False);
+ });
+ }
+
+ private static ChainSpec LoadChainSpec(string chainSpecFile) =>
+ new ChainSpecFileLoader(new EthereumJsonSerializer(), LimboLogs.Instance).LoadEmbeddedOrFromFile(chainSpecFile);
+
+ private static XdcChainSpecEngineParameters EngineParameters(ChainSpec chainSpec) =>
+ chainSpec.EngineChainSpecParametersProvider.GetChainSpecParameters();
+}
diff --git a/src/Nethermind/Nethermind.Xdc.Test/XdcForkIdConformanceTests.cs b/src/Nethermind/Nethermind.Xdc.Test/XdcForkIdConformanceTests.cs
index a3f03ed1bc7..25133c1d3c0 100644
--- a/src/Nethermind/Nethermind.Xdc.Test/XdcForkIdConformanceTests.cs
+++ b/src/Nethermind/Nethermind.Xdc.Test/XdcForkIdConformanceTests.cs
@@ -1,7 +1,6 @@
// SPDX-FileCopyrightText: 2026 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only
-using System.IO;
using Nethermind.Core;
using Nethermind.Core.Crypto;
using Nethermind.Core.Test.Builders;
@@ -65,8 +64,7 @@ private static void AssertForkId(string chainSpecFile, Hash256 genesisHash, ulon
private static XdcForkInfo ForkInfo(string chainSpecFile, Hash256 genesisHash)
{
- string path = Path.Combine(TestContext.CurrentContext.WorkDirectory, "../../../../", "Chains", chainSpecFile);
- ChainSpec chainSpec = new ChainSpecFileLoader(new EthereumJsonSerializer(), LimboLogs.Instance).LoadEmbeddedOrFromFile(path);
+ ChainSpec chainSpec = new ChainSpecFileLoader(new EthereumJsonSerializer(), LimboLogs.Instance).LoadEmbeddedOrFromFile(chainSpecFile);
XdcChainSpecEngineParameters engineParameters =
chainSpec.EngineChainSpecParametersProvider.GetChainSpecParameters();
diff --git a/src/Nethermind/Nethermind.Xdc/Spec/XdcChainSpecEngineParameters.cs b/src/Nethermind/Nethermind.Xdc/Spec/XdcChainSpecEngineParameters.cs
index c5e86ce654b..b534ac1f9f0 100644
--- a/src/Nethermind/Nethermind.Xdc/Spec/XdcChainSpecEngineParameters.cs
+++ b/src/Nethermind/Nethermind.Xdc/Spec/XdcChainSpecEngineParameters.cs
@@ -109,6 +109,12 @@ public void AddTransitions(SortedSet blockNumbers, SortedSet times
// Without its own release spec boundary the flag would only flip on whichever transition encloses it.
if (DynamicGasLimitBlock is not null)
blockNumbers.Add(DynamicGasLimitBlock.Value);
+ if (TipXDCX is not null)
+ blockNumbers.Add(TipXDCX.Value);
+ if (TIPXDCXMinerDisable is not null)
+ blockNumbers.Add(TIPXDCXMinerDisable.Value);
+ if (TIPXDCXReceiverDisable is not null)
+ blockNumbers.Add(TIPXDCXReceiverDisable.Value);
}
}