diff --git a/src/chainparams.cpp b/src/chainparams.cpp
index 9562a39..b2868e5 100644
--- a/src/chainparams.cpp
+++ b/src/chainparams.cpp
@@ -80,6 +80,8 @@ public:
         consensus.nPowTargetTimespan =  12 * 60;
         consensus.nPowTargetSpacing = 3 * 60;
         consensus.nPowTargetTimespanV2 = 40 * 3 * 60; // 40 blocks, ~2 hours
+        consensus.nLwmaHeight = 999999999; // mainnet: placeholder until the activation block is announced
+        consensus.nLwmaAveragingWindow = 60;
         consensus.nDifficultyAdjustmentForkHeight = 275520;
         consensus.fPowAllowMinDifficultyBlocks = false;
         consensus.fPowNoRetargeting = false;
@@ -202,6 +204,8 @@ public:
         consensus.nPowTargetTimespan =  12 * 60;
         consensus.nPowTargetSpacing = 3 * 60;
         consensus.nPowTargetTimespanV2 = 40 * 3 * 60;
+        consensus.nLwmaHeight = 0; // testnet: active from genesis
+        consensus.nLwmaAveragingWindow = 60;
         consensus.nDifficultyAdjustmentForkHeight = 0; // always use V2 on testnet
         consensus.fPowAllowMinDifficultyBlocks = true;
         consensus.fPowNoRetargeting = false;
@@ -307,6 +311,8 @@ public:
         consensus.nPowTargetTimespan =  12 * 60;
         consensus.nPowTargetSpacing = 3 * 60;
         consensus.nPowTargetTimespanV2 = 40 * 3 * 60;
+        consensus.nLwmaHeight = 150; // regtest: after the usual 101-block warm-up
+        consensus.nLwmaAveragingWindow = 60;
         consensus.nDifficultyAdjustmentForkHeight = 0; // disabled on regtest
         consensus.fPowAllowMinDifficultyBlocks = true;
         consensus.fPowNoRetargeting = true;
diff --git a/src/consensus/params.h b/src/consensus/params.h
index 956cda6..fa8bb1a 100644
--- a/src/consensus/params.h
+++ b/src/consensus/params.h
@@ -90,6 +90,10 @@ struct Params {
     int64_t nPowTargetTimespan;
     int64_t nPowTargetTimespanV2;
     int nDifficultyAdjustmentForkHeight;
+    /** LWMA per-block difficulty: first height that uses it, and its averaging window */
+    int nLwmaHeight;
+    int64_t nLwmaAveragingWindow;
+    bool IsLwmaActive(int nHeight) const { return nHeight >= nLwmaHeight; }
     int64_t DifficultyAdjustmentInterval() const { return nPowTargetTimespan / nPowTargetSpacing; }
     int64_t PowTargetTimespan(int nHeight) const {
         if (nDifficultyAdjustmentForkHeight > 0 && nHeight >= nDifficultyAdjustmentForkHeight)
diff --git a/src/pow.cpp b/src/pow.cpp
index 54d97af..19990b0 100644
--- a/src/pow.cpp
+++ b/src/pow.cpp
@@ -6,18 +6,61 @@
 #include "auxpow.h"
 #include <pow.h>
 
+#include <algorithm>
 #include <arith_uint256.h>
 #include <chain.h>
 #include <primitives/block.h>
 #include <uint256.h>
 #include <util/system.h>
 
+// LWMA-1 (zawy12): linearly weighted moving average of the last N blocks,
+// recalculated every block. Recent blocks count more, so difficulty follows
+// real hashrate within a few blocks instead of jumping every 40.
+static unsigned int Lwma1CalculateNextWorkRequired(const CBlockIndex* pindexLast, const Consensus::Params& params)
+{
+    const int64_t T = params.nPowTargetSpacing;
+    const int64_t N = params.nLwmaAveragingWindow;
+    const int64_t k = N * (N + 1) * T / 2;
+    const int height = pindexLast->nHeight;
+    const arith_uint256 powLimit = UintToArith256(params.powLimit);
+
+    if (height < N) return powLimit.GetCompact();
+
+    arith_uint256 sumTarget;
+    int64_t t = 0;
+    int64_t j = 0;
+    int64_t previousTimestamp = pindexLast->GetAncestor(height - N)->GetBlockTime();
+
+    for (int i = height - N + 1; i <= height; i++) {
+        const CBlockIndex* block = pindexLast->GetAncestor(i);
+        // Timestamps must move forward; a block claiming an earlier time counts as 1 second.
+        const int64_t thisTimestamp = (block->GetBlockTime() > previousTimestamp) ? block->GetBlockTime() : previousTimestamp + 1;
+        // Cap any single solve time at 6x target so one fake timestamp cannot crash difficulty.
+        const int64_t solvetime = std::min(6 * T, thisTimestamp - previousTimestamp);
+        previousTimestamp = thisTimestamp;
+        j++;
+        t += solvetime * j;
+        arith_uint256 target;
+        target.SetCompact(block->nBits);
+        sumTarget += target / (k * N);
+    }
+
+    arith_uint256 nextTarget = sumTarget * t;
+    if (nextTarget > powLimit) nextTarget = powLimit;
+    return nextTarget.GetCompact();
+}
+
 unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHeader *pblock, const Consensus::Params& params)
 {
     assert(pindexLast != nullptr);
     unsigned int nProofOfWorkLimit = UintToArith256(params.powLimit).GetCompact();
 
     int nHeight = pindexLast->nHeight + 1;
+
+    // From nLwmaHeight on, difficulty adjusts every block (LWMA-1).
+    // Before it, the original 40-block rule below is untouched.
+    if (params.IsLwmaActive(nHeight))
+        return Lwma1CalculateNextWorkRequired(pindexLast, params);
     int64_t nInterval = params.DifficultyAdjustmentIntervalForHeight(nHeight);
 
     // Only change once per difficulty adjustment interval
