diff --git a/consensus/ethash/consensus.go b/consensus/ethash/consensus.go
index 19a0885..5c9e7c5 100644
--- a/consensus/ethash/consensus.go
+++ b/consensus/ethash/consensus.go
@@ -624,6 +624,10 @@ func (ethash *Ethash) FinalizeAndAssemble(chain consensus.ChainHeaderReader, hea
 	if len(withdrawals) > 0 {
 		return nil, errors.New("ethash does not support withdrawals")
 	}
+	// Pinch: refuse to assemble a block whose coinbase is below the stake threshold.
+	if err := mutations.PinchStakeCheck(chain.Config(), header, state); err != nil {
+		return nil, err
+	}
 	// Finalize block
 	ethash.Finalize(chain, header, state, txs, uncles, nil)
 
diff --git a/core/state_processor.go b/core/state_processor.go
index f712bbd..346dfb1 100644
--- a/core/state_processor.go
+++ b/core/state_processor.go
@@ -102,6 +102,10 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
 	if len(withdrawals) > 0 && !(p.config.IsEnabledByTime(p.config.GetEIP4895TransitionTime, &blockTime) || p.config.IsEnabled(p.config.GetEIP4895Transition, blockNumber)) {
 		return nil, nil, 0, fmt.Errorf("withdrawals before shanghai")
 	}
+	// Pinch: stake-gated mining (no-op on other chains).
+	if err := mutations.PinchStakeCheck(p.config, header, statedb); err != nil {
+		return nil, nil, 0, err
+	}
 	// Finalize the block, applying any consensus engine specific extras (e.g. block rewards)
 	p.engine.Finalize(p.bc, header, statedb, block.Transactions(), block.Uncles(), withdrawals)
 
diff --git a/eth/tracers/api_parity.go b/eth/tracers/api_parity.go
index 6142abc..d9599b2 100644
--- a/eth/tracers/api_parity.go
+++ b/eth/tracers/api_parity.go
@@ -27,6 +27,7 @@ import (
 	"github.com/ethereum/go-ethereum/internal/ethapi"
 	"github.com/ethereum/go-ethereum/params/mutations"
 	"github.com/ethereum/go-ethereum/rpc"
+	"github.com/holiman/uint256"
 )
 
 // TraceFilterArgs represents the arguments for a call.
@@ -140,6 +141,18 @@ func (api *TraceAPI) traceBlockReward(ctx context.Context, block *types.Block, c
 	chainConfig := api.debugAPI.backend.ChainConfig()
 	minerReward, _ := mutations.GetRewards(chainConfig, block.Header(), block.Uncles())
 
+	// Pinch (PINCH-STAKE-1): the credited reward depends on the coinbase tier;
+	// derive it from the block's final state.
+	if mutations.PinchStakeActive(chainConfig, block.Number()) {
+		statedb, release, err := api.debugAPI.backend.StateAtBlock(ctx, block, defaultTraceReexec, nil, true, false)
+		if err != nil {
+			return nil, err
+		}
+		r, _ := mutations.PinchMinedReward(chainConfig, block.Header(), statedb.GetBalance(block.Coinbase()))
+		release()
+		minerReward = r
+	}
+
 	coinbase := block.Coinbase()
 
 	tr := &ParityTrace{
@@ -161,6 +174,11 @@ func (api *TraceAPI) traceBlockReward(ctx context.Context, block *types.Block, c
 func (api *TraceAPI) traceBlockUncleRewards(ctx context.Context, block *types.Block, config *TraceConfig) ([]*ParityTrace, error) {
 	chainConfig := api.debugAPI.backend.ChainConfig()
 	_, uncleRewards := mutations.GetRewards(chainConfig, block.Header(), block.Uncles())
+	if mutations.PinchStakeActive(chainConfig, block.Number()) {
+		for i := range uncleRewards { // Pinch: no uncle rewards after activation
+			uncleRewards[i] = new(uint256.Int)
+		}
+	}
 
 	results := make([]*ParityTrace, len(uncleRewards))
 	for i, uncle := range block.Uncles() {
diff --git a/go.mod b/go.mod
index abca422..7375ec1 100644
--- a/go.mod
+++ b/go.mod
@@ -182,9 +182,3 @@ require (
 	google.golang.org/protobuf v1.33.0 // indirect
 	gopkg.in/yaml.v2 v2.4.0 // indirect
 )
-
-replace github.com/etclabscore/go-openrpc-reflect => github.com/fukuii-project/archive-reference-material v0.0.0-20220829134241-ba5a99fa846d
-
-replace github.com/etclabscore/go-jsonschema-walk => github.com/fukuii-project/archive-reference-material v0.0.0-20200501124244-44dea48ac8a4
-
-replace github.com/yusufpapurcu/wmi => github.com/fukuii-project/archive-reference-material v0.0.0-20240128142943-6c94d732ac31
diff --git a/go.sum b/go.sum
index 638052d..57d679e 100644
--- a/go.sum
+++ b/go.sum
@@ -147,6 +147,10 @@ github.com/edsrzf/mmap-go v1.2.0 h1:hXLYlkbaPzt1SaQk+anYwKSRNhufIDCchSPkUD6dD84=
 github.com/edsrzf/mmap-go v1.2.0/go.mod h1:19H/e8pUPLicwkyNgOykDXkJ9F0MHE+Z52B8EIth78Q=
 github.com/emicklei/dot v1.6.2 h1:08GN+DD79cy/tzN6uLCT84+2Wk9u+wvqP+Hkx/dIR8A=
 github.com/emicklei/dot v1.6.2/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s=
+github.com/etclabscore/go-jsonschema-walk v0.0.6 h1:DrNzoKWKd8f8XB5nFGBY00IcjakRE22OTI12k+2LkyY=
+github.com/etclabscore/go-jsonschema-walk v0.0.6/go.mod h1:VdfDY72AFAiUhy0ZXEaWSpveGjMT5JcDIm903NGqFwQ=
+github.com/etclabscore/go-openrpc-reflect v0.0.37 h1:IH0e7JqIvR9OhbbFWi/BHIkXrqbR3Zyia3RJ733eT6c=
+github.com/etclabscore/go-openrpc-reflect v0.0.37/go.mod h1:0404Ky3igAasAOpyj1eESjstTyneBAIk5PgJFbK4s5E=
 github.com/ethereum/c-kzg-4844 v0.4.0 h1:3MS1s4JtA868KpJxroZoepdV0ZKBp3u/O5HcZ7R3nlY=
 github.com/ethereum/c-kzg-4844 v0.4.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0=
 github.com/ethereum/evmc/v7 v7.5.0 h1:vwKcs1DINXqvaymranmlpO64fysJJkirCCJ+kkaPaCs=
@@ -436,6 +440,8 @@ github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBi
 github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
+github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
+github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
 go.mongodb.org/mongo-driver v1.17.7 h1:a9w+U3Vt67eYzcfq3k/OAv284/uUUkL0uP75VE5rCOU=
 go.mongodb.org/mongo-driver v1.17.7/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ=
 go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
diff --git a/params/mutations/pinch.go b/params/mutations/pinch.go
new file mode 100644
index 0000000..627bc8e
--- /dev/null
+++ b/params/mutations/pinch.go
@@ -0,0 +1,217 @@
+// Pinch stake-gated mining (PINCH-STAKE-1, revision 2).
+//
+// From the activation block onwards, on the Pinch networks only:
+//
+//  1. Threshold T(n) = min(25,000 PINCH, 0.25% of the scheduled supply
+//     mined up to block n). It starts near 2,500 PINCH at activation on
+//     mainnet and reaches 25,000 PINCH once 10,000,000 PINCH have been
+//     scheduled (around block 200,000). It depends only on the block number.
+//  2. A block is valid only if its coinbase holds at least T(n)
+//     (balance after the block's transactions, before block rewards).
+//  3. The block reward is the scheduled reward (50 PINCH, halving every
+//     2,100,000 blocks) multiplied by a tier fraction:
+//     >= 20·T -> 100%
+//     >=  8·T ->  75%
+//     >=  4·T ->  60%
+//     >=    T ->  50%
+//     The fraction never exceeds 100%. The unpaid part is never minted.
+//  4. Uncle rewards and the nephew bonus are not paid after activation, so a
+//     block never mints more than the scheduled reward. (Uncles are still
+//     valid and still count for difficulty.)
+//
+// All balances are read before any reward of the block is credited.
+package mutations
+
+import (
+	"errors"
+	"math/big"
+	"sort"
+
+	"github.com/ethereum/go-ethereum/core/state"
+	"github.com/ethereum/go-ethereum/core/types"
+	"github.com/ethereum/go-ethereum/params/types/ctypes"
+	"github.com/holiman/uint256"
+)
+
+var (
+	PinchMainnetChainID = big.NewInt(5261646)
+	PinchTestnetChainID = big.NewInt(5261647)
+
+	// Activation blocks. The testnet (retired public testnet / local tests)
+	// activates almost immediately so the rule can be exercised quickly.
+	PinchStakeForkMainnet uint64 = 20000
+	PinchStakeForkTestnet uint64 = 30
+
+	ErrPinchStakeTooLow = errors.New("pinch: coinbase balance below the PINCH mining threshold")
+)
+
+func pinchAmount(whole uint64) *uint256.Int {
+	return new(uint256.Int).Mul(uint256.NewInt(whole), uint256.NewInt(1_000_000_000_000_000_000))
+}
+
+// pinchTier: balance must be at least mult × threshold for num/den of the reward.
+type pinchTier struct {
+	mult     uint64
+	num, den uint64
+}
+
+// Ordered from highest to lowest.
+var pinchTiers = []pinchTier{
+	{20, 1, 1},
+	{8, 3, 4},
+	{4, 3, 5},
+	{1, 1, 2},
+}
+
+// PinchStakeCap is the highest value the mining threshold can reach.
+var PinchStakeCap = pinchAmount(25_000)
+
+// Threshold = scheduled supply × 25 / 10000 (0.25%), capped at PinchStakeCap.
+const (
+	pinchThresholdNum = 25
+	pinchThresholdDen = 10_000
+)
+
+// PinchScheduledSupply returns the sum of scheduled block rewards for blocks
+// 1..n (uncle rewards excluded), from the chain's block reward schedule.
+func PinchScheduledSupply(config ctypes.ChainConfigurator, n uint64) *uint256.Int {
+	total := new(uint256.Int)
+	if n == 0 {
+		return total
+	}
+	sched := config.GetEthashBlockRewardSchedule()
+	if len(sched) == 0 {
+		r := ctypes.EthashBlockReward(config, new(big.Int).SetUint64(n))
+		return total.Mul(r, uint256.NewInt(n))
+	}
+	keys := make([]uint64, 0, len(sched))
+	for k := range sched {
+		keys = append(keys, k)
+	}
+	sort.Slice(keys, func(i, j int) bool { return keys[i] < keys[j] })
+	for i, from := range keys {
+		if from > n {
+			break
+		}
+		lo := from
+		if lo < 1 {
+			lo = 1
+		}
+		hi := n
+		if i+1 < len(keys) && keys[i+1]-1 < hi {
+			hi = keys[i+1] - 1
+		}
+		if hi < lo {
+			continue
+		}
+		cnt := uint256.NewInt(hi - lo + 1)
+		total.Add(total, new(uint256.Int).Mul(sched[from], cnt))
+	}
+	return total
+}
+
+// PinchStakeThreshold returns the mining threshold T(n) for block n.
+func PinchStakeThreshold(config ctypes.ChainConfigurator, n *big.Int) *uint256.Int {
+	t := PinchScheduledSupply(config, n.Uint64())
+	t.Mul(t, uint256.NewInt(pinchThresholdNum))
+	t.Div(t, uint256.NewInt(pinchThresholdDen))
+	if t.Cmp(PinchStakeCap) > 0 {
+		t.Set(PinchStakeCap)
+	}
+	return t
+}
+
+func pinchForkBlock(config ctypes.ChainConfigurator) (uint64, bool) {
+	id := config.GetChainID()
+	if id == nil {
+		return 0, false
+	}
+	switch {
+	case id.Cmp(PinchMainnetChainID) == 0:
+		return PinchStakeForkMainnet, true
+	case id.Cmp(PinchTestnetChainID) == 0:
+		return PinchStakeForkTestnet, true
+	}
+	return 0, false
+}
+
+// PinchStakeActive reports whether the stake rule applies at block number n.
+func PinchStakeActive(config ctypes.ChainConfigurator, n *big.Int) bool {
+	fork, ok := pinchForkBlock(config)
+	if !ok || n == nil {
+		return false
+	}
+	return n.Cmp(new(big.Int).SetUint64(fork)) >= 0
+}
+
+// PinchStakeTier returns the reward fraction num/den for a balance at
+// threshold t. ok is false when the balance is below the threshold.
+func PinchStakeTier(t, balance *uint256.Int) (num, den uint64, ok bool) {
+	for _, tr := range pinchTiers {
+		if balance.Cmp(new(uint256.Int).Mul(t, uint256.NewInt(tr.mult))) >= 0 {
+			return tr.num, tr.den, true
+		}
+	}
+	return 0, 1, false
+}
+
+// PinchStakeCheck returns an error if the block's coinbase does not meet the
+// mining threshold. It must be called after the block's transactions have
+// been applied and before rewards are credited.
+func PinchStakeCheck(config ctypes.ChainConfigurator, header *types.Header, st *state.StateDB) error {
+	if !PinchStakeActive(config, header.Number) {
+		return nil
+	}
+	t := PinchStakeThreshold(config, header.Number)
+	if _, _, ok := PinchStakeTier(t, st.GetBalance(header.Coinbase)); !ok {
+		return ErrPinchStakeTooLow
+	}
+	return nil
+}
+
+func pinchScale(v *uint256.Int, num, den uint64, ok bool) *uint256.Int {
+	if !ok {
+		return new(uint256.Int)
+	}
+	r := new(uint256.Int).Mul(v, uint256.NewInt(num))
+	return r.Div(r, uint256.NewInt(den))
+}
+
+// pinchAdjustRewards replaces the Ethash rewards after activation:
+// the miner receives scheduled reward × tier fraction (no nephew bonus) and
+// uncles receive nothing. Per-block issuance therefore never exceeds the
+// scheduled reward, which keeps total supply within the 210M schedule
+// (plus the uncle rewards already minted before activation).
+func pinchAdjustRewards(config ctypes.ChainConfigurator, st *state.StateDB, header *types.Header, uncles []*types.Header) (*uint256.Int, []*uint256.Int) {
+	num, den, ok := PinchStakeTier(PinchStakeThreshold(config, header.Number), st.GetBalance(header.Coinbase))
+	miner := pinchScale(ctypes.EthashBlockReward(config, header.Number), num, den, ok)
+	out := make([]*uint256.Int, len(uncles))
+	for i := range out {
+		out[i] = new(uint256.Int)
+	}
+	return miner, out
+}
+
+// PinchMinedReward returns the block reward that was actually credited to the
+// coinbase of a post-activation block, given the coinbase balance in the
+// block's final state. Because rewards grow with the tier and tiers are
+// ordered, exactly one tier is consistent with the final balance.
+// ok is false when the rule is not active for this block.
+func PinchMinedReward(config ctypes.ChainConfigurator, header *types.Header, postBalance *uint256.Int) (*uint256.Int, bool) {
+	if !PinchStakeActive(config, header.Number) {
+		return nil, false
+	}
+	base := ctypes.EthashBlockReward(config, header.Number)
+	thr := PinchStakeThreshold(config, header.Number)
+	for _, t := range pinchTiers {
+		r := pinchScale(base, t.num, t.den, true)
+		if postBalance.Cmp(r) < 0 {
+			continue
+		}
+		pre := new(uint256.Int).Sub(postBalance, r)
+		if n, d, ok := PinchStakeTier(thr, pre); ok && n == t.num && d == t.den {
+			return r, true
+		}
+	}
+	return new(uint256.Int), true
+}
diff --git a/params/mutations/pinch_test.go b/params/mutations/pinch_test.go
new file mode 100644
index 0000000..dcb914b
--- /dev/null
+++ b/params/mutations/pinch_test.go
@@ -0,0 +1,255 @@
+package mutations
+
+import (
+	"encoding/json"
+	"math/big"
+	"testing"
+
+	"github.com/ethereum/go-ethereum/common"
+	"github.com/ethereum/go-ethereum/core/rawdb"
+	"github.com/ethereum/go-ethereum/core/state"
+	"github.com/ethereum/go-ethereum/core/types"
+	"github.com/ethereum/go-ethereum/params"
+	"github.com/ethereum/go-ethereum/params/types/coregeth"
+	"github.com/holiman/uint256"
+)
+
+func pinchTestConfig(t *testing.T, chainID int64) *coregeth.CoreGethChainConfig {
+	t.Helper()
+	raw := `{"networkId":` + big.NewInt(chainID).String() + `,"chainId":` + big.NewInt(chainID).String() + `,
+	 "eip155Block":0,"ethash":{},
+	 "blockReward":{"0x0":"0x2b5e3af16b1880000","0x200b20":"0x15af1d78b58c40000","0x401640":"0xad78ebc5ac620000"}}`
+	c := &coregeth.CoreGethChainConfig{}
+	if err := json.Unmarshal([]byte(raw), c); err != nil {
+		t.Fatalf("config: %v", err)
+	}
+	return c
+}
+
+func pinchState(t *testing.T) *state.StateDB {
+	t.Helper()
+	st, err := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+	return st
+}
+
+func pinch(n uint64) *uint256.Int { return pinchAmount(n) }
+
+func milli(n uint64) *uint256.Int { // n/1000 PINCH
+	return new(uint256.Int).Mul(uint256.NewInt(n), uint256.NewInt(1_000_000_000_000_000))
+}
+
+func TestPinchThreshold(t *testing.T) {
+	cfg := pinchTestConfig(t, 5261646)
+	cases := []struct {
+		n   uint64
+		sup *uint256.Int
+		thr *uint256.Int
+	}{
+		{0, pinch(0), pinch(0)},
+		{1, pinch(50), milli(125)},
+		{20_000, pinch(1_000_000), pinch(2_500)},
+		{100_000, pinch(5_000_000), pinch(12_500)},
+		{200_000, pinch(10_000_000), pinch(25_000)},
+		{300_000, pinch(15_000_000), pinch(25_000)},
+		{2_100_000, pinch(104_999_975), pinch(25_000)},
+	}
+	for _, c := range cases {
+		if got := PinchScheduledSupply(cfg, c.n); got.Cmp(c.sup) != 0 {
+			t.Errorf("supply(%d) = %s want %s", c.n, got, c.sup)
+		}
+		if got := PinchStakeThreshold(cfg, new(big.Int).SetUint64(c.n)); got.Cmp(c.thr) != 0 {
+			t.Errorf("threshold(%d) = %s want %s", c.n, got, c.thr)
+		}
+	}
+}
+
+func TestPinchTiers(t *testing.T) {
+	thr := pinch(2_500)
+	cases := []struct {
+		bal      *uint256.Int
+		num, den uint64
+		ok       bool
+	}{
+		{pinch(0), 0, 1, false},
+		{new(uint256.Int).Sub(pinch(2_500), uint256.NewInt(1)), 0, 1, false},
+		{pinch(2_500), 1, 2, true},
+		{pinch(9_999), 1, 2, true},
+		{pinch(10_000), 3, 5, true},
+		{pinch(20_000), 3, 4, true},
+		{pinch(49_999), 3, 4, true},
+		{pinch(50_000), 1, 1, true},
+		{pinch(9_000_000), 1, 1, true},
+	}
+	for i, c := range cases {
+		n, d, ok := PinchStakeTier(thr, c.bal)
+		if n != c.num || d != c.den || ok != c.ok {
+			t.Errorf("case %d: got %d/%d %v want %d/%d %v", i, n, d, ok, c.num, c.den, c.ok)
+		}
+	}
+}
+
+func TestPinchRewardsAndGate(t *testing.T) {
+	cfg := pinchTestConfig(t, 5261646)
+	fork := PinchStakeForkMainnet // threshold 2,500 PINCH at activation
+	miner := common.HexToAddress("0x1111111111111111111111111111111111111111")
+
+	type tc struct {
+		block   uint64
+		balance *uint256.Int
+		reward  *uint256.Int // credited to miner
+		gateErr bool
+	}
+	cases := []tc{
+		{fork - 1, pinch(0), pinch(50), false},      // before fork: old rules
+		{fork, pinch(0), pinch(0), true},            // below threshold: invalid
+		{fork, pinch(2_499), pinch(0), true},        // just below
+		{fork, pinch(2_500), pinch(25), false},      // 50%
+		{fork, pinch(10_000), pinch(30), false},     // 60%
+		{fork, pinch(20_000), milli(37_500), false}, // 75%
+		{fork, pinch(50_000), pinch(50), false},     // 100%
+		{fork, pinch(2_000_000), pinch(50), false},  // capped at 100%
+		{2_100_000, pinch(24_999), pinch(0), true},  // threshold capped at 25k
+		{2_100_000, pinch(25_000), milli(12_500), false},
+		{2_100_000, pinch(500_000), pinch(25), false}, // halving applies
+	}
+	for i, c := range cases {
+		st := pinchState(t)
+		st.AddBalance(miner, c.balance)
+		h := &types.Header{Number: new(big.Int).SetUint64(c.block), Coinbase: miner}
+		err := PinchStakeCheck(cfg, h, st)
+		if (err != nil) != c.gateErr {
+			t.Errorf("case %d: gate err=%v want %v", i, err, c.gateErr)
+		}
+		AccumulateRewards(cfg, st, h, nil)
+		got := new(uint256.Int).Sub(st.GetBalance(miner), c.balance)
+		if got.Cmp(c.reward) != 0 {
+			t.Errorf("case %d (block %d, bal %s): reward %s want %s", i, c.block, c.balance, got, c.reward)
+		}
+	}
+}
+
+func TestPinchUncles(t *testing.T) {
+	cfg := pinchTestConfig(t, 5261646)
+	fork := PinchStakeForkMainnet
+	miner := common.HexToAddress("0x1111111111111111111111111111111111111111")
+	richUncle := common.HexToAddress("0x2222222222222222222222222222222222222222")
+	poorUncle := common.HexToAddress("0x3333333333333333333333333333333333333333")
+	st := pinchState(t)
+	st.AddBalance(miner, pinch(500_000))
+	st.AddBalance(richUncle, pinch(100_000))
+	h := &types.Header{Number: new(big.Int).SetUint64(fork + 10), Coinbase: miner}
+	uncles := []*types.Header{
+		{Number: new(big.Int).SetUint64(fork + 9), Coinbase: richUncle},
+		{Number: new(big.Int).SetUint64(fork + 9), Coinbase: poorUncle},
+	}
+	AccumulateRewards(cfg, st, h, uncles)
+	// After activation: no uncle rewards, no nephew bonus.
+	if got := new(uint256.Int).Sub(st.GetBalance(richUncle), pinch(100_000)); !got.IsZero() {
+		t.Errorf("rich uncle reward %s want 0", got)
+	}
+	if got := st.GetBalance(poorUncle); !got.IsZero() {
+		t.Errorf("poor uncle reward %s want 0", got)
+	}
+	if got, want := new(uint256.Int).Sub(st.GetBalance(miner), pinch(500_000)), pinch(50); got.Cmp(want) != 0 {
+		t.Errorf("miner reward %s want %s", got, want)
+	}
+	// Before activation the classic Ethash uncle rewards still apply.
+	st2 := pinchState(t)
+	h2 := &types.Header{Number: new(big.Int).SetUint64(fork - 1), Coinbase: miner}
+	AccumulateRewards(cfg, st2, h2, []*types.Header{{Number: new(big.Int).SetUint64(fork - 2), Coinbase: poorUncle}})
+	if got, want := st2.GetBalance(poorUncle), milli(43_750); got.Cmp(want) != 0 {
+		t.Errorf("pre-fork uncle reward %s want %s", got, want)
+	}
+}
+
+// Per-block issuance after activation never exceeds the scheduled reward,
+// whatever the balances and uncles.
+func TestPinchIssuanceBound(t *testing.T) {
+	cfg := pinchTestConfig(t, 5261646)
+	fork := PinchStakeForkMainnet
+	bals := []uint64{0, 24_999, 25_000, 100_000, 200_000, 500_000, 10_000_000}
+	addrs := []common.Address{
+		common.HexToAddress("0xa1"), common.HexToAddress("0xa2"), common.HexToAddress("0xa3"),
+	}
+	for _, blk := range []uint64{fork, fork + 1, 2_100_000, 4_200_001} {
+		for _, bm := range bals {
+			for _, bu := range bals {
+				st := pinchState(t)
+				st.AddBalance(addrs[0], pinch(bm))
+				st.AddBalance(addrs[1], pinch(bu))
+				st.AddBalance(addrs[2], pinch(bu))
+				before := new(uint256.Int)
+				for _, a := range addrs {
+					before.Add(before, st.GetBalance(a))
+				}
+				h := &types.Header{Number: new(big.Int).SetUint64(blk), Coinbase: addrs[0]}
+				uncles := []*types.Header{
+					{Number: new(big.Int).SetUint64(blk - 1), Coinbase: addrs[1]},
+					{Number: new(big.Int).SetUint64(blk - 2), Coinbase: addrs[2]},
+				}
+				AccumulateRewards(cfg, st, h, uncles)
+				after := new(uint256.Int)
+				for _, a := range addrs {
+					after.Add(after, st.GetBalance(a))
+				}
+				minted := new(uint256.Int).Sub(after, before)
+				if limit := schedule(blk); minted.Cmp(limit) > 0 {
+					t.Fatalf("block %d miner %d uncles %d: minted %s > schedule %s", blk, bm, bu, minted, limit)
+				}
+			}
+		}
+	}
+}
+
+func schedule(blk uint64) *uint256.Int {
+	return new(uint256.Int).Rsh(pinch(50), uint(blk/2_100_000))
+}
+
+// PinchMinedReward recovers the credited reward from the final balance.
+func TestPinchMinedReward(t *testing.T) {
+	cfg := pinchTestConfig(t, 5261646)
+	fork := PinchStakeForkMainnet
+	miner := common.HexToAddress("0x1111111111111111111111111111111111111111")
+	pre := []*uint256.Int{
+		pinch(25_000), pinch(99_999), new(uint256.Int).Sub(pinch(100_000), uint256.NewInt(1)),
+		pinch(100_000), pinch(199_990), pinch(200_000), pinch(499_999), pinch(500_000), pinch(7_777_777),
+	}
+	for _, blk := range []uint64{fork, 2_100_000} {
+		for i, b := range pre {
+			st := pinchState(t)
+			st.AddBalance(miner, b)
+			h := &types.Header{Number: new(big.Int).SetUint64(blk), Coinbase: miner}
+			AccumulateRewards(cfg, st, h, nil)
+			want := new(uint256.Int).Sub(st.GetBalance(miner), b)
+			got, ok := PinchMinedReward(cfg, h, st.GetBalance(miner))
+			if !ok || got.Cmp(want) != 0 {
+				t.Errorf("block %d case %d: derived %v (ok=%v) want %s", blk, i, got, ok, want)
+			}
+		}
+	}
+	if _, ok := PinchMinedReward(cfg, &types.Header{Number: new(big.Int).SetUint64(fork - 1)}, pinch(1)); ok {
+		t.Error("derived reward before activation")
+	}
+}
+
+func TestPinchOtherChainsUnaffected(t *testing.T) {
+	cfg := params.ClassicChainConfig
+	miner := common.HexToAddress("0x1111111111111111111111111111111111111111")
+	st := pinchState(t)
+	h := &types.Header{Number: big.NewInt(int64(PinchStakeForkMainnet) + 5), Coinbase: miner}
+	if err := PinchStakeCheck(cfg, h, st); err != nil {
+		t.Fatalf("classic gated: %v", err)
+	}
+	if PinchStakeActive(cfg, h.Number) {
+		t.Fatal("stake active on classic")
+	}
+	if PinchStakeActive(pinchTestConfig(t, 5261647), big.NewInt(int64(PinchStakeForkTestnet)-1)) {
+		t.Fatal("testnet active before fork")
+	}
+	if !PinchStakeActive(pinchTestConfig(t, 5261647), big.NewInt(int64(PinchStakeForkTestnet))) {
+		t.Fatal("testnet inactive at fork")
+	}
+}
diff --git a/params/mutations/rewards.go b/params/mutations/rewards.go
index 9c7eef6..83abb6a 100644
--- a/params/mutations/rewards.go
+++ b/params/mutations/rewards.go
@@ -65,6 +65,9 @@ func GetRewards(config ctypes.ChainConfigurator, header *types.Header, uncles []
 // reward. The coinbase of each uncle block is also rewarded.
 func AccumulateRewards(config ctypes.ChainConfigurator, state *state.StateDB, header *types.Header, uncles []*types.Header) {
 	minerReward, uncleRewards := GetRewards(config, header, uncles)
+	if PinchStakeActive(config, header.Number) {
+		minerReward, uncleRewards = pinchAdjustRewards(config, state, header, uncles)
+	}
 	for i, uncle := range uncles {
 		state.AddBalance(uncle.Coinbase, uncleRewards[i])
 	}
