Repository: nspcc-dev/neo-go
Pull request #3567 (2024-08-30 18:49:53)

Go 1.22


Patch:
diff --git a/README.md b/README.md
index 3b6f0668c..4c3267e59 100644
--- a/README.md
+++ b/README.md
@@ -51,7 +51,7 @@ NeoGo, `:latest` points to the latest release) or build yourself.
 
 ### Building
 
-Building NeoGo requires Go 1.21+ and `make`:
+Building NeoGo requires Go 1.22+ and `make`:
 
 ```
 make
diff --git a/cli/query/query.go b/cli/query/query.go
index 424b473b5..b8456e9b8 100644
--- a/cli/query/query.go
+++ b/cli/query/query.go
@@ -200,11 +200,10 @@ func queryCandidates(ctx *cli.Context) error {
 		if !a.Active && b.Active {
 			return -1
 		}
-		res := cmp.Compare(a.Votes, b.Votes)
-		if res != 0 {
-			return res
-		}
-		return a.PublicKey.Cmp(&b.PublicKey)
+		return cmp.Or(
+			cmp.Compare(a.Votes, b.Votes),
+			a.PublicKey.Cmp(&b.PublicKey),
+		)
 	})
 	var res []byte
 	res = fmt.Appendf(res, "Key\tVotes\tCommittee\tConsensus\n")
diff --git a/cli/smartcontract/smart_contract.go b/cli/smartcontract/smart_contract.go
index 8489c31b5..bc6306e0a 100644
--- a/cli/smartcontract/smart_contract.go
+++ b/cli/smartcontract/smart_contract.go
@@ -417,7 +417,7 @@ func initSmartContract(ctx *cli.Context) error {
 
 	gm := []byte("module " + contractName + `
 
-go 1.21
+go 1.22
 
 require (
 	github.com/nspcc-dev/neo-go/pkg/interop ` + ver + `
diff --git a/cli/wallet/nep17.go b/cli/wallet/nep17.go
index 462069b8b..efbb5eadb 100644
--- a/cli/wallet/nep17.go
+++ b/cli/wallet/nep17.go
@@ -362,7 +362,7 @@ func getMatchingTokenRPC(ctx *cli.Context, c *rpcclient.Client, addr util.Uint16
 func getMatchingTokenAux(ctx *cli.Context, get func(i int) *wallet.Token, n int, name string, standard string) (*wallet.Token, error) {
 	var token *wallet.Token
 	var count int
-	for i := 0; i < n; i++ {
+	for i := range n {
 		t := get(i)
 		if t != nil && (t.Hash.StringLE() == name || t.Address() == name || t.Symbol == name || t.Name == name) && t.Standard == standard {
 			if count == 1 {
@@ -540,7 +540,7 @@ func multiTransferNEP17(ctx *cli.Context) error {
 		recipients      []transferTarget
 		cosignersSepPos = ctx.NArg() // `--` position.
 	)
-	for i := 0; i < ctx.NArg(); i++ {
+	for i := range ctx.NArg() {
 		arg := ctx.Args().Get(i)
 		if arg == cmdargs.CosignersSeparator {
 			cosignersSepPos = i
@@ -561,7 +561,7 @@ func multiTransferNEP17(ctx *cli.Context) error {
 	}
 
 	cache := make(map[string]*wallet.Token)
-	for i := 0; i < cosignersSepPos; i++ {
+	for i := range cosignersSepPos {
 		arg := ctx.Args().Get(i)
 		ss := strings.SplitN(arg, ":", 3)
 		if len(ss) != 3 {
diff --git a/examples/engine/go.mod b/examples/engine/go.mod
index 12283f847..9ec3d5190 100644
--- a/examples/engine/go.mod
+++ b/examples/engine/go.mod
@@ -1,5 +1,5 @@
 module github.com/nspcc-dev/neo-go/examples/engine
 
-go 1.21
+go 1.22
 
 require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240727093519-1a48f1ce43ec
diff --git a/examples/events/go.mod b/examples/events/go.mod
index f9aaf9d18..d912304ad 100644
--- a/examples/events/go.mod
+++ b/examples/events/go.mod
@@ -1,5 +1,5 @@
 module github.com/nspcc-dev/neo-go/examples/events
 
-go 1.21
+go 1.22
 
 require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240727093519-1a48f1ce43ec
diff --git a/examples/iterator/go.mod b/examples/iterator/go.mod
index 622f38c53..75d9f5462 100644
--- a/examples/iterator/go.mod
+++ b/examples/iterator/go.mod
@@ -1,5 +1,5 @@
 module github.com/nspcc-dev/neo-go/examples/iterator
 
-go 1.21
+go 1.22
 
 require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240727093519-1a48f1ce43ec
diff --git a/examples/nft-d/go.mod b/examples/nft-d/go.mod
index d9d8b6507..7bb0eff59 100644
--- a/examples/nft-d/go.mod
+++ b/examples/nft-d/go.mod
@@ -1,5 +1,5 @@
 module github.com/nspcc-dev/neo-go/examples/nft
 
-go 1.21
+go 1.22
 
 require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240727093519-1a48f1ce43ec
diff --git a/examples/nft-nd-nns/go.mod b/examples/nft-nd-nns/go.mod
index 3f95e8566..01dd8fad8 100644
--- a/examples/nft-nd-nns/go.mod
+++ b/examples/nft-nd-nns/go.mod
@@ -1,6 +1,6 @@
 module github.com/nspcc-dev/neo-go/examples/nft-nd-nns
 
-go 1.21
+go 1.22
 
 require (
 	github.com/nspcc-dev/neo-go v0.102.1-0.20231020181554-d89c8801d689
diff --git a/examples/nft-nd/go.mod b/examples/nft-nd/go.mod
index da95393ae..0c8f812b6 100644
--- a/examples/nft-nd/go.mod
+++ b/examples/nft-nd/go.mod
@@ -1,5 +1,5 @@
 module github.com/nspcc-dev/neo-go/examples/nft-nd
 
-go 1.21
+go 1.22
 
 require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240727093519-1a48f1ce43ec
diff --git a/examples/oracle/go.mod b/examples/oracle/go.mod
index 1f719a097..156d900b6 100644
--- a/examples/oracle/go.mod
+++ b/examples/oracle/go.mod
@@ -1,5 +1,5 @@
 module github.com/nspcc-dev/neo-go/examples/oracle
 
-go 1.21
+go 1.22
 
 require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240727093519-1a48f1ce43ec
diff --git a/examples/runtime/go.mod b/examples/runtime/go.mod
index 3b90e9622..6f47d8f44 100644
--- a/examples/runtime/go.mod
+++ b/examples/runtime/go.mod
@@ -1,5 +1,5 @@
 module github.com/nspcc-dev/neo-go/examples/runtime
 
-go 1.21
+go 1.22
 
 require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240727093519-1a48f1ce43ec
diff --git a/examples/storage/go.mod b/examples/storage/go.mod
index 99851cce0..5a27687f6 100644
--- a/examples/storage/go.mod
+++ b/examples/storage/go.mod
@@ -1,5 +1,5 @@
 module github.com/nspcc-dev/neo-go/examples/storage
 
-go 1.21
+go 1.22
 
 require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240727093519-1a48f1ce43ec
diff --git a/examples/timer/go.mod b/examples/timer/go.mod
index 1e7a09d7b..e6540b0dd 100644
--- a/examples/timer/go.mod
+++ b/examples/timer/go.mod
@@ -1,5 +1,5 @@
 module github.com/nspcc-dev/neo-go/examples/timer
 
-go 1.21
+go 1.22
 
 require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240727093519-1a48f1ce43ec
diff --git a/examples/token/go.mod b/examples/token/go.mod
index 6e86fae28..c8c1a0ba4 100644
--- a/examples/token/go.mod
+++ b/examples/token/go.mod
@@ -1,5 +1,5 @@
 module github.com/nspcc-dev/neo-go/examples/token
 
-go 1.21
+go 1.22
 
 require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240727093519-1a48f1ce43ec
diff --git a/examples/zkp/cubic_circuit/go.mod b/examples/zkp/cubic_circuit/go.mod
index 5ca7861c9..a690447ea 100644
--- a/examples/zkp/cubic_circuit/go.mod
+++ b/examples/zkp/cubic_circuit/go.mod
@@ -1,6 +1,6 @@
 module github.com/nspcc-dev/neo-go/examples/zkp/cubic
 
-go 1.21
+go 1.22
 
 require (
 	github.com/consensys/gnark v0.10.0
diff --git a/examples/zkp/xor_compat/go.mod b/examples/zkp/xor_compat/go.mod
index 29b882dec..e952a7978 100644
--- a/examples/zkp/xor_compat/go.mod
+++ b/examples/zkp/xor_compat/go.mod
@@ -1,5 +1,5 @@
 module github.com/nspcc-dev/neo-go/examples/zkp/xor
 
-go 1.21
+go 1.22
 
 require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240727093519-1a48f1ce43ec
diff --git a/go.mod b/go.mod
index 435b33068..7c9045334 100644
--- a/go.mod
+++ b/go.mod
@@ -1,6 +1,6 @@
 module github.com/nspcc-dev/neo-go
 
-go 1.21
+go 1.22
 
 require (
 	github.com/chzyer/readline v1.5.1
diff --git a/internal/contracts/oracle_contract/go.mod b/internal/contracts/oracle_contract/go.mod
index 8cd5d6312..86aa74a09 100644
--- a/internal/contracts/oracle_contract/go.mod
+++ b/internal/contracts/oracle_contract/go.mod
@@ -1,5 +1,5 @@
 module github.com/nspcc-dev/neo-go/internal/examples/oracle
 
-go 1.21
+go 1.22
 
 require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240727093519-1a48f1ce43ec
diff --git a/internal/random/random_util.go b/internal/random/random_util.go
index 1928d73ab..0b7981a33 100644
--- a/internal/random/random_util.go
+++ b/internal/random/random_util.go
@@ -1,8 +1,7 @@
 package random
 
 import (
-	"math/rand"
-	"time"
+	"math/rand/v2"
 
 	"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
 	"github.com/nspcc-dev/neo-go/pkg/util"
@@ -27,14 +26,14 @@ func Bytes(n int) []byte {
 
 // Fill fills buffer with random bytes.
 func Fill(buf []byte) {
-	r := rand.New(rand.NewSource(time.Now().UnixNano()))
-	// Rand reader returns no errors
-	r.Read(buf)
+	for i := range buf {
+		buf[i] = byte(rand.Int())
+	}
 }
 
 // Int returns a random integer in [minI,maxI).
 func Int(minI, maxI int) int {
-	return minI + rand.Intn(maxI-minI)
+	return minI + rand.IntN(maxI-minI)
 }
 
 // Uint256 returns a random Uint256.
@@ -48,8 +47,3 @@ func Uint160() util.Uint160 {
 	str := String(20)
 	return hash.RipeMD160([]byte(str))
 }
-
-func init() {
-	//nolint:staticcheck
-	rand.Seed(time.Now().UTC().UnixNano())
-}
diff --git a/pkg/compiler/analysis.go b/pkg/compiler/analysis.go
index e9a0acc49..1ff348fd8 100644
--- a/pkg/compiler/analysis.go
+++ b/pkg/compiler/analysis.go
@@ -229,7 +229,7 @@ func isExprNil(e ast.Expr) bool {
 // indexOfStruct returns the index of the given field inside that struct.
 // If the struct does not contain that field, it will return -1.
 func indexOfStruct(strct *types.Struct, fldName string) int {
-	for i := 0; i < strct.NumFields(); i++ {
+	for i := range strct.NumFields() {
 		if strct.Field(i).Name() == fldName {
 			return i
 		}
diff --git a/pkg/compiler/codegen.go b/pkg/compiler/codegen.go
index 2d9cfc1a9..c0f670de3 100644
--- a/pkg/compiler/codegen.go
+++ b/pkg/compiler/codegen.go
@@ -383,7 +383,7 @@ func (c *codegen) isVerifyFunc(decl *ast.FuncDecl) bool {
 }
 
 func (c *codegen) clearSlots(n int) {
-	for i := 0; i < n; i++ {
+	for i := range n {
 		emit.Opcodes(c.prog.BinWriter, opcode.PUSHNULL)
 		c.emitStoreByIndex(varLocal, i)
 	}
@@ -680,7 +680,7 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
 			ast.Walk(c, n.Rhs[0])
 			c.emitToken(n.Tok, c.typeOf(n.Rhs[0]))
 		}
-		for i := 0; i < len(n.Lhs); i++ {
+		for i := range n.Lhs {
 			switch t := n.Lhs[i].(type) {
 			case *ast.Ident:
 				if n.Tok == token.DEFINE {
@@ -1099,7 +1099,7 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
 				f := c.typeOf(n.Fun).Underlying().(*types.Signature)
 				sz = f.Results().Len()
 			}
-			for i := 0; i < sz; i++ {
+			for range sz {
 				emit.Opcodes(c.prog.BinWriter, opcode.DROP)
 			}
 		}
@@ -1662,7 +1662,7 @@ func (c *codegen) dropStackLabel() {
 
 func (c *codegen) dropItems(n int) {
 	if n < 4 {
-		for i := 0; i < n; i++ {
+		for range n {
 			emit.Opcodes(c.prog.BinWriter, opcode.DROP)
 		}
 		return
@@ -1930,7 +1930,7 @@ func (c *codegen) convertBuiltin(expr *ast.CallExpr) {
 					opcode.INC)      // x y cnt+1
 				emit.Jmp(c.prog.BinWriter, opcode.JMPL, start)
 				c.setLabel(after)
-				for i := 0; i < 4; i++ { // leave x on stack
+				for range 4 { // leave x on stack
 					emit.Opcodes(c.prog.BinWriter, opcode.DROP)
 				}
 			} else {
@@ -2012,7 +2012,7 @@ func (c *codegen) emitConvert(typ stackitem.Type) {
 func (c *codegen) convertByteArray(elems []ast.Expr) {
 	buf := make([]byte, len(elems))
 	varIndices := []int{}
-	for i := 0; i < len(elems); i++ {
+	for i := range elems {
 		t := c.typeAndValueOf(elems[i])
 		if t.Value != nil {
 			val, _ := constant.Int64Val(t.Value)
@@ -2508,7 +2508,7 @@ func removeNOPs(b []byte, nopOffsets []int, sequencePoints map[string][]DebugSeq
 	// 2. Convert instructions.
 	copyOffset := 0
 	l := len(nopOffsets)
-	for i := 0; i < l; i++ {
+	for i := range l {
 		start := nopOffsets[i]
 		end := len(b)
 		if i != l-1 {
diff --git a/pkg/compiler/inline.go b/pkg/compiler/inline.go
index e9e07320d..3677aed6c 100644
--- a/pkg/compiler/inline.go
+++ b/pkg/compiler/inline.go
@@ -124,7 +124,7 @@ func (c *codegen) inlineCall(f *funcScope, n *ast.CallExpr) {
 	ast.Walk(c, f.decl.Body)
 	c.setLabel(c.inlineContext[offSz].returnLabel)
 	if c.scope.voidCalls[n] {
-		for i := 0; i < f.decl.Type.Results.NumFields(); i++ {
+		for range f.decl.Type.Results.NumFields() {
 			emit.Opcodes(c.prog.BinWriter, opcode.DROP)
 		}
 	}
diff --git a/pkg/core/block/block.go b/pkg/core/block/block.go
index 3e451258f..aae9c30c1 100644
--- a/pkg/core/block/block.go
+++ b/pkg/core/block/block.go
@@ -83,7 +83,7 @@ func NewTrimmedFromReader(stateRootEnabled bool, br *io.BinReader) (*Block, erro
 	}
 	if lenHashes > 0 {
 		block.Transactions = make([]*transaction.Transaction, lenHashes)
-		for i := 0; i < int(lenHashes); i++ {
+		for i := range lenHashes {
 			var hash util.Uint256
 			hash.DecodeBinary(br)
 			block.Transactions[i] = transaction.NewTrimmedTX(hash)
@@ -124,7 +124,7 @@ func (b *Block) DecodeBinary(br *io.BinReader) {
 		return
 	}
 	txes := make([]*transaction.Transaction, contentsCount)
-	for i := 0; i < int(contentsCount); i++ {
+	for i := range txes {
 		tx := &transaction.Transaction{}
 		tx.DecodeBinary(br)
 		txes[i] = tx
@@ -140,7 +140,7 @@ func (b *Block) DecodeBinary(br *io.BinReader) {
 func (b *Block) EncodeBinary(bw *io.BinWriter) {
 	b.Header.EncodeBinary(bw)
 	bw.WriteVarUint(uint64(len(b.Transactions)))
-	for i := 0; i < len(b.Transactions); i++ {
+	for i := range b.Transactions {
 		b.Transactions[i].EncodeBinary(bw)
 	}
 }
diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go
index afa834c56..fcfc1eec0 100644
--- a/pkg/core/blockchain.go
+++ b/pkg/core/blockchain.go
@@ -1203,7 +1203,7 @@ func (bc *Blockchain) resetTransfers(cache *dao.Simple, height uint32) error {
 				oldBatchSize = v[0]
 				newBatchSize byte
 			)
-			for i := byte(0); i < v[0]; i++ { // From oldest to newest transfer of the batch.
+			for range v[0] { // From oldest to newest transfer of the batch.
 				var t *state.NEP17Transfer
 				if k[0] == byte(storage.STNEP11Transfers) {
 					tr := new(state.NEP11Transfer)
diff --git a/pkg/core/interop/context.go b/pkg/core/interop/context.go
index 08934238b..d9ff39426 100644
--- a/pkg/core/interop/context.go
+++ b/pkg/core/interop/context.go
@@ -363,11 +363,10 @@ func (c *ContractMD) AddMethod(md *MethodAndPrice, desc *manifest.Method) {
 	desc.Safe = md.RequiredFlags&(callflag.All^callflag.ReadOnly) == 0
 
 	index, _ := slices.BinarySearchFunc(c.methods, *md, func(e, t MethodAndPrice) int {
-		res := cmp.Compare(e.MD.Name, t.MD.Name)
-		if res != 0 {
-			return res
-		}
-		return cmp.Compare(len(e.MD.Parameters), len(t.MD.Parameters))
+		return cmp.Or(
+			cmp.Compare(e.MD.Name, t.MD.Name),
+			cmp.Compare(len(e.MD.Parameters), len(t.MD.Parameters)),
+		)
 	})
 	c.methods = slices.Insert(c.methods, index, *md)
 
diff --git a/pkg/core/interop/crypto/ecdsa.go b/pkg/core/interop/crypto/ecdsa.go
index c0f712bfd..5af343cde 100644
--- a/pkg/core/interop/crypto/ecdsa.go
+++ b/pkg/core/interop/crypto/ecdsa.go
@@ -32,7 +32,7 @@ func ECDSASecp256r1CheckMultisig(ic *interop.Context) error {
 	if len(pkeys) < len(sigs) {
 		return errors.New("more signatures than there are keys")
 	}
-	sigok := vm.CheckMultisigPar(ic.VM, elliptic.P256(), hash.NetSha256(ic.Network, ic.Container).BytesBE(), pkeys, sigs)
+	sigok := vm.CheckMultisigPar(elliptic.P256(), hash.NetSha256(ic.Network, ic.Container).BytesBE(), pkeys, sigs)
 	ic.VM.Estack().PushItem(stackitem.Bool(sigok))
 	return nil
 }
diff --git a/pkg/core/interop/storage/find.go b/pkg/core/interop/storage/find.go
index dacf5a83f..e153d6f00 100644
--- a/pkg/core/interop/storage/find.go
+++ b/pkg/core/interop/storage/find.go
@@ -4,6 +4,7 @@ import (
 	"bytes"
 	"context"
 	"fmt"
+	"slices"
 
 	"github.com/nspcc-dev/neo-go/pkg/core/interop"
 	"github.com/nspcc-dev/neo-go/pkg/core/storage"
@@ -61,7 +62,7 @@ func (s *Iterator) Value() stackitem.Item {
 	}
 	key := s.curr.Key
 	if s.opts&FindRemovePrefix == 0 {
-		key = append(bytes.Clone(s.prefix), key...)
+		key = slices.Concat(s.prefix, key)
 	}
 	if s.opts&FindKeysOnly != 0 {
 		return stackitem.NewByteArray(key)
diff --git a/pkg/core/mpt/billet.go b/pkg/core/mpt/billet.go
index 10acfdd19..462fdbff5 100644
--- a/pkg/core/mpt/billet.go
+++ b/pkg/core/mpt/billet.go
@@ -318,7 +318,7 @@ func (b *Billet) tryCollapseExtension(curr *ExtensionNode) Node {
 
 func (b *Billet) tryCollapseBranch(curr *BranchNode) Node {
 	canCollapse := true
-	for i := 0; i < childrenCount; i++ {
+	for i := range childrenCount {
 		if curr.Children[i].Type() == EmptyT {
 			continue
 		}
diff --git a/pkg/core/mpt/branch.go b/pkg/core/mpt/branch.go
index 3f7317b23..17be68e0f 100644
--- a/pkg/core/mpt/branch.go
+++ b/pkg/core/mpt/branch.go
@@ -26,7 +26,7 @@ var _ Node = (*BranchNode)(nil)
 // NewBranchNode returns a new branch node.
 func NewBranchNode() *BranchNode {
 	b := new(BranchNode)
-	for i := 0; i < childrenCount; i++ {
+	for i := range childrenCount {
 		b.Children[i] = EmptyNode{}
 	}
 	return b
@@ -58,14 +58,14 @@ func (b *BranchNode) Size() int {
 
 // EncodeBinary implements io.Serializable.
 func (b *BranchNode) EncodeBinary(w *io.BinWriter) {
-	for i := 0; i < childrenCount; i++ {
+	for i := range childrenCount {
 		encodeBinaryAsChild(b.Children[i], w)
 	}
 }
 
 // DecodeBinary implements io.Serializable.
 func (b *BranchNode) DecodeBinary(r *io.BinReader) {
-	for i := 0; i < childrenCount; i++ {
+	for i := range childrenCount {
 		no := new(NodeObject)
 		no.DecodeBinary(r)
 		b.Children[i] = no.Node
diff --git a/pkg/core/mpt/helpers.go b/pkg/core/mpt/helpers.go
index 27167c593..ad31a664f 100644
--- a/pkg/core/mpt/helpers.go
+++ b/pkg/core/mpt/helpers.go
@@ -1,6 +1,10 @@
 package mpt
 
-import "github.com/nspcc-dev/neo-go/pkg/util"
+import (
+	"slices"
+
+	"github.com/nspcc-dev/neo-go/pkg/util"
+)
 
 // lcp returns the longest common prefix of a and b.
 // Note: it does no allocations.
@@ -47,7 +51,7 @@ func toNibbles(path []byte) []byte {
 // ignoring the first byte (prefix).
 func strToNibbles(path string) []byte {
 	result := make([]byte, (len(path)-1)*2)
-	for i := 0; i < len(path)-1; i++ {
+	for i := range len(path) - 1 {
 		result[i*2] = path[i+1] >> 4
 		result[i*2+1] = path[i+1] & 0x0F
 	}
@@ -85,9 +89,7 @@ func GetChildrenPaths(path []byte, node Node) map[util.Uint256][][]byte {
 		}
 	case *ExtensionNode:
 		if n.next.Type() == HashT {
-			cPath := make([]byte, len(path)+len(n.key))
-			copy(cPath, path)
-			copy(cPath[len(path):], n.key)
+			cPath := slices.Concat(path, n.key)
 			res[n.next.Hash()] = [][]byte{cPath}
 		}
 	default:
diff --git a/pkg/core/mpt/trie.go b/pkg/core/mpt/trie.go
index 0d86d16f0..f0e807c0b 100644
--- a/pkg/core/mpt/trie.go
+++ b/pkg/core/mpt/trie.go
@@ -5,6 +5,7 @@ import (
 	"encoding/binary"
 	"errors"
 	"fmt"
+	"slices"
 
 	"github.com/nspcc-dev/neo-go/pkg/core/storage"
 	"github.com/nspcc-dev/neo-go/pkg/io"
@@ -616,7 +617,7 @@ func (t *Trie) Find(prefix, from []byte, maxNum int) ([]storage.KeyValue, error)
 		if leaf, ok := node.(*LeafNode); ok {
 			if from == nil || !bytes.Equal(pathToNode, from) { // (*Billet).traverse includes `from` path into result if so. Need to filter out manually.
 				res = append(res, storage.KeyValue{
-					Key:   append(bytes.Clone(prefix), pathToNode...),
+					Key:   slices.Concat(prefix, pathToNode),
 					Value: bytes.Clone(leaf.value),
 				})
 				count++
diff --git a/pkg/core/mpt/trie_store.go b/pkg/core/mpt/trie_store.go
index 924ea7811..3ab800088 100644
--- a/pkg/core/mpt/trie_store.go
+++ b/pkg/core/mpt/trie_store.go
@@ -4,6 +4,7 @@ import (
 	"bytes"
 	"errors"
 	"fmt"
+	"slices"
 
 	"github.com/nspcc-dev/neo-go/pkg/core/storage"
 	"github.com/nspcc-dev/neo-go/pkg/util"
@@ -98,7 +99,7 @@ func (m *TrieStore) Seek(rng storage.SeekRange, f func(k, v []byte) bool) {
 		if leaf, ok := node.(*LeafNode); ok {
 			// (*Billet).traverse includes `from` path into the result if so. It's OK for Seek, so shouldn't be filtered out.
 			kv := storage.KeyValue{
-				Key:   append(bytes.Clone(rng.Prefix), pathToNode...), // Do not cut prefix.
+				Key:   slices.Concat(rng.Prefix, pathToNode), // Do not cut prefix.
 				Value: bytes.Clone(leaf.value),
 			}
 			return !f(kv.Key, kv.Value) // Should return whether to stop.
diff --git a/pkg/core/native/native_neo.go b/pkg/core/native/native_neo.go
index 58b22a0be..eea595552 100644
--- a/pkg/core/native/native_neo.go
+++ b/pkg/core/native/native_neo.go
@@ -442,7 +442,7 @@ func (n *NEO) OnPersist(ic *interop.Context) error {
 		// during the last epoch block handling or by initialization code).
 
 		var oldCommittee, newCommittee stackitem.Item
-		for i := 0; i < len(cache.committee); i++ {
+		for i := range cache.committee {
 			if cache.newEpochCommittee[i].Key != cache.committee[i].Key ||
 				(i == 0 && len(cache.newEpochCommittee) != len(cache.committee)) {
 				oldCommittee, newCommittee = cache.committee.toNotificationItem(), cache.newEpochCommittee.toNotificationItem()
diff --git a/pkg/core/state/notification_event.go b/pkg/core/state/notification_event.go
index 87f09dfee..d30f231a9 100644
--- a/pkg/core/state/notification_event.go
+++ b/pkg/core/state/notification_event.go
@@ -111,7 +111,7 @@ func (aer *AppExecResult) DecodeBinary(r *io.BinReader) {
 		return
 	}
 	arr := make([]stackitem.Item, sz)
-	for i := 0; i < int(sz); i++ {
+	for i := range arr {
 		arr[i] = stackitem.DecodeBinaryProtected(r)
 		if r.Err != nil {
 			return
diff --git a/pkg/core/state/tokens.go b/pkg/core/state/tokens.go
index 06f7d489c..0e22f360e 100644
--- a/pkg/core/state/tokens.go
+++ b/pkg/core/state/tokens.go
@@ -82,7 +82,7 @@ func (bs *TokenTransferInfo) DecodeBinary(r *io.BinReader) {
 	bs.NewNEP17Batch = r.ReadBool()
 	lenBalances := r.ReadVarUint()
 	m := make(map[int32]uint32, lenBalances)
-	for i := 0; i < int(lenBalances); i++ {
+	for range lenBalances {
 		key := int32(r.ReadU32LE())
 		m[key] = r.ReadU32LE()
 	}
@@ -142,7 +142,7 @@ func (lg *TokenTransferLog) ForEachNEP11(f func(*NEP11Transfer) (bool, error)) (
 	}
 	transfers := make([]NEP11Transfer, lg.Size())
 	r := io.NewBinReaderFromBuf(lg.Raw[1:])
-	for i := 0; i < lg.Size(); i++ {
+	for i := range transfers {
 		transfers[i].DecodeBinary(r)
 	}
 	if r.Err != nil {
@@ -164,7 +164,7 @@ func (lg *TokenTransferLog) ForEachNEP17(f func(*NEP17Transfer) (bool, error)) (
 	}
 	transfers := make([]NEP17Transfer, lg.Size())
 	r := io.NewBinReaderFromBuf(lg.Raw[1:])
-	for i := 0; i < lg.Size(); i++ {
+	for i := range transfers {
 		transfers[i].DecodeBinary(r)
 	}
 	if r.Err != nil {
diff --git a/pkg/core/storage/store.go b/pkg/core/storage/store.go
index e21804004..3ee884467 100644
--- a/pkg/core/storage/store.go
+++ b/pkg/core/storage/store.go
@@ -3,6 +3,7 @@ package storage
 import (
 	"errors"
 	"fmt"
+	"slices"
 
 	"github.com/nspcc-dev/neo-go/pkg/core/storage/dbconfig"
 	"github.com/nspcc-dev/neo-go/pkg/core/storage/dboper"
@@ -105,10 +106,8 @@ type (
 func seekRangeToPrefixes(sr SeekRange) *util.Range {
 	var (
 		rang  *util.Range
-		start = make([]byte, len(sr.Prefix)+len(sr.Start))
+		start = slices.Concat(sr.Prefix, sr.Start)
 	)
-	copy(start, sr.Prefix)
-	copy(start[len(sr.Prefix):], sr.Start)
 
 	if !sr.Backwards {
 		rang = util.BytesPrefix(sr.Prefix)
diff --git a/pkg/core/transaction/transaction.go b/pkg/core/transaction/transaction.go
index a11285e42..e34344327 100644
--- a/pkg/core/transaction/transaction.go
+++ b/pkg/core/transaction/transaction.go
@@ -8,7 +8,7 @@ import (
 	"fmt"
 	"math"
 	"math/big"
-	"math/rand"
+	"math/rand/v2"
 
 	"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
 	"github.com/nspcc-dev/neo-go/pkg/encoding/address"
@@ -160,7 +160,7 @@ func (t *Transaction) decodeHashableFields(br *io.BinReader, buf []byte) {
 		return
 	}
 	t.Signers = make([]Signer, nsigners)
-	for i := 0; i < int(nsigners); i++ {
+	for i := range t.Signers {
 		t.Signers[i].DecodeBinary(br)
 	}
 	nattrs := br.ReadVarUint()
@@ -169,7 +169,7 @@ func (t *Transaction) decodeHashableFields(br *io.BinReader, buf []byte) {
 		return
 	}
 	t.Attributes = make([]Attribute, nattrs)
-	for i := 0; i < int(nattrs); i++ {
+	for i := range t.Attributes {
 		t.Attributes[i].DecodeBinary(br)
 	}
 	t.Script = br.ReadVarBytes(MaxScriptLength)
@@ -197,7 +197,7 @@ func (t *Transaction) decodeBinaryNoSize(br *io.BinReader, buf []byte) {
 		return
 	}
 	t.Scripts = make([]Witness, nscripts)
-	for i := 0; i < int(nscripts); i++ {
+	for i := range t.Scripts {
 		t.Scripts[i].DecodeBinary(br)
 	}
 
@@ -422,7 +422,7 @@ func (t *Transaction) isValid() error {
 	if len(t.Signers) == 0 {
 		return ErrEmptySigners
 	}
-	for i := 0; i < len(t.Signers); i++ {
+	for i := range t.Signers {
 		for j := i + 1; j < len(t.Signers); j++ {
 			if t.Signers[i].Account.Equals(t.Signers[j].Account) {
 				return ErrNonUniqueSigners
diff --git a/pkg/core/transaction/witness_condition.go b/pkg/core/transaction/witness_condition.go
index d639cf300..109540ba5 100644
--- a/pkg/core/transaction/witness_condition.go
+++ b/pkg/core/transaction/witness_condition.go
@@ -233,7 +233,7 @@ func readArrayOfConditions(r *io.BinReader, maxDepth int) []WitnessCondition {
 		return nil
 	}
 	a := make([]WitnessCondition, l)
-	for i := 0; i < int(l); i++ {
+	for i := range a {
 		a[i] = decodeBinaryCondition(r, maxDepth-1)
 	}
 	if r.Err != nil {
@@ -253,7 +253,7 @@ func (c *ConditionAnd) DecodeBinarySpecific(r *io.BinReader, maxDepth int) {
 
 func arrayToJSON(c WitnessCondition, a []WitnessCondition) ([]byte, error) {
 	exprs := make([]json.RawMessage, len(a))
-	for i := 0; i < len(a); i++ {
+	for i := range a {
 		b, err := a[i].MarshalJSON()
 		if err != nil {
 			return nil, err
diff --git a/pkg/crypto/hash/merkle_tree.go b/pkg/crypto/hash/merkle_tree.go
index 09bca3eec..30c60500a 100644
--- a/pkg/crypto/hash/merkle_tree.go
+++ b/pkg/crypto/hash/merkle_tree.go
@@ -19,7 +19,7 @@ func NewMerkleTree(hashes []util.Uint256) (*MerkleTree, error) {
 	}
 
 	nodes := make([]*MerkleTreeNode, len(hashes))
-	for i := 0; i < len(hashes); i++ {
+	for i := range hashes {
 		nodes[i] = &MerkleTreeNode{
 			hash: hashes[i],
 		}
@@ -45,7 +45,7 @@ func buildMerkleTree(leaves []*MerkleTreeNode) *MerkleTreeNode {
 	}
 
 	parents := make([]*MerkleTreeNode, (len(leaves)+1)/2)
-	for i := 0; i < len(parents); i++ {
+	for i := range parents {
 		parents[i] = &MerkleTreeNode{}
 		parents[i].leftChild = leaves[i*2]
 		leaves[i*2].parent = parents[i]
@@ -81,7 +81,7 @@ func CalcMerkleRoot(hashes []util.Uint256) util.Uint256 {
 
 	scratch := make([]byte, 64)
 	parents := hashes[:(len(hashes)+1)/2]
-	for i := 0; i < len(parents); i++ {
+	for i := range parents {
 		copy(scratch, hashes[i*2].BytesBE())
 
 		if i*2+1 == len(hashes) {
diff --git a/pkg/crypto/keys/nep2.go b/pkg/crypto/keys/nep2.go
index f0c92eaa5..cc4ea675f 100644
--- a/pkg/crypto/keys/nep2.go
+++ b/pkg/crypto/keys/nep2.go
@@ -149,7 +149,7 @@ func xor(a, b []byte) []byte {
 		panic("cannot XOR non equal length arrays")
 	}
 	dst := make([]byte, len(a))
-	for i := 0; i < len(dst); i++ {
+	for i := range dst {
 		dst[i] = a[i] ^ b[i]
 	}
 	return dst
diff --git a/pkg/encoding/bigint/bigint.go b/pkg/encoding/bigint/bigint.go
index 58d74b255..05a5150e3 100644
--- a/pkg/encoding/bigint/bigint.go
+++ b/pkg/encoding/bigint/bigint.go
@@ -41,7 +41,7 @@ func FromBytes(data []byte) *big.Int {
 
 	lw := size / wordSizeBytes
 	ws := make([]big.Word, lw+1)
-	for i := 0; i < lw; i++ {
+	for i := range lw {
 		base := i * wordSizeBytes
 		for j := base + 7; j >= base; j-- {
 			ws[i] <<= 8
diff --git a/pkg/encoding/fixedn/decimal.go b/pkg/encoding/fixedn/decimal.go
index 4a6cfc8e0..5fe0e2391 100644
--- a/pkg/encoding/fixedn/decimal.go
+++ b/pkg/encoding/fixedn/decimal.go
@@ -17,7 +17,7 @@ var _pow10 []*big.Int
 
 func init() {
 	var p = int64(1)
-	for i := 0; i <= maxAllowedPrecision; i++ {
+	for range maxAllowedPrecision + 1 {
 		_pow10 = append(_pow10, big.NewInt(p))
 		p *= 10
 	}
diff --git a/pkg/interop/go.mod b/pkg/interop/go.mod
index eeda448b6..50bf310da 100644
--- a/pkg/interop/go.mod
+++ b/pkg/interop/go.mod
@@ -1,3 +1,3 @@
 module github.com/nspcc-dev/neo-go/pkg/interop
 
-go 1.21
+go 1.22
diff --git a/pkg/io/binaryReader.go b/pkg/io/binaryReader.go
index 642eae245..848affa70 100644
--- a/pkg/io/binaryReader.go
+++ b/pkg/io/binaryReader.go
@@ -128,7 +128,7 @@ func (r *BinReader) ReadArray(t any, maxSize ...int) {
 	l := int(lu)
 	arr := reflect.MakeSlice(sliceType, l, l)
 
-	for i := 0; i < l; i++ {
+	for i := range l {
 		var elem reflect.Value
 		if isPtr {
 			elem = reflect.New(elemType.Elem())
diff --git a/pkg/io/binaryWriter.go b/pkg/io/binaryWriter.go
index 9abadb7cf..c4e7f0f05 100644
--- a/pkg/io/binaryWriter.go
+++ b/pkg/io/binaryWriter.go
@@ -78,7 +78,7 @@ func (w *BinWriter) WriteArray(arr any) {
 		typ := val.Type().Elem()
 
 		w.WriteVarUint(uint64(val.Len()))
-		for i := 0; i < val.Len(); i++ {
+		for i := range val.Len() {
 			el, ok := val.Index(i).Interface().(encodable)
 			if !ok {
 				el, ok = val.Index(i).Addr().Interface().(encodable)
diff --git a/pkg/io/size.go b/pkg/io/size.go
index 25dd51686..8bfabb5fa 100644
--- a/pkg/io/size.go
+++ b/pkg/io/size.go
@@ -75,7 +75,7 @@ func GetVarSize(value any) int {
 		if valueLength != 0 {
 			switch reflect.ValueOf(value).Index(0).Interface().(type) {
 			case Serializable:
-				for i := 0; i < valueLength; i++ {
+				for i := range valueLength {
 					valueSize += GetVarSize(v.Index(i).Interface())
 				}
 			case uint8, int8:
diff --git a/pkg/neorpc/result/mpt.go b/pkg/neorpc/result/mpt.go
index ff2a6b1a4..a8421c98c 100644
--- a/pkg/neorpc/result/mpt.go
+++ b/pkg/neorpc/result/mpt.go
@@ -49,7 +49,7 @@ func (p *ProofWithKey) EncodeBinary(w *io.BinWriter) {
 func (p *ProofWithKey) DecodeBinary(r *io.BinReader) {
 	p.Key = r.ReadVarBytes()
 	sz := r.ReadVarUint()
-	for i := uint64(0); i < sz; i++ {
+	for range sz {
 		p.Proof = append(p.Proof, r.ReadVarBytes())
 	}
 }
diff --git a/pkg/network/bqueue/queue.go b/pkg/network/bqueue/queue.go
index 4ba5be7fc..779448ac2 100644
--- a/pkg/network/bqueue/queue.go
+++ b/pkg/network/bqueue/queue.go
@@ -157,9 +157,7 @@ func (bq *Queue) Discard() {
 		close(bq.checkBlocks)
 		// Technically we could bq.queue = nil, but this would cost
 		// another if in Run().
-		for i := 0; i < len(bq.queue); i++ {
-			bq.queue[i] = nil
-		}
+		clear(bq.queue)
 		bq.len = 0
 		bq.queueLock.Unlock()
 	}
diff --git a/pkg/network/discovery.go b/pkg/network/discovery.go
index 5659ee6c0..b48d7682f 100644
--- a/pkg/network/discovery.go
+++ b/pkg/network/discovery.go
@@ -2,7 +2,7 @@ package network
 
 import (
 	"math"
-	"math/rand"
+	"math/rand/v2"
 	"sync"
 	"sync/atomic"
 	"time"
@@ -300,7 +300,7 @@ func (d *DefaultDiscovery) updateNetSize() {
 }
 
 func (d *DefaultDiscovery) tryAddress(addr string) {
-	var tout = rand.Int63n(int64(tryMaxWait))
+	var tout = rand.Int64N(int64(tryMaxWait))
 	time.Sleep(time.Duration(tout)) // Have a sleep before working hard.
 	p, err := d.transport.Dial(addr, d.dialTimeout)
 	atomic.AddInt32(&d.outstanding, -1)
diff --git a/pkg/network/payload/headers.go b/pkg/network/payload/headers.go
index 5d75279e0..11add0d12 100644
--- a/pkg/network/payload/headers.go
+++ b/pkg/network/payload/headers.go
@@ -44,7 +44,7 @@ func (p *Headers) DecodeBinary(br *io.BinReader) {
 
 	p.Hdrs = make([]*block.Header, lenHeaders)
 
-	for i := 0; i < int(lenHeaders); i++ {
+	for i := range p.Hdrs {
 		header := &block.Header{}
 		header.StateRootEnabled = p.StateRootInHeader
 		header.DecodeBinary(br)
diff --git a/pkg/network/payload/mptdata.go b/pkg/network/payload/mptdata.go
index 687aa68eb..8a5e9c200 100644
--- a/pkg/network/payload/mptdata.go
+++ b/pkg/network/payload/mptdata.go
@@ -26,7 +26,7 @@ func (d *MPTData) DecodeBinary(r *io.BinReader) {
 		r.Err = errors.New("empty MPT nodes list")
 		return
 	}
-	for i := uint64(0); i < sz; i++ {
+	for range sz {
 		d.Nodes = append(d.Nodes, r.ReadVarBytes())
 		if r.Err != nil {
 			return
diff --git a/pkg/network/server.go b/pkg/network/server.go
index 556ac4e37..250d9bd6d 100644
--- a/pkg/network/server.go
+++ b/pkg/network/server.go
@@ -7,7 +7,7 @@ import (
 	"errors"
 	"fmt"
 	"math/big"
-	mrand "math/rand"
+	mrand "math/rand/v2"
 	"net"
 	"runtime"
 	"slices"
@@ -288,7 +288,7 @@ func (s *Server) Start() {
 
 	var txThreads = optimalNumOfThreads()
 	s.txHandlerLoopWG.Add(txThreads)
-	for i := 0; i < txThreads; i++ {
+	for range txThreads {
 		go s.txHandlerLoop()
 	}
 	go s.broadcastTxLoop()
@@ -1331,7 +1331,7 @@ func getRequestBlocksPayload(p Peer, currHeight uint32, lastRequestedHeight *ato
 				}
 			}
 		} else {
-			index := mrand.Intn(bqueue.CacheSize / payload.MaxHashesCount)
+			index := mrand.IntN(bqueue.CacheSize / payload.MaxHashesCount)
 			needHeight = currHeight + 1 + uint32(index*payload.MaxHashesCount)
 		}
 		break
@@ -1487,7 +1487,7 @@ func (s *Server) RequestTx(hashes ...util.Uint256) {
 	slices.SortFunc(sorted, util.Uint256.Compare)
 	s.txCbList.Store(sorted)
 
-	for i := 0; i <= len(hashes)/payload.MaxHashesCount; i++ {
+	for i := range len(hashes)/payload.MaxHashesCount + 1 {
 		start := i * payload.MaxHashesCount
 		stop := (i + 1) * payload.MaxHashesCount
 		stop = min(stop, len(hashes))
diff --git a/pkg/services/oracle/oracle.go b/pkg/services/oracle/oracle.go
index e0c0c6906..d8a3ad9c2 100644
--- a/pkg/services/oracle/oracle.go
+++ b/pkg/services/oracle/oracle.go
@@ -218,7 +218,7 @@ func (o *Oracle) start() {
 	o.running = true
 	o.respMtx.Unlock()
 
-	for i := 0; i < o.MainCfg.MaxConcurrentRequests; i++ {
+	for range o.MainCfg.MaxConcurrentRequests {
 		go o.runRequestWorker()
 	}
 	go o.ResponseHandler.Run()
diff --git a/pkg/services/rpcsrv/server.go b/pkg/services/rpcsrv/server.go
index 88db50f5f..2098a89ba 100644
--- a/pkg/services/rpcsrv/server.go
+++ b/pkg/services/rpcsrv/server.go
@@ -978,7 +978,7 @@ func (s *Server) calculateNetworkFee(reqParams params.Params) (any, *neorpc.Erro
 					paramz = []manifest.Parameter{{Type: smartcontract.SignatureType}}
 				} else if nSigs, _, ok := vm.ParseMultiSigContract(w.VerificationScript); ok {
 					paramz = make([]manifest.Parameter, nSigs)
-					for j := 0; j < nSigs; j++ {
+					for j := range paramz {
 						paramz[j] = manifest.Parameter{Type: smartcontract.SignatureType}
 					}
 				}
diff --git a/pkg/smartcontract/manifest/abi.go b/pkg/smartcontract/manifest/abi.go
index 67e28b073..de85abcae 100644
--- a/pkg/smartcontract/manifest/abi.go
+++ b/pkg/smartcontract/manifest/abi.go
@@ -63,11 +63,10 @@ func (a *ABI) IsValid() error {
 		}
 	}
 	if sliceHasDups(a.Methods, func(a, b Method) int {
-		res := cmp.Compare(a.Name, b.Name)
-		if res != 0 {
-			return res
-		}
-		return cmp.Compare(len(a.Parameters), len(b.Parameters))
+		return cmp.Or(
+			cmp.Compare(a.Name, b.Name),
+			cmp.Compare(len(a.Parameters), len(b.Parameters)),
+		)
 	}) {
 		return errors.New("duplicate method specifications")
 	}
diff --git a/pkg/smartcontract/rpcbinding/binding.go b/pkg/smartcontract/rpcbinding/binding.go
index 5d98f2e18..22d78318c 100644
--- a/pkg/smartcontract/rpcbinding/binding.go
+++ b/pkg/smartcontract/rpcbinding/binding.go
@@ -1,6 +1,7 @@
 package rpcbinding
 
 import (
+	"cmp"
 	"fmt"
 	"slices"
 	"strings"
@@ -473,27 +474,25 @@ func Generate(cfg binding.Config) error {
 }
 
 func dropManifestMethods(meths []manifest.Method, manifested []manifest.Method) []manifest.Method {
-	for _, m := range manifested {
-		for i := 0; i < len(meths); i++ {
-			if meths[i].Name == m.Name && len(meths[i].Parameters) == len(m.Parameters) {
-				meths = append(meths[:i], meths[i+1:]...)
-				i--
-			}
-		}
-	}
-	return meths
+	return slices.DeleteFunc(meths, func(m manifest.Method) bool {
+		return slices.ContainsFunc(manifested, func(e manifest.Method) bool {
+			return 0 == cmp.Or(
+				cmp.Compare(m.Name, e.Name),
+				cmp.Compare(len(m.Parameters), len(e.Parameters)),
+			)
+		})
+	})
 }
 
 func dropManifestEvents(events []manifest.Event, manifested []manifest.Event) []manifest.Event {
-	for _, e := range manifested {
-		for i := 0; i < len(events); i++ {
-			if events[i].Name == e.Name && len(events[i].Parameters) == len(e.Parameters) {
-				events = append(events[:i], events[i+1:]...)
-				i--
-			}
-		}
-	}
-	return events
+	return slices.DeleteFunc(events, func(e manifest.Event) bool {
+		return slices.ContainsFunc(manifested, func(v manifest.Event) bool {
+			return 0 == cmp.Or(
+				cmp.Compare(e.Name, v.Name),
+				cmp.Compare(len(e.Parameters), len(v.Parameters)),
+			)
+		})
+	})
 }
 
 func dropStdMethods(meths []manifest.Method, std *standard.Standard) []manifest.Method {
@@ -712,7 +711,7 @@ func scTemplateToRPC(cfg binding.Config, ctr ContractTmpl, imports map[string]st
 					ctr.SafeMethods[len(ctr.SafeMethods)-1].ItemTo = cutPointer(ctr.Methods[i].ReturnType)
 				}
 			}
-			ctr.Methods = append(ctr.Methods[:i], ctr.Methods[i+1:]...)
+			ctr.Methods = slices.Delete(ctr.Methods, i, i+1)
 			i--
 		} else {
 			ctr.Methods[i].Comment = fmt.Sprintf("creates a transaction invoking `%s` method of the contract.", ctr.Methods[i].NameABI)
diff --git a/pkg/smartcontract/zkpbinding/binding.go b/pkg/smartcontract/zkpbinding/binding.go
index bfb1b1a19..9eb0a8fc6 100644
--- a/pkg/smartcontract/zkpbinding/binding.go
+++ b/pkg/smartcontract/zkpbinding/binding.go
@@ -133,11 +133,11 @@ func VerifyProof(a []byte, b []byte, c []byte, publicInput [][]byte) bool {
 		panic("error: inputlen or iclen")
 	}
 	icPoints := make([]crypto.Bls12381Point, iclen)
-	for i := 0; i < iclen; i++ {
+	for i := range iclen {
 		icPoints[i] = crypto.Bls12381Deserialize(ic[i])
 	}
 	acc := icPoints[0]
-	for i := 0; i < inputlen; i++ {
+	for i := range inputlen {
 		scalar := publicInput[i] // 32-bytes LE field element.
 		temp := crypto.Bls12381Mul(icPoints[i+1], scalar, false)
 		acc = crypto.Bls12381Add(acc, temp)
@@ -165,7 +165,7 @@ supportedstandards: []`
 	// and dependency packages version needed for smart contract compilation.
 	verifyGomod = `module verify
 
-go 1.21
+go 1.22
 
 require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20231004150345-8849ccde2524
 `
diff --git a/pkg/util/uint160.go b/pkg/util/uint160.go
index 9d1d362f3..6c59d56c0 100644
--- a/pkg/util/uint160.go
+++ b/pkg/util/uint160.go
@@ -96,7 +96,7 @@ func (u Uint160) StringLE() string {
 
 // Reverse returns a reversed representation of u.
 func (u Uint160) Reverse() (r Uint160) {
-	for i := 0; i < Uint160Size; i++ {
+	for i := range Uint160Size {
 		r[i] = u[Uint160Size-i-1]
 	}
 
diff --git a/pkg/vm/opcode/from_string.go b/pkg/vm/opcode/from_string.go
index 79c0e3e44..57f45ffdf 100644
--- a/pkg/vm/opcode/from_string.go
+++ b/pkg/vm/opcode/from_string.go
@@ -5,7 +5,7 @@ import "errors"
 var stringToOpcode = make(map[string]Opcode)
 
 func init() {
-	for i := 0; i < 255; i++ {
+	for i := range 255 {
 		op := Opcode(i)
 		stringToOpcode[op.String()] = op
 	}
diff --git a/pkg/vm/stack.go b/pkg/vm/stack.go
index f098dfc55..4e393a60d 100644
--- a/pkg/vm/stack.go
+++ b/pkg/vm/stack.go
@@ -265,7 +265,7 @@ func (s *Stack) Iter(f func(Element)) {
 //		// do something with the element.
 //	})
 func (s *Stack) IterBack(f func(Element)) {
-	for i := 0; i < len(s.elems); i++ {
+	for i := range s.elems {
 		f(s.elems[i])
 	}
 }
@@ -347,7 +347,7 @@ func (s *Stack) PopSigElements() ([][]byte, error) {
 			return nil, fmt.Errorf("wrong number of elements: need %d, have %d", num, s.Len())
 		}
 		elems = make([][]byte, num)
-		for i := 0; i < num; i++ {
+		for i := range num {
 			elems[i] = s.Pop().Bytes()
 		}
 	}
diff --git a/pkg/vm/stackitem/serialization.go b/pkg/vm/stackitem/serialization.go
index 190ca8640..fe702f9b2 100644
--- a/pkg/vm/stackitem/serialization.go
+++ b/pkg/vm/stackitem/serialization.go
@@ -335,7 +335,7 @@ func (r *deserContext) decodeBinary() Item {
 			return nil
 		}
 		arr := make([]Item, size)
-		for i := 0; i < size; i++ {
+		for i := range size {
 			arr[i] = r.decodeBinary()
 		}
 
@@ -350,7 +350,7 @@ func (r *deserContext) decodeBinary() Item {
 			return nil
 		}
 		m := NewMap()
-		for i := 0; i < size; i++ {
+		for range size {
 			key := r.decodeBinary()
 			value := r.decodeBinary()
 			if r.Err != nil {
diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go
index 39ea54124..5c9b03b7b 100644
--- a/pkg/vm/vm.go
+++ b/pkg/vm/vm.go
@@ -696,7 +696,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
 		if parameter[1] > 0 {
 			sz := int(parameter[1])
 			ctx.arguments.init(sz, &v.refs)
-			for i := 0; i < sz; i++ {
+			for i := range sz {
 				ctx.arguments.Set(i, v.estack.Pop().Item(), &v.refs)
 			}
 		}
@@ -1199,7 +1199,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
 		}
 
 		items := make([]stackitem.MapElement, n)
-		for i := 0; i < n; i++ {
+		for i := range n {
 			key := v.estack.Pop()
 			validateMapKey(key)
 			val := v.estack.Pop().value
@@ -1215,7 +1215,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
 		}
 
 		items := make([]stackitem.Item, n)
-		for i := 0; i < n; i++ {
+		for i := range n {
 			items[i] = v.estack.Pop().value
 		}
 
@@ -1807,7 +1807,7 @@ func (v *VM) handleException() {
 				j = -1
 				continue
 			}
-			for i := 0; i < pop; i++ {
+			for range pop {
 				ctx := v.istack[len(v.istack)-1]
 				v.istack = v.istack[:len(v.istack)-1]
 				v.unloadContext(ctx)
@@ -1852,7 +1852,7 @@ func throwUnhandledException(item stackitem.Item) {
 // block in one of its contexts.
 func (v *VM) ContractHasTryBlock() bool {
 	var topctx *Context // Currently executing context.
-	for i := 0; i < len(v.istack); i++ {
+	for i := range v.istack {
 		ictx := v.istack[len(v.istack)-1-i] // It's a stack, going backwards like handleException().
 		if topctx == nil {
 			topctx = ictx
@@ -1871,7 +1871,7 @@ func (v *VM) ContractHasTryBlock() bool {
 }
 
 // CheckMultisigPar checks if the sigs contains sufficient valid signatures.
-func CheckMultisigPar(v *VM, curve elliptic.Curve, h []byte, pkeys [][]byte, sigs [][]byte) bool {
+func CheckMultisigPar(curve elliptic.Curve, h []byte, pkeys [][]byte, sigs [][]byte) bool {
 	if len(sigs) == 1 {
 		return slices.ContainsFunc(pkeys, func(keyb []byte) bool {
 			pkey := bytesToPublicKey(keyb, curve)
@@ -1909,7 +1909,7 @@ func CheckMultisigPar(v *VM, curve elliptic.Curve, h []byte, pkeys [][]byte, sig
 	const workerCount = 3
 	tasks := make(chan task, 2)
 	results := make(chan verify, len(sigs))
-	for i := 0; i < workerCount; i++ {
+	for range workerCount {
 		go worker(tasks, results)
 	}
 
diff --git a/scripts/gendump/main.go b/scripts/gendump/main.go
index f21159383..271cb85c8 100644
--- a/scripts/gendump/main.go
+++ b/scripts/gendump/main.go
@@ -90,7 +90,7 @@ func main() {
 	txNum := int(*txPerBlock)
 	for i := bc.BlockHeight(); i < blocksNum; i++ {
 		txs := make([]*transaction.Transaction, txNum)
-		for j := 0; j < txNum; j++ {
+		for j := range txNum {
 			nonce++
 			_, err = rand.Read(key)
 			handleError("can't get random data for key", err)


Test patch:
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index eab01a75a..4cf7bb0aa 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -156,19 +156,13 @@ jobs:
     strategy:
       matrix:
         os: [ubuntu-22.04, windows-2022, macos-12, macos-14]
-        go_versions: [ '1.21', '1.22', '1.23' ]
+        go_versions: [ '1.22', '1.23' ]
         exclude:
           # Only latest Go version for Windows and MacOS.
-          - os: windows-2022
-            go_versions: '1.21'
           - os: windows-2022
             go_versions: '1.22'
-          - os: macos-12
-            go_versions: '1.21'
           - os: macos-12
             go_versions: '1.22'
-          - os: macos-14
-            go_versions: '1.21'
           - os: macos-14
             go_versions: '1.22'
           # Exclude latest Go version for Ubuntu as Coverage uses it.
diff --git a/cli/nep_test/nep17_test.go b/cli/nep_test/nep17_test.go
index e7aa29393..beed51a95 100644
--- a/cli/nep_test/nep17_test.go
+++ b/cli/nep_test/nep17_test.go
@@ -4,6 +4,7 @@ import (
 	"io"
 	"math/big"
 	"path/filepath"
+	"slices"
 	"strconv"
 	"strings"
 	"testing"
@@ -80,7 +81,7 @@ func TestNEP17Balance(t *testing.T) {
 
 		e.CheckNextLine(t, "^Account "+testcli.TestWalletMultiAccount1)
 		// The order of assets is undefined.
-		for i := 0; i < 2; i++ {
+		for range 2 {
 			line := e.GetNextLine(t)
 			if strings.Contains(line, "GAS") {
 				e.CheckLine(t, line, "^\\s*GAS:\\s+GasToken \\("+e.Chain.UtilityTokenHash().StringLE()+"\\)")
@@ -133,8 +134,7 @@ func TestNEP17Transfer(t *testing.T) {
 	}
 
 	t.Run("missing receiver", func(t *testing.T) {
-		as := append([]string{}, args[:8]...)
-		as = append(as, args[10:]...)
+		as := slices.Concat(args[:8], args[10:])
 		e.In.WriteString("one\r")
 		e.RunWithErrorCheck(t, `Required flag "to" not set`, as...)
 		e.In.Reset()
diff --git a/cli/vm/cli_test.go b/cli/vm/cli_test.go
index c2b994f92..985b5510f 100644
--- a/cli/vm/cli_test.go
+++ b/cli/vm/cli_test.go
@@ -353,7 +353,7 @@ require (
 	github.com/nspcc-dev/neo-go/pkg/interop v0.0.0
 )
 replace github.com/nspcc-dev/neo-go/pkg/interop => ` + filepath.Join(wd, "../../pkg/interop") + `
-go 1.21`)
+go 1.22`)
 	require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "go.mod"), goMod, os.ModePerm))
 	return filename
 }
diff --git a/cli/wallet/wallet_test.go b/cli/wallet/wallet_test.go
index 2a60d796d..10b80c38c 100644
--- a/cli/wallet/wallet_test.go
+++ b/cli/wallet/wallet_test.go
@@ -1062,7 +1062,7 @@ func TestWalletDumpKeys(t *testing.T) {
 		e.CheckNextLine(t, pubRegex)
 		e.CheckNextLine(t, "^\\s*$")
 		e.CheckNextLine(t, "NVTiAjNgagDkTr5HTzDmQP9kPwPHN5BgVq")
-		for i := 0; i < 4; i++ {
+		for range 4 {
 			e.CheckNextLine(t, pubRegex)
 		}
 		e.CheckNextLine(t, "^\\s*$")
@@ -1085,7 +1085,7 @@ func TestWalletDumpKeys(t *testing.T) {
 		cmd := append(cmd, "-a", "NVTiAjNgagDkTr5HTzDmQP9kPwPHN5BgVq")
 		e.Run(t, cmd...)
 		e.CheckNextLine(t, "3 out of 4 multisig contract")
-		for i := 0; i < 4; i++ {
+		for range 4 {
 			e.CheckNextLine(t, pubRegex)
 		}
 		e.CheckEOF(t)
diff --git a/examples/nft-nd-nns/nns_test.go b/examples/nft-nd-nns/nns_test.go
index 5d4362105..140be1feb 100644
--- a/examples/nft-nd-nns/nns_test.go
+++ b/examples/nft-nd-nns/nns_test.go
@@ -158,7 +158,7 @@ func TestRegisterAndRenew(t *testing.T) {
 	c.InvokeWithFeeFail(t, "GAS limit exceeded", defaultNameServiceSysfee, "register", "neo.org", e.CommitteeHash)
 	c.InvokeWithFeeFail(t, "GAS limit exceeded", defaultNameServiceDomainPrice, "register", "neo.com", e.CommitteeHash)
 	var maxLenFragment string
-	for i := 0; i < maxDomainNameFragmentLength; i++ {
+	for i := range maxDomainNameFragmentLength {
 		maxLenFragment += "q"
 	}
 	c.Invoke(t, true, "isAvailable", maxLenFragment+".com")
diff --git a/examples/zkp/cubic_circuit/main_test.go b/examples/zkp/cubic_circuit/main_test.go
index 39cc5d61f..1c9518657 100644
--- a/examples/zkp/cubic_circuit/main_test.go
+++ b/examples/zkp/cubic_circuit/main_test.go
@@ -315,7 +315,7 @@ func setup(t *testing.T, ccs constraint.ConstraintSystem, phase1ResponsePath str
 	// receive a []byte, deserialize it, add his contribution and send back to
 	// coordinator, like it is done in https://github.com/bnb-chain/zkbnb-setup
 	// for BN254 elliptic curve.
-	for i := 0; i < nContributionsPhase2; i++ {
+	for i := range nContributionsPhase2 {
 		srs2.Contribute()
 	}
 
diff --git a/internal/basicchain/testdata/storage/storage_contract.go b/internal/basicchain/testdata/storage/storage_contract.go
index f98772b8e..338e28a0f 100644
--- a/internal/basicchain/testdata/storage/storage_contract.go
+++ b/internal/basicchain/testdata/storage/storage_contract.go
@@ -19,7 +19,7 @@ var valuesPrefix = []byte{0x01}
 func _deploy(data any, isUpdate bool) {
 	if !isUpdate {
 		ctx := storage.GetContext()
-		for i := 0; i < valuesCount; i++ {
+		for i := range valuesCount {
 			key := append(valuesPrefix, byte(i))
 			storage.Put(ctx, key, i)
 		}
diff --git a/internal/testchain/address.go b/internal/testchain/address.go
index 3db905638..eabdc41c1 100644
--- a/internal/testchain/address.go
+++ b/internal/testchain/address.go
@@ -128,7 +128,7 @@ func CommitteeAddress() string {
 // Sign signs data by all consensus nodes and returns invocation script.
 func Sign(h hash.Hashable) []byte {
 	buf := io.NewBufBinWriter()
-	for i := 0; i < 3; i++ {
+	for i := range 3 {
 		pKey := PrivateKey(i)
 		sig := pKey.SignHashable(uint32(Network()), h)
 		if len(sig) != 64 {
diff --git a/internal/testcli/executor.go b/internal/testcli/executor.go
index eb8d18ad2..24957db0e 100644
--- a/internal/testcli/executor.go
+++ b/internal/testcli/executor.go
@@ -377,7 +377,7 @@ func (e *Executor) CheckTxTestInvokeOutput(t *testing.T, scriptSize int) {
 
 func (e *Executor) CheckScriptDump(t *testing.T, scriptSize int) {
 	e.CheckNextLine(t, `INDEX\s+`)
-	for i := 0; i < scriptSize; i++ {
+	for range scriptSize {
 		e.CheckNextLine(t, `\d+\s+\w+`)
 	}
 }
diff --git a/pkg/compiler/global_test.go b/pkg/compiler/global_test.go
index bc0a617c3..ec4686afd 100644
--- a/pkg/compiler/global_test.go
+++ b/pkg/compiler/global_test.go
@@ -883,11 +883,11 @@ func TestMultipleFuncSameName(t *testing.T) {
 func TestConstDontUseSlots(t *testing.T) {
 	const count = 256
 	buf := bytes.NewBufferString("package foo\n")
-	for i := 0; i < count; i++ {
+	for i := range count {
 		buf.WriteString(fmt.Sprintf("const n%d = 1\n", i))
 	}
 	buf.WriteString("func Main() int { sum := 0\n")
-	for i := 0; i < count; i++ {
+	for i := range count {
 		buf.WriteString(fmt.Sprintf("sum += n%d\n", i))
 	}
 	buf.WriteString("return sum }")
@@ -899,11 +899,11 @@ func TestConstDontUseSlots(t *testing.T) {
 func TestUnderscoreVarsDontUseSlots(t *testing.T) {
 	const count = 128
 	buf := bytes.NewBufferString("package foo\n")
-	for i := 0; i < count; i++ {
+	for i := range count {
 		buf.WriteString(fmt.Sprintf("var _, n%d = 1, 1\n", i))
 	}
 	buf.WriteString("func Main() int { sum := 0\n")
-	for i := 0; i < count; i++ {
+	for i := range count {
 		buf.WriteString(fmt.Sprintf("sum += n%d\n", i))
 	}
 	buf.WriteString("return sum }")
diff --git a/pkg/compiler/limit_test.go b/pkg/compiler/limit_test.go
index b80282dd5..000d60c85 100644
--- a/pkg/compiler/limit_test.go
+++ b/pkg/compiler/limit_test.go
@@ -15,7 +15,7 @@ func TestManyVariables(t *testing.T) {
 	const count = 155
 
 	buf := bytes.NewBufferString("package main\n")
-	for i := 0; i < count; i++ {
+	for i := range count {
 		buf.WriteString(fmt.Sprintf("var a%d = %d\n", i, i))
 	}
 	buf.WriteString("func Main() int {\nreturn 7\n}\n")
diff --git a/pkg/compiler/native_test.go b/pkg/compiler/native_test.go
index 249e6717e..a3c6780e0 100644
--- a/pkg/compiler/native_test.go
+++ b/pkg/compiler/native_test.go
@@ -361,7 +361,7 @@ func runNativeTestCase(t *testing.T, b *nef.File, di *compiler.DebugInfo, ctr in
 		if t.CallFlag != md.RequiredFlags {
 			return fmt.Errorf("wrong flags %v", t.CallFlag)
 		}
-		for i := 0; i < int(t.ParamCount); i++ {
+		for range t.ParamCount {
 			_ = v.Estack().Pop()
 		}
 		if v.Estack().Len() != 0 {
diff --git a/pkg/compiler/syscall_test.go b/pkg/compiler/syscall_test.go
index 1358e02cb..63cddce70 100644
--- a/pkg/compiler/syscall_test.go
+++ b/pkg/compiler/syscall_test.go
@@ -151,7 +151,7 @@ func runSyscallTestCase(t *testing.T, ic *interop.Context, realName string,
 		if ic.VM.Estack().Len() < f.ParamCount {
 			return errors.New("not enough parameters")
 		}
-		for i := 0; i < f.ParamCount; i++ {
+		for range f.ParamCount {
 			ic.VM.Estack().Pop()
 		}
 		if !tc.isVoid {
@@ -385,7 +385,7 @@ func TestInteropTypesComparison(t *testing.T) {
 	typeCheck := func(t *testing.T, typeName string, typeLen int) {
 		t.Run(typeName, func(t *testing.T) {
 			var ha, hb string
-			for i := 0; i < typeLen; i++ {
+			for i := range typeLen {
 				if i == typeLen-1 {
 					ha += "2"
 					hb += "3"
diff --git a/pkg/consensus/consensus_test.go b/pkg/consensus/consensus_test.go
index df1dd8a86..3ec43501a 100644
--- a/pkg/consensus/consensus_test.go
+++ b/pkg/consensus/consensus_test.go
@@ -195,7 +195,7 @@ func TestService_GetVerified(t *testing.T) {
 	srv := newTestService(t)
 	srv.dbft.Start(0)
 	var txs []*transaction.Transaction
-	for i := 0; i < 4; i++ {
+	for i := range 4 {
 		tx := transaction.New([]byte{byte(opcode.PUSH1)}, 100000)
 		tx.Nonce = 123 + uint32(i)
 		tx.ValidUntilBlock = 1
@@ -208,7 +208,7 @@ func TestService_GetVerified(t *testing.T) {
 	hashes := []util.Uint256{txs[0].Hash(), txs[1].Hash(), txs[2].Hash()}
 
 	// Everyone sends a message.
-	for i := 0; i < 4; i++ {
+	for i := range 4 {
 		p := new(Payload)
 		// One PrepareRequest and three ChangeViews.
 		if i == 1 {
@@ -578,7 +578,7 @@ func addSender(t *testing.T, txs ...*transaction.Transaction) {
 func signTx(t *testing.T, bc Ledger, txs ...*transaction.Transaction) {
 	validators := make([]*keys.PublicKey, 4)
 	privNetKeys := make([]*keys.PrivateKey, 4)
-	for i := 0; i < 4; i++ {
+	for i := range 4 {
 		privNetKeys[i] = testchain.PrivateKey(i)
 		validators[i] = privNetKeys[i].PublicKey()
 	}
diff --git a/pkg/consensus/payload_test.go b/pkg/consensus/payload_test.go
index 0e7253967..cf6e761ff 100644
--- a/pkg/consensus/payload_test.go
+++ b/pkg/consensus/payload_test.go
@@ -3,7 +3,7 @@ package consensus
 import (
 	"encoding/hex"
 	gio "io"
-	"math/rand"
+	"math/rand/v2"
 	"testing"
 
 	"github.com/nspcc-dev/dbft"
@@ -200,7 +200,7 @@ func randomPrepareRequest(t *testing.T) *prepareRequest {
 		transactionHashes: make([]util.Uint256, txCount),
 	}
 
-	for i := 0; i < txCount; i++ {
+	for i := range txCount {
 		req.transactionHashes[i] = random.Uint256()
 	}
 
diff --git a/pkg/consensus/recovery_message_test.go b/pkg/consensus/recovery_message_test.go
index 50ee54c2f..58a1ac22a 100644
--- a/pkg/consensus/recovery_message_test.go
+++ b/pkg/consensus/recovery_message_test.go
@@ -23,7 +23,7 @@ func testRecoveryMessageSetters(t *testing.T, enableStateRoot bool) {
 	srv := newTestServiceWithState(t, enableStateRoot)
 	privs := make([]*privateKey, testchain.Size())
 	pubs := make([]dbft.PublicKey, testchain.Size())
-	for i := 0; i < testchain.Size(); i++ {
+	for i := range testchain.Size() {
 		privs[i], pubs[i] = getTestValidator(i)
 	}
 
diff --git a/pkg/core/bench_test.go b/pkg/core/bench_test.go
index f7d25e67d..25d9f1ae4 100644
--- a/pkg/core/bench_test.go
+++ b/pkg/core/bench_test.go
@@ -27,7 +27,7 @@ func BenchmarkBlockchain_VerifyWitness(t *testing.B) {
 	tx := e.NewTx(t, []neotest.Signer{acc}, e.NativeHash(t, nativenames.Gas), "transfer", acc.ScriptHash(), acc.Script(), 1, nil)
 
 	t.ResetTimer()
-	for n := 0; n < t.N; n++ {
+	for range t.N {
 		_, err := bc.VerifyWitness(tx.Signers[0].Account, tx, &tx.Scripts[0], 100000000)
 		require.NoError(t, err)
 	}
@@ -92,9 +92,9 @@ func benchmarkForEachNEP17Transfer(t *testing.B, ps storage.Store, startFromBloc
 	acc := random.Uint160()
 	from := e.Validator.ScriptHash()
 
-	for j := 0; j < chainHeight; j++ {
+	for range chainHeight {
 		b := smartcontract.NewBuilder()
-		for i := 0; i < transfersPerBlock; i++ {
+		for range transfersPerBlock {
 			b.InvokeWithAssert(gasHash, "transfer", from, acc, 1, nil)
 		}
 		script, err := b.Script()
@@ -119,7 +119,7 @@ func benchmarkForEachNEP17Transfer(t *testing.B, ps storage.Store, startFromBloc
 	t.ResetTimer()
 	t.ReportAllocs()
 	t.StartTimer()
-	for i := 0; i < t.N; i++ {
+	for range t.N {
 		require.NoError(t, bc.ForEachNEP17Transfer(acc, newestTimestamp, func(t *state.NEP17Transfer) (bool, error) {
 			if t.Timestamp < oldestTimestamp {
 				// iterating from newest to oldest, already have reached the needed height
@@ -165,7 +165,7 @@ func benchmarkGasPerVote(t *testing.B, ps storage.Store, nRewardRecords int, rew
 	voters := make([]*wallet.Account, sz)
 	candidates := make(keys.PublicKeys, sz)
 	txs := make([]*transaction.Transaction, 0, len(voters)*3)
-	for i := 0; i < sz; i++ {
+	for i := range sz {
 		priv, err := keys.NewPrivateKey()
 		require.NoError(t, err)
 		candidates[i] = priv.PublicKey()
@@ -186,7 +186,7 @@ func benchmarkGasPerVote(t *testing.B, ps storage.Store, nRewardRecords int, rew
 		e.CheckHalt(t, tx.Hash())
 	}
 	voteTxs := make([]*transaction.Transaction, 0, sz)
-	for i := 0; i < sz; i++ {
+	for i := range sz {
 		priv := voters[i].PrivateKey()
 		h := priv.GetScriptHash()
 		voteTx := e.NewTx(t, []neotest.Signer{neotest.NewSingleSigner(voters[i])}, neoHash, "vote", h, candidates[i].Bytes())
@@ -211,7 +211,7 @@ func benchmarkGasPerVote(t *testing.B, ps storage.Store, nRewardRecords int, rew
 	t.ResetTimer()
 	t.ReportAllocs()
 	t.StartTimer()
-	for i := 0; i < t.N; i++ {
+	for range t.N {
 		_, err := bc.CalculateClaimable(to, end)
 		require.NoError(t, err)
 	}
diff --git a/pkg/core/block/block_test.go b/pkg/core/block/block_test.go
index f0e708f22..5400cf923 100644
--- a/pkg/core/block/block_test.go
+++ b/pkg/core/block/block_test.go
@@ -73,7 +73,7 @@ func TestTrimmedBlock(t *testing.T) {
 
 	assert.Equal(t, block.Script, trimmedBlock.Script)
 	assert.Equal(t, len(block.Transactions), len(trimmedBlock.Transactions))
-	for i := 0; i < len(block.Transactions); i++ {
+	for i := range block.Transactions {
 		assert.Equal(t, block.Transactions[i].Hash(), trimmedBlock.Transactions[i].Hash())
 		assert.True(t, trimmedBlock.Transactions[i].Trimmed)
 	}
diff --git a/pkg/core/blockchain_core_test.go b/pkg/core/blockchain_core_test.go
index c5f24995f..4db49bfff 100644
--- a/pkg/core/blockchain_core_test.go
+++ b/pkg/core/blockchain_core_test.go
@@ -89,16 +89,16 @@ func TestRemoveOldTransfers(t *testing.T) {
 	acc3 := util.Uint160{3}
 	ttl := state.TokenTransferLog{Raw: []byte{1}} // It's incorrect, but who cares.
 
-	for i := uint32(0); i < 3; i++ {
+	for i := range uint32(3) {
 		bc.dao.PutTokenTransferLog(acc1, older, i, false, &ttl)
 	}
-	for i := uint32(0); i < 3; i++ {
+	for i := range uint32(3) {
 		bc.dao.PutTokenTransferLog(acc2, newer, i, false, &ttl)
 	}
-	for i := uint32(0); i < 2; i++ {
+	for i := range uint32(2) {
 		bc.dao.PutTokenTransferLog(acc3, older, i, true, &ttl)
 	}
-	for i := uint32(0); i < 2; i++ {
+	for i := range uint32(2) {
 		bc.dao.PutTokenTransferLog(acc3, newer, i, true, &ttl)
 	}
 
@@ -106,7 +106,7 @@ func TestRemoveOldTransfers(t *testing.T) {
 	require.NoError(t, err)
 	_ = bc.removeOldTransfers(0)
 
-	for i := uint32(0); i < 2; i++ {
+	for i := range uint32(2) {
 		log, err := bc.dao.GetTokenTransferLog(acc1, older, i, false)
 		require.NoError(t, err)
 		require.Equal(t, 0, len(log.Raw))
@@ -153,7 +153,7 @@ func TestBlockchain_InitWithIncompleteStateJump(t *testing.T) {
 	bcSpout := newTestChainWithCustomCfg(t, spountCfg)
 
 	// Generate some content.
-	for i := 0; i < len(bcSpout.GetConfig().StandbyCommittee); i++ {
+	for range bcSpout.GetConfig().StandbyCommittee {
 		require.NoError(t, bcSpout.AddBlock(bcSpout.newBlock()))
 	}
 
diff --git a/pkg/core/blockchain_neotest_test.go b/pkg/core/blockchain_neotest_test.go
index c5d5b48ee..22523882b 100644
--- a/pkg/core/blockchain_neotest_test.go
+++ b/pkg/core/blockchain_neotest_test.go
@@ -299,7 +299,7 @@ func TestBlockchain_InitializeNeoCache_Bug3181(t *testing.T) {
 	// Put some empty blocks to reach N-1 block height, so that newEpoch cached
 	// values of native Neo contract require an update on the subsequent cache
 	// initialization.
-	for i := 0; i < len(bc.GetConfig().StandbyCommittee)-1-2; i++ {
+	for range len(bc.GetConfig().StandbyCommittee) - 1 - 2 {
 		e.AddNewBlock(t)
 	}
 	bc.Close() // Ensure persist is done and persistent store is properly closed.
@@ -358,12 +358,12 @@ func TestBlockchain_InitializeNeoCache_Bug3424(t *testing.T) {
 	// voters vote for candidates.
 	voters := make([]neotest.Signer, committeeSize+1)
 	candidates := make([]neotest.Signer, committeeSize+1)
-	for i := 0; i < committeeSize+1; i++ {
+	for i := range committeeSize + 1 {
 		voters[i] = e.NewAccount(t, 10_0000_0000)
 		candidates[i] = e.NewAccount(t, 2000_0000_0000) // enough for one registration
 	}
 	txes := make([]*transaction.Transaction, 0, committeeSize*3)
-	for i := 0; i < committeeSize+1; i++ {
+	for i := range committeeSize + 1 {
 		transferTx := neoValidatorsInvoker.PrepareInvoke(t, "transfer", e.Validator.ScriptHash(), voters[i].(neotest.SingleSigner).Account().PrivateKey().GetScriptHash(), int64(committeeSize+1-i)*1000000, nil)
 		txes = append(txes, transferTx)
 		registerTx := neoValidatorsInvoker.WithSigners(candidates[i]).PrepareInvoke(t, "registerCandidate", candidates[i].(neotest.SingleSigner).Account().PublicKey().Bytes())
@@ -458,7 +458,7 @@ func TestBlockchain_InitializeNativeCacheWrtNativeActivations(t *testing.T) {
 
 	// Ensure Notary will be properly initialized and accessing Notary cache works
 	// as expected.
-	for i := 0; i < notaryEnabledHeight; i++ {
+	for i := range notaryEnabledHeight {
 		require.NotPanics(t, func() {
 			e.AddNewBlock(t)
 		}, h+uint32(i)+1)
@@ -641,7 +641,7 @@ func TestBlockchain_GetBlock(t *testing.T) {
 	blocks := e.GenerateNewBlocks(t, 10)
 	neoValidatorInvoker := e.ValidatorInvoker(e.NativeHash(t, nativenames.Neo))
 
-	for i := 0; i < len(blocks); i++ {
+	for i := range blocks {
 		block, err := bc.GetBlock(blocks[i].Hash())
 		require.NoErrorf(t, err, "can't get block %d: %s", i, err)
 		assert.Equal(t, blocks[i].Index, block.Index)
@@ -881,7 +881,7 @@ func TestBlockchain_HasBlock(t *testing.T) {
 
 	blocks := e.GenerateNewBlocks(t, 10)
 
-	for i := 0; i < len(blocks); i++ {
+	for i := range blocks {
 		assert.True(t, bc.HasBlock(blocks[i].Hash()))
 	}
 	newBlock := e.NewUnsignedBlock(t)
@@ -1008,7 +1008,7 @@ func TestBlockchain_Subscriptions(t *testing.T) {
 
 	// 3 burn events for every tx and 1 mint for primary node
 	require.True(t, len(notificationCh) >= 4)
-	for i := 0; i < 4; i++ {
+	for range 4 {
 		notif := <-notificationCh
 		require.Equal(t, nativeGASHash, notif.ScriptHash)
 	}
@@ -2327,7 +2327,7 @@ func TestBlockchain_ResetStateErrors(t *testing.T) {
 		bc, validators, committee := chain.NewMultiWithCustomConfigAndStore(t, cfg, db, false)
 		e := neotest.NewExecutor(t, bc, validators, committee)
 		go bc.Run()
-		for i := 0; i < chainHeight; i++ {
+		for range chainHeight {
 			e.AddNewBlock(t) // get some height
 		}
 		bc.Close()
diff --git a/pkg/core/dao/dao_test.go b/pkg/core/dao/dao_test.go
index 9db364118..2652c422c 100644
--- a/pkg/core/dao/dao_test.go
+++ b/pkg/core/dao/dao_test.go
@@ -388,7 +388,7 @@ func BenchmarkStoreAsTransaction(b *testing.B) {
 
 	b.ResetTimer()
 	b.ReportAllocs()
-	for n := 0; n < b.N; n++ {
+	for range b.N {
 		err := dao.StoreAsTransaction(tx, 1, aer)
 		if err != nil {
 			b.FailNow()
diff --git a/pkg/core/fee/opcode_test.go b/pkg/core/fee/opcode_test.go
index 292d95dc9..b4adf7cd8 100644
--- a/pkg/core/fee/opcode_test.go
+++ b/pkg/core/fee/opcode_test.go
@@ -13,7 +13,7 @@ func BenchmarkOpcode1(t *testing.B) {
 	// Just so that we don't always test the same opcode.
 	script := []opcode.Opcode{opcode.NOP, opcode.ADD, opcode.SYSCALL, opcode.APPEND}
 	l := len(script)
-	for n := 0; n < t.N; n++ {
+	for n := range t.N {
 		_ = Opcode(feeFactor, script[n%l])
 	}
 }
diff --git a/pkg/core/helper_test.go b/pkg/core/helper_test.go
index a2466d6e5..6d541d03f 100644
--- a/pkg/core/helper_test.go
+++ b/pkg/core/helper_test.go
@@ -117,7 +117,7 @@ func (bc *Blockchain) genBlocks(n int) ([]*block.Block, error) {
 	blocks := make([]*block.Block, n)
 	lastHash := bc.topBlock.Load().(*block.Block).Hash()
 	lastIndex := bc.topBlock.Load().(*block.Block).Index
-	for i := 0; i < n; i++ {
+	for i := range n {
 		blocks[i] = newBlock(bc.config.ProtocolConfiguration, uint32(i)+lastIndex+1, lastHash)
 		if err := bc.AddBlock(blocks[i]); err != nil {
 			return blocks, err
diff --git a/pkg/core/interop/contract/call_test.go b/pkg/core/interop/contract/call_test.go
index c1f6034f3..e32774a87 100644
--- a/pkg/core/interop/contract/call_test.go
+++ b/pkg/core/interop/contract/call_test.go
@@ -192,7 +192,7 @@ func TestSystemContractCall_Permissions(t *testing.T) {
 		e.DeployContract(t, ctrA, nil)
 
 		var hashAStr string
-		for i := 0; i < util.Uint160Size; i++ {
+		for i := range util.Uint160Size {
 			hashAStr += fmt.Sprintf("%#x", ctrA.Hash[i])
 			if i != util.Uint160Size-1 {
 				hashAStr += ", "
@@ -367,7 +367,7 @@ func TestSnapshotIsolation_Exceptions(t *testing.T) {
 	e.DeployContract(t, ctrA, nil)
 
 	var hashAStr string
-	for i := 0; i < util.Uint160Size; i++ {
+	for i := range util.Uint160Size {
 		hashAStr += fmt.Sprintf("%#x", ctrA.Hash[i])
 		if i != util.Uint160Size-1 {
 			hashAStr += ", "
@@ -599,7 +599,7 @@ func TestRET_after_FINALLY_PanicInsideVoidMethod(t *testing.T) {
 	e.DeployContract(t, ctrA, nil)
 
 	var hashAStr string
-	for i := 0; i < util.Uint160Size; i++ {
+	for i := range util.Uint160Size {
 		hashAStr += fmt.Sprintf("%#x", ctrA.Hash[i])
 		if i != util.Uint160Size-1 {
 			hashAStr += ", "
@@ -659,7 +659,7 @@ func TestRET_after_FINALLY_CallNonVoidAfterVoidMethod(t *testing.T) {
 	e.DeployContract(t, ctrA, nil)
 
 	var hashAStr string
-	for i := 0; i < util.Uint160Size; i++ {
+	for i := range util.Uint160Size {
 		hashAStr += fmt.Sprintf("%#x", ctrA.Hash[i])
 		if i != util.Uint160Size-1 {
 			hashAStr += ", "
diff --git a/pkg/core/interop/runtime/engine_test.go b/pkg/core/interop/runtime/engine_test.go
index f5a5fe79c..e1fb12a7d 100644
--- a/pkg/core/interop/runtime/engine_test.go
+++ b/pkg/core/interop/runtime/engine_test.go
@@ -3,7 +3,6 @@ package runtime
 import (
 	"encoding/json"
 	"math/big"
-	"math/rand"
 	"testing"
 
 	"github.com/nspcc-dev/neo-go/internal/random"
@@ -46,10 +45,10 @@ func TestPlatform(t *testing.T) {
 
 func TestGetTime(t *testing.T) {
 	b := block.New(false)
-	b.Timestamp = rand.Uint64()
+	b.Timestamp = 1725021259
 	ic := &interop.Context{VM: vm.New(), Block: b}
 	require.NoError(t, GetTime(ic))
-	checkStack(t, ic.VM, new(big.Int).SetUint64(b.Timestamp))
+	checkStack(t, ic.VM, new(big.Int).SetUint64(1725021259))
 }
 
 func TestGetScriptHash(t *testing.T) {
diff --git a/pkg/core/interop/runtime/util_test.go b/pkg/core/interop/runtime/util_test.go
index 7e6519d51..24c6a2740 100644
--- a/pkg/core/interop/runtime/util_test.go
+++ b/pkg/core/interop/runtime/util_test.go
@@ -84,7 +84,7 @@ func TestRuntimeGetNotifications(t *testing.T) {
 			require.Error(t, GetNotifications(ic))
 		})
 		t.Run("too many notifications", func(t *testing.T) {
-			for i := 0; i <= vm.MaxStackSize; i++ {
+			for range vm.MaxStackSize + 1 {
 				ic.Notifications = append(ic.Notifications, state.NotificationEvent{
 					ScriptHash: util.Uint160{3},
 					Name:       "Event3",
diff --git a/pkg/core/interop/storage/bench_test.go b/pkg/core/interop/storage/bench_test.go
index ee64de6b4..1305e5183 100644
--- a/pkg/core/interop/storage/bench_test.go
+++ b/pkg/core/interop/storage/bench_test.go
@@ -20,7 +20,7 @@ func BenchmarkStorageFind(b *testing.B) {
 			require.NoError(b, native.PutContractState(context.DAO, contractState))
 
 			items := make(map[string]state.StorageItem)
-			for i := 0; i < count; i++ {
+			for range count {
 				items["abc"+random.String(10)] = random.Bytes(10)
 			}
 			for k, v := range items {
@@ -33,7 +33,7 @@ func BenchmarkStorageFind(b *testing.B) {
 
 			b.ResetTimer()
 			b.ReportAllocs()
-			for i := 0; i < b.N; i++ {
+			for range b.N {
 				b.StopTimer()
 				v.Estack().PushVal(istorage.FindDefault)
 				v.Estack().PushVal("abc")
@@ -64,7 +64,7 @@ func BenchmarkStorageFindIteratorNext(b *testing.B) {
 					require.NoError(b, native.PutContractState(context.DAO, contractState))
 
 					items := make(map[string]state.StorageItem)
-					for i := 0; i < count; i++ {
+					for range count {
 						items["abc"+random.String(10)] = random.Bytes(10)
 					}
 					for k, v := range items {
@@ -76,7 +76,7 @@ func BenchmarkStorageFindIteratorNext(b *testing.B) {
 					require.NotEqual(b, 0, changes)
 					b.ReportAllocs()
 					b.ResetTimer()
-					for i := 0; i < b.N; i++ {
+					for range b.N {
 						b.StopTimer()
 						v.Estack().PushVal(istorage.FindDefault)
 						v.Estack().PushVal("abc")
@@ -88,7 +88,7 @@ func BenchmarkStorageFindIteratorNext(b *testing.B) {
 							b.FailNow()
 						}
 						res := context.VM.Estack().Pop().Item()
-						for i := 0; i < last; i++ {
+						for range last {
 							context.VM.Estack().PushVal(res)
 							b.StartTimer()
 							require.NoError(b, iterator.Next(context))
diff --git a/pkg/core/mempool/bench_test.go b/pkg/core/mempool/bench_test.go
index 2775f0837..909ad12b3 100644
--- a/pkg/core/mempool/bench_test.go
+++ b/pkg/core/mempool/bench_test.go
@@ -49,7 +49,7 @@ func BenchmarkPool(b *testing.B) {
 		b.Run(name, func(b *testing.B) {
 			p := New(poolSize, 0, false, nil)
 			b.ResetTimer()
-			for i := 0; i < b.N; i++ {
+			for range b.N {
 				for j := range txes {
 					if p.Add(txes[j], fe) != nil {
 						b.Fail()
diff --git a/pkg/core/mempool/mem_pool_test.go b/pkg/core/mempool/mem_pool_test.go
index 1bc0ead53..12767c95a 100644
--- a/pkg/core/mempool/mem_pool_test.go
+++ b/pkg/core/mempool/mem_pool_test.go
@@ -120,7 +120,7 @@ func TestOverCapacity(t *testing.T) {
 		require.True(t, slices.IsSortedFunc(mp.verifiedTxes, func(a, b item) int { return -a.Compare(b) }))
 	}
 
-	for i := 0; i < mempoolSize; i++ {
+	for i := range mempoolSize {
 		tx := transaction.New([]byte{byte(opcode.PUSH1)}, 0)
 		tx.Nonce = uint32(i)
 		tx.Signers = []transaction.Signer{{Account: acc}}
@@ -135,7 +135,7 @@ func TestOverCapacity(t *testing.T) {
 	bigScript[0] = byte(opcode.PUSH1)
 	bigScript[1] = byte(opcode.RET)
 	// Fees are also prioritized.
-	for i := 0; i < mempoolSize; i++ {
+	for range mempoolSize {
 		tx := transaction.New(bigScript, 0)
 		tx.NetworkFee = 10000
 		tx.Nonce = txcnt
@@ -176,7 +176,7 @@ func TestOverCapacity(t *testing.T) {
 	require.Equal(t, *uint256.NewInt(9*10000 + 7000), mp.fees[acc].feeSum)
 
 	// High priority always wins over low priority.
-	for i := 0; i < mempoolSize; i++ {
+	for range mempoolSize {
 		tx := transaction.New([]byte{byte(opcode.PUSH1)}, 0)
 		tx.NetworkFee = 8000
 		tx.Nonce = txcnt
@@ -203,7 +203,7 @@ func TestGetVerified(t *testing.T) {
 	mp := New(mempoolSize, 0, false, nil)
 
 	txes := make([]*transaction.Transaction, 0, mempoolSize)
-	for i := 0; i < mempoolSize; i++ {
+	for i := range mempoolSize {
 		tx := transaction.New([]byte{byte(opcode.PUSH1)}, 0)
 		tx.Nonce = uint32(i)
 		tx.Signers = []transaction.Signer{{Account: util.Uint160{1, 2, 3}}}
@@ -228,7 +228,7 @@ func TestRemoveStale(t *testing.T) {
 
 	txes1 := make([]*transaction.Transaction, 0, mempoolSize/2)
 	txes2 := make([]*transaction.Transaction, 0, mempoolSize/2)
-	for i := 0; i < mempoolSize; i++ {
+	for i := range mempoolSize {
 		tx := transaction.New([]byte{byte(opcode.PUSH1)}, 0)
 		tx.Nonce = uint32(i)
 		tx.Signers = []transaction.Signer{{Account: util.Uint160{1, 2, 3}}}
diff --git a/pkg/core/mpt/bench_test.go b/pkg/core/mpt/bench_test.go
index 7c3c00d51..2d8a04725 100644
--- a/pkg/core/mpt/bench_test.go
+++ b/pkg/core/mpt/bench_test.go
@@ -10,7 +10,7 @@ func benchmarkBytes(b *testing.B, n Node) {
 	inv := n.(interface{ invalidateCache() })
 	b.ReportAllocs()
 	b.ResetTimer()
-	for i := 0; i < b.N; i++ {
+	for range b.N {
 		inv.invalidateCache()
 		_ = n.Bytes()
 	}
diff --git a/pkg/core/mpt/trie_store_test.go b/pkg/core/mpt/trie_store_test.go
index 94194fabe..ed43a61e3 100644
--- a/pkg/core/mpt/trie_store_test.go
+++ b/pkg/core/mpt/trie_store_test.go
@@ -50,7 +50,7 @@ func TestTrieStore_TestTrieOperations(t *testing.T) {
 				return true
 			})
 			require.Equal(t, 4, len(res))
-			for i := 0; i < len(res); i++ {
+			for i := range res {
 				require.Equal(t, byte(storage.STStorage), res[i][0])
 				if i < len(res)-1 {
 					cmp := bytes.Compare(res[i], res[i+1])
diff --git a/pkg/core/native/compatibility_test.go b/pkg/core/native/compatibility_test.go
index ed1dce5b3..d28c00bcd 100644
--- a/pkg/core/native/compatibility_test.go
+++ b/pkg/core/native/compatibility_test.go
@@ -27,7 +27,7 @@ func TestNamesASCII(t *testing.T) {
 
 func isASCII(s string) bool {
 	ok := true
-	for i := 0; i < len(s); i++ {
+	for i := range s {
 		ok = ok && s[i] <= unicode.MaxASCII
 	}
 	return ok
diff --git a/pkg/core/native/native_test/ledger_test.go b/pkg/core/native/native_test/ledger_test.go
index cd65d5796..6ea620888 100644
--- a/pkg/core/native/native_test/ledger_test.go
+++ b/pkg/core/native/native_test/ledger_test.go
@@ -238,13 +238,13 @@ func TestLedger_GetTransactionSignersInteropAPI(t *testing.T) {
 		txHash  = tx.Hash().BytesBE()
 		acc     = c.Committee.ScriptHash().BytesBE()
 	)
-	for i := 0; i < util.Uint256Size; i++ {
+	for i := range util.Uint256Size {
 		hashStr += fmt.Sprintf("%#x", txHash[i])
 		if i != util.Uint256Size-1 {
 			hashStr += ", "
 		}
 	}
-	for i := 0; i < util.Uint160Size; i++ {
+	for i := range util.Uint160Size {
 		accStr += fmt.Sprintf("%#x", acc[i])
 		if i != util.Uint160Size-1 {
 			accStr += ", "
diff --git a/pkg/core/native/native_test/management_test.go b/pkg/core/native/native_test/management_test.go
index 546b7f923..daf9c3672 100644
--- a/pkg/core/native/native_test/management_test.go
+++ b/pkg/core/native/native_test/management_test.go
@@ -231,7 +231,7 @@ func TestManagement_NativeUpdate(t *testing.T) {
 	})
 
 	// Add some blocks up to the Cockatrice enabling height and check the default natives state.
-	for i := 0; i < cockatriceHeight-1; i++ {
+	for range cockatriceHeight - 1 {
 		c.AddNewBlock(t)
 		for _, name := range nativenames.All {
 			h := state.CreateNativeContractHash(name)
@@ -275,7 +275,7 @@ func TestManagement_NativeUpdate_Call(t *testing.T) {
 	})
 
 	// Invoke Cockatrice-dependant method before Cockatrice should fail.
-	for i := 0; i < cockatriceHeight-1; i++ {
+	for range cockatriceHeight - 1 {
 		c.InvokeFail(t, "at instruction 45 (SYSCALL): System.Contract.Call failed: method not found: getCommitteeAddress/0", method)
 	}
 
diff --git a/pkg/core/native/native_test/neo_test.go b/pkg/core/native/native_test/neo_test.go
index f5e336df4..1880c1556 100644
--- a/pkg/core/native/native_test/neo_test.go
+++ b/pkg/core/native/native_test/neo_test.go
@@ -131,12 +131,12 @@ func TestNEO_CommitteeEvents(t *testing.T) {
 
 	voters := make([]neotest.Signer, committeeSize)
 	candidates := make([]neotest.Signer, committeeSize)
-	for i := 0; i < committeeSize; i++ {
+	for i := range committeeSize {
 		voters[i] = e.NewAccount(t, 10_0000_0000)
 		candidates[i] = e.NewAccount(t, 2000_0000_0000) // enough for one registration
 	}
 	txes := make([]*transaction.Transaction, 0, committeeSize*3)
-	for i := 0; i < committeeSize; i++ {
+	for i := range committeeSize {
 		transferTx := neoValidatorsInvoker.PrepareInvoke(t, "transfer", e.Validator.ScriptHash(), voters[i].(neotest.SingleSigner).Account().PrivateKey().GetScriptHash(), int64(committeeSize-i)*1000000, nil)
 		txes = append(txes, transferTx)
 
@@ -196,7 +196,7 @@ func TestNEO_Vote(t *testing.T) {
 	validatorsCount := cfg.GetNumOfCNs(0)
 	freq := validatorsCount + committeeSize
 	advanceChain := func(t *testing.T) {
-		for i := 0; i < freq; i++ {
+		for range freq {
 			neoCommitteeInvoker.AddNewBlock(t)
 		}
 	}
@@ -217,13 +217,13 @@ func TestNEO_Vote(t *testing.T) {
 	// how much GAS voters receive for NEO ownership.
 	referenceAccounts := make([]neotest.Signer, committeeSize+1)
 	candidates := make([]neotest.Signer, committeeSize+1)
-	for i := 0; i < committeeSize+1; i++ {
+	for i := range committeeSize + 1 {
 		voters[i] = e.NewAccount(t, 10_0000_0000)
 		referenceAccounts[i] = e.NewAccount(t, 10_0000_0000)
 		candidates[i] = e.NewAccount(t, 2000_0000_0000) // enough for one registration
 	}
 	txes := make([]*transaction.Transaction, 0, committeeSize*4-2)
-	for i := 0; i < committeeSize+1; i++ {
+	for i := range committeeSize + 1 {
 		transferTx := neoValidatorsInvoker.PrepareInvoke(t, "transfer", e.Validator.ScriptHash(), voters[i].(neotest.SingleSigner).Account().PrivateKey().GetScriptHash(), int64(committeeSize+1-i)*1000000, nil)
 		txes = append(txes, transferTx)
 		transferTx = neoValidatorsInvoker.PrepareInvoke(t, "transfer", e.Validator.ScriptHash(), referenceAccounts[i].(neotest.SingleSigner).Account().PrivateKey().GetScriptHash(), int64(committeeSize+1-i)*1000000, nil)
@@ -419,7 +419,7 @@ func TestNEO_GetAccountState(t *testing.T) {
 	committeeSize := cfg.GetCommitteeSize(0)
 	validatorSize := cfg.GetNumOfCNs(0)
 	advanceChain := func(t *testing.T) {
-		for i := 0; i < committeeSize; i++ {
+		for range committeeSize {
 			neoValidatorInvoker.AddNewBlock(t)
 		}
 	}
@@ -485,7 +485,7 @@ func TestNEO_GetAccountStateInteropAPI(t *testing.T) {
 	committeeSize := cfg.GetCommitteeSize(0)
 	validatorSize := cfg.GetNumOfCNs(0)
 	advanceChain := func(t *testing.T) {
-		for i := 0; i < committeeSize; i++ {
+		for range committeeSize {
 			neoValidatorInvoker.AddNewBlock(t)
 		}
 	}
@@ -500,7 +500,7 @@ func TestNEO_GetAccountStateInteropAPI(t *testing.T) {
 	neoValidatorInvoker.WithSigners(acc).Invoke(t, true, "transfer", acc.ScriptHash(), acc.ScriptHash(), amount, nil)
 
 	var hashAStr string
-	for i := 0; i < util.Uint160Size; i++ {
+	for i := range util.Uint160Size {
 		hashAStr += fmt.Sprintf("%#x", acc.ScriptHash()[i])
 		if i != util.Uint160Size-1 {
 			hashAStr += ", "
@@ -544,11 +544,11 @@ func TestNEO_CommitteeBountyOnPersist(t *testing.T) {
 	const singleBounty = 50000000
 	bs := map[int]int64{0: singleBounty}
 	checkBalances := func() {
-		for i := 0; i < committeeSize; i++ {
+		for i := range committeeSize {
 			require.EqualValues(t, bs[i], e.Chain.GetUtilityTokenBalance(hs[i].GetScriptHash()).Int64(), i)
 		}
 	}
-	for i := 0; i < committeeSize*2; i++ {
+	for i := range committeeSize * 2 {
 		e.AddNewBlock(t)
 		bs[(i+1)%committeeSize] += singleBounty
 		checkBalances()
@@ -731,7 +731,7 @@ func TestNEO_CalculateBonus(t *testing.T) {
 
 	t.Run("Zero", func(t *testing.T) {
 		initialGASBalance := e.Chain.GetUtilityTokenBalance(accH)
-		for i := 0; i < rewardDistance; i++ {
+		for range rewardDistance {
 			e.AddNewBlock(t)
 		}
 		// Claim GAS, but there's no NEO on the account, so no GAS should be earned.
@@ -750,13 +750,13 @@ func TestNEO_CalculateBonus(t *testing.T) {
 
 		// Five blocks of NEO owning with default GasPerBlockValue.
 		neoValidatorsInvoker.Invoke(t, true, "transfer", e.Validator.ScriptHash(), accH, amount, nil)
-		for i := 0; i < rewardDistance/2-2; i++ {
+		for range rewardDistance/2 - 2 {
 			e.AddNewBlock(t)
 		}
 		neoCommitteeInvoker.Invoke(t, stackitem.Null{}, "setGasPerBlock", newGASPerBlock*native.GASFactor)
 
 		// Five blocks more with modified GasPerBlock value.
-		for i := 0; i < rewardDistance/2; i++ {
+		for range rewardDistance / 2 {
 			e.AddNewBlock(t)
 		}
 
@@ -783,12 +783,12 @@ func TestNEO_GetCandidates(t *testing.T) {
 	// Register a set of candidates and vote for them.
 	voters := make([]neotest.Signer, candidatesCount)
 	candidates := make([]neotest.Signer, candidatesCount)
-	for i := 0; i < candidatesCount; i++ {
+	for i := range candidatesCount {
 		voters[i] = e.NewAccount(t, 10_0000_0000)
 		candidates[i] = e.NewAccount(t, 2000_0000_0000) // enough for one registration
 	}
 	txes := make([]*transaction.Transaction, 0, candidatesCount*3)
-	for i := 0; i < candidatesCount; i++ {
+	for i := range candidatesCount {
 		transferTx := neoValidatorsInvoker.PrepareInvoke(t, "transfer", e.Validator.ScriptHash(), voters[i].(neotest.SingleSigner).Account().PrivateKey().GetScriptHash(), int64(candidatesCount+1-i)*1000000, nil)
 		txes = append(txes, transferTx)
 		registerTx := neoValidatorsInvoker.WithSigners(candidates[i]).PrepareInvoke(t, "registerCandidate", candidates[i].(neotest.SingleSigner).Account().PublicKey().Bytes())
@@ -818,7 +818,7 @@ func TestNEO_GetCandidates(t *testing.T) {
 
 	// Check that GetAllCandidates works the same way as GetCandidates.
 	checkGetAllCandidates := func(t *testing.T, expected []stackitem.Item) {
-		for i := 0; i < len(expected)+1; i++ {
+		for i := range len(expected) + 1 {
 			w := io.NewBufBinWriter()
 			emit.AppCall(w.BinWriter, neoCommitteeInvoker.Hash, "getAllCandidates", callflag.All)
 			for j := 0; j < i+1; j++ {
diff --git a/pkg/core/native/native_test/notary_test.go b/pkg/core/native/native_test/notary_test.go
index c64ff18ae..9cba3cbe0 100644
--- a/pkg/core/native/native_test/notary_test.go
+++ b/pkg/core/native/native_test/notary_test.go
@@ -183,7 +183,7 @@ func TestNotary_MaliciousWithdrawal(t *testing.T) {
 
 	// Perform several deposits to a set of different accounts.
 	count := 3
-	for i := 0; i < count; i++ {
+	for range count {
 		h := random.Uint160()
 		gasCommitteeInvoker.Invoke(t, true, "transfer", multisigHash, notaryHash, 2*feePerKey, &notary.OnNEP17PaymentData{Account: &h, Till: e.Chain.BlockHeight() + 2})
 	}
diff --git a/pkg/core/state/mpt_root_test.go b/pkg/core/state/mpt_root_test.go
index b38621497..d7bb94b6e 100644
--- a/pkg/core/state/mpt_root_test.go
+++ b/pkg/core/state/mpt_root_test.go
@@ -2,7 +2,7 @@ package state
 
 import (
 	"encoding/json"
-	"math/rand"
+	"math/rand/v2"
 	"testing"
 
 	"github.com/nspcc-dev/neo-go/internal/random"
diff --git a/pkg/core/state/native_state_test.go b/pkg/core/state/native_state_test.go
index a1b403748..ab07980bc 100644
--- a/pkg/core/state/native_state_test.go
+++ b/pkg/core/state/native_state_test.go
@@ -85,13 +85,13 @@ func BenchmarkNEP17BalanceBytes(b *testing.B) {
 
 	b.Run("stackitem", func(b *testing.B) {
 		b.ReportAllocs()
-		for i := 0; i < b.N; i++ {
+		for range b.N {
 			_, _ = stackitem.SerializeConvertible(&bl)
 		}
 	})
 	b.Run("bytes", func(b *testing.B) {
 		b.ReportAllocs()
-		for i := 0; i < b.N; i++ {
+		for range b.N {
 			_ = bl.Bytes(nil)
 		}
 	})
@@ -100,7 +100,7 @@ func BenchmarkNEP17BalanceBytes(b *testing.B) {
 
 		b.ResetTimer()
 		b.ReportAllocs()
-		for i := 0; i < b.N; i++ {
+		for range b.N {
 			_ = bl.Bytes(bs[:0])
 		}
 	})
@@ -114,13 +114,13 @@ func BenchmarkNEP17BalanceFromBytes(b *testing.B) {
 
 	b.Run("stackitem", func(b *testing.B) {
 		b.ReportAllocs()
-		for i := 0; i < b.N; i++ {
+		for range b.N {
 			_ = stackitem.DeserializeConvertible(buf, new(NEP17Balance))
 		}
 	})
 	b.Run("from bytes", func(b *testing.B) {
 		b.ReportAllocs()
-		for i := 0; i < b.N; i++ {
+		for range b.N {
 			_, _ = NEP17BalanceFromBytes(buf)
 		}
 	})
diff --git a/pkg/core/state/notification_event_test.go b/pkg/core/state/notification_event_test.go
index d61e96287..36c31dfa4 100644
--- a/pkg/core/state/notification_event_test.go
+++ b/pkg/core/state/notification_event_test.go
@@ -33,7 +33,7 @@ func BenchmarkAppExecResult_EncodeBinary(b *testing.B) {
 	w := io.NewBufBinWriter()
 	b.ReportAllocs()
 	b.ResetTimer()
-	for i := 0; i < b.N; i++ {
+	for range b.N {
 		w.Reset()
 		aer.EncodeBinary(w.BinWriter)
 	}
diff --git a/pkg/core/state/tokens_test.go b/pkg/core/state/tokens_test.go
index 9f8f78671..68e19b394 100644
--- a/pkg/core/state/tokens_test.go
+++ b/pkg/core/state/tokens_test.go
@@ -2,9 +2,8 @@ package state
 
 import (
 	"math/big"
-	"math/rand"
+	"math/rand/v2"
 	"testing"
-	"time"
 
 	"github.com/nspcc-dev/neo-go/internal/random"
 	"github.com/nspcc-dev/neo-go/internal/testserdes"
@@ -13,7 +12,7 @@ import (
 )
 
 func TestTokenTransferLog_Append17(t *testing.T) {
-	r := rand.New(rand.NewSource(time.Now().UnixNano()))
+	r := rand.New(rand.NewPCG(42, 100500))
 	expected := []*NEP17Transfer{
 		random17Transfer(r),
 		random17Transfer(r),
@@ -39,7 +38,7 @@ func TestTokenTransferLog_Append17(t *testing.T) {
 }
 
 func TestTokenTransferLog_Append(t *testing.T) {
-	r := rand.New(rand.NewSource(time.Now().UnixNano()))
+	r := rand.New(rand.NewPCG(42, 100500))
 	expected := []*NEP11Transfer{
 		random11Transfer(r),
 		random11Transfer(r),
@@ -65,7 +64,7 @@ func TestTokenTransferLog_Append(t *testing.T) {
 }
 
 func BenchmarkTokenTransferLog_Append(b *testing.B) {
-	r := rand.New(rand.NewSource(time.Now().UnixNano()))
+	r := rand.New(rand.NewPCG(42, 100500))
 	ts := make([]*NEP17Transfer, TokenTransferBatchSize)
 	for i := range ts {
 		ts[i] = random17Transfer(r)
@@ -74,7 +73,7 @@ func BenchmarkTokenTransferLog_Append(b *testing.B) {
 	lg := new(TokenTransferLog)
 	b.ResetTimer()
 	b.ReportAllocs()
-	for i := 0; i < b.N; i++ {
+	for range b.N {
 		for _, tr := range ts {
 			err := lg.Append(tr)
 			if err != nil {
diff --git a/pkg/core/statesync/mptpool_test.go b/pkg/core/statesync/mptpool_test.go
index 2d094025c..711fb5cfd 100644
--- a/pkg/core/statesync/mptpool_test.go
+++ b/pkg/core/statesync/mptpool_test.go
@@ -82,7 +82,7 @@ func TestPool_AddRemoveUpdate(t *testing.T) {
 func TestPool_GetBatch(t *testing.T) {
 	check := func(t *testing.T, limit int, itemsCount int) {
 		mp := NewPool()
-		for i := 0; i < itemsCount; i++ {
+		for range itemsCount {
 			mp.Add(random.Uint256(), []byte{0x01})
 		}
 		batch := mp.GetBatch(limit)
diff --git a/pkg/core/statesync/neotest_test.go b/pkg/core/statesync/neotest_test.go
index d69325a5c..1b93e8e3b 100644
--- a/pkg/core/statesync/neotest_test.go
+++ b/pkg/core/statesync/neotest_test.go
@@ -28,7 +28,7 @@ func TestStateSyncModule_Init(t *testing.T) {
 	}
 	bcSpout, validators, committee := chain.NewMultiWithCustomConfig(t, spoutCfg)
 	e := neotest.NewExecutor(t, bcSpout, validators, committee)
-	for i := 0; i <= 2*stateSyncInterval+int(maxTraceable)+1; i++ {
+	for range 2*stateSyncInterval + int(maxTraceable) + 2 {
 		e.AddNewBlock(t)
 	}
 
diff --git a/pkg/core/storage/memcached_store_test.go b/pkg/core/storage/memcached_store_test.go
index 497add2d8..586e5e06e 100644
--- a/pkg/core/storage/memcached_store_test.go
+++ b/pkg/core/storage/memcached_store_test.go
@@ -229,7 +229,7 @@ func benchmarkCachedSeek(t *testing.B, ps Store, psElementsCount, tsElementsCoun
 
 		ts = NewMemCachedStore(ps)
 	)
-	for i := 0; i < psElementsCount; i++ {
+	for i := range psElementsCount {
 		// lower KVs with matching prefix that should be found
 		ts.Put(append(lowerPrefixGood, random.Bytes(10)...), []byte("value"))
 		// lower KVs with non-matching prefix that shouldn't be found
@@ -266,7 +266,7 @@ func benchmarkCachedSeek(t *testing.B, ps Store, psElementsCount, tsElementsCoun
 
 	t.ReportAllocs()
 	t.ResetTimer()
-	for n := 0; n < t.N; n++ {
+	for range t.N {
 		ts.Seek(SeekRange{Prefix: searchPrefix}, func(k, v []byte) bool { return true })
 	}
 	t.StopTimer()
diff --git a/pkg/core/storage/memory_store_test.go b/pkg/core/storage/memory_store_test.go
index 544887ac1..d79003a84 100644
--- a/pkg/core/storage/memory_store_test.go
+++ b/pkg/core/storage/memory_store_test.go
@@ -21,7 +21,7 @@ func BenchmarkMemorySeek(t *testing.B) {
 				badPrefix    = []byte{2}
 			)
 			ts := NewMemCachedStore(ms)
-			for i := 0; i < count; i++ {
+			for range count {
 				ts.Put(append(searchPrefix, random.Bytes(10)...), random.Bytes(10))
 				ts.Put(append(badPrefix, random.Bytes(10)...), random.Bytes(10))
 			}
@@ -30,7 +30,7 @@ func BenchmarkMemorySeek(t *testing.B) {
 
 			t.ReportAllocs()
 			t.ResetTimer()
-			for n := 0; n < t.N; n++ {
+			for range t.N {
 				ms.Seek(SeekRange{Prefix: searchPrefix}, func(k, v []byte) bool { return false })
 			}
 		})
diff --git a/pkg/core/transaction/bench_test.go b/pkg/core/transaction/bench_test.go
index 763637680..f1de34fbe 100644
--- a/pkg/core/transaction/bench_test.go
+++ b/pkg/core/transaction/bench_test.go
@@ -32,7 +32,7 @@ func init() {
 }
 
 func BenchmarkDecodeBinary(t *testing.B) {
-	for n := 0; n < t.N; n++ {
+	for range t.N {
 		r := io.NewBinReaderFromBuf(benchTx)
 		tx := &Transaction{}
 		tx.DecodeBinary(r)
@@ -41,14 +41,14 @@ func BenchmarkDecodeBinary(t *testing.B) {
 }
 
 func BenchmarkDecodeJSON(t *testing.B) {
-	for n := 0; n < t.N; n++ {
+	for range t.N {
 		tx := &Transaction{}
 		require.NoError(t, tx.UnmarshalJSON(benchTxJSON))
 	}
 }
 
 func BenchmarkDecodeFromBytes(t *testing.B) {
-	for n := 0; n < t.N; n++ {
+	for range t.N {
 		_, err := NewTransactionFromBytes(benchTx)
 		require.NoError(t, err)
 	}
@@ -60,7 +60,7 @@ func BenchmarkTransaction_Bytes(b *testing.B) {
 
 	b.ReportAllocs()
 	b.ResetTimer()
-	for i := 0; i < b.N; i++ {
+	for range b.N {
 		_ = tx.Bytes()
 	}
 }
@@ -71,7 +71,7 @@ func BenchmarkGetVarSize(b *testing.B) {
 
 	b.ResetTimer()
 	b.ReportAllocs()
-	for i := 0; i < b.N; i++ {
+	for range b.N {
 		_ = io.GetVarSize(tx)
 	}
 }
diff --git a/pkg/core/transaction/oracle_test.go b/pkg/core/transaction/oracle_test.go
index 89c4d392e..bfb4127ab 100644
--- a/pkg/core/transaction/oracle_test.go
+++ b/pkg/core/transaction/oracle_test.go
@@ -2,7 +2,7 @@ package transaction
 
 import (
 	"encoding/json"
-	"math/rand"
+	"math/rand/v2"
 	"testing"
 
 	"github.com/nspcc-dev/neo-go/internal/testserdes"
diff --git a/pkg/core/transaction/transaction_test.go b/pkg/core/transaction/transaction_test.go
index c99f2790f..1e2171834 100644
--- a/pkg/core/transaction/transaction_test.go
+++ b/pkg/core/transaction/transaction_test.go
@@ -318,7 +318,7 @@ func BenchmarkTxHash(b *testing.B) {
 
 	// Prime cache.
 	tx.Hash()
-	for i := 0; i < b.N; i++ {
+	for range b.N {
 		_ = tx.Hash()
 	}
 }
diff --git a/pkg/core/transaction/witness_condition_test.go b/pkg/core/transaction/witness_condition_test.go
index 49fc2ad1e..ff19bf889 100644
--- a/pkg/core/transaction/witness_condition_test.go
+++ b/pkg/core/transaction/witness_condition_test.go
@@ -74,7 +74,7 @@ func TestWitnessConditionSerDes(t *testing.T) {
 	}
 	var maxSubCondAnd = &ConditionAnd{}
 	var maxSubCondOr = &ConditionAnd{}
-	for i := 0; i < maxSubitems+1; i++ {
+	for range maxSubitems + 1 {
 		*maxSubCondAnd = append(*maxSubCondAnd, (*ConditionBoolean)(&someBool))
 		*maxSubCondOr = append(*maxSubCondOr, (*ConditionBoolean)(&someBool))
 	}
diff --git a/pkg/crypto/hash/merkle_bench_test.go b/pkg/crypto/hash/merkle_bench_test.go
index 1cf442fba..e053a9873 100644
--- a/pkg/crypto/hash/merkle_bench_test.go
+++ b/pkg/crypto/hash/merkle_bench_test.go
@@ -1,37 +1,32 @@
-package hash
+package hash_test
 
 import (
-	"math/rand"
 	"testing"
-	"time"
 
+	"github.com/nspcc-dev/neo-go/internal/random"
+	"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
 	"github.com/nspcc-dev/neo-go/pkg/util"
 	"github.com/stretchr/testify/require"
 )
 
 func BenchmarkMerkle(t *testing.B) {
-	var err error
 	var hashes = make([]util.Uint256, 100000)
-	var h = make([]byte, 32)
-	r := rand.New(rand.NewSource(time.Now().UnixNano()))
 	for i := range hashes {
-		r.Read(h)
-		hashes[i], err = util.Uint256DecodeBytesBE(h)
-		require.NoError(t, err)
+		hashes[i] = random.Uint256()
 	}
 
 	t.Run("NewMerkleTree", func(t *testing.B) {
 		t.ResetTimer()
-		for n := 0; n < t.N; n++ {
-			tr, err := NewMerkleTree(hashes)
+		for range t.N {
+			tr, err := hash.NewMerkleTree(hashes)
 			require.NoError(t, err)
 			_ = tr.Root()
 		}
 	})
 	t.Run("CalcMerkleRoot", func(t *testing.B) {
 		t.ResetTimer()
-		for n := 0; n < t.N; n++ {
-			_ = CalcMerkleRoot(hashes)
+		for range t.N {
+			_ = hash.CalcMerkleRoot(hashes)
 		}
 	})
 }
diff --git a/pkg/crypto/keys/publickey_test.go b/pkg/crypto/keys/publickey_test.go
index 6fa70fd54..fa0bb4bb9 100644
--- a/pkg/crypto/keys/publickey_test.go
+++ b/pkg/crypto/keys/publickey_test.go
@@ -26,7 +26,7 @@ func TestEncodeDecodeInfinity(t *testing.T) {
 }
 
 func TestEncodeDecodePublicKey(t *testing.T) {
-	for i := 0; i < 4; i++ {
+	for range 4 {
 		k, err := NewPrivateKey()
 		require.NoError(t, err)
 		p := k.PublicKey()
@@ -225,7 +225,7 @@ func BenchmarkPublicEqual(t *testing.B) {
 	k12 := getPubKey(t)
 	k2, err := NewPublicKeyFromString("03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c")
 	require.NoError(t, err)
-	for n := 0; n < t.N; n++ {
+	for range t.N {
 		_ = k11.Equal(k12)
 		_ = k11.Equal(k2)
 	}
@@ -233,14 +233,14 @@ func BenchmarkPublicEqual(t *testing.B) {
 
 func BenchmarkPublicBytes(t *testing.B) {
 	k := getPubKey(t)
-	for n := 0; n < t.N; n++ {
+	for range t.N {
 		_ = k.Bytes()
 	}
 }
 
 func BenchmarkPublicUncompressedBytes(t *testing.B) {
 	k := getPubKey(t)
-	for n := 0; n < t.N; n++ {
+	for range t.N {
 		_ = k.Bytes()
 	}
 }
@@ -249,7 +249,7 @@ func BenchmarkPublicDecodeBytes(t *testing.B) {
 	keyBytes, err := hex.DecodeString("03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c")
 	require.NoError(t, err)
 	k := new(PublicKey)
-	for n := 0; n < t.N; n++ {
+	for range t.N {
 		require.NoError(t, k.DecodeBytes(keyBytes))
 	}
 }
diff --git a/pkg/encoding/bigint/bench_test.go b/pkg/encoding/bigint/bench_test.go
index 893be0118..aed527ddb 100644
--- a/pkg/encoding/bigint/bench_test.go
+++ b/pkg/encoding/bigint/bench_test.go
@@ -10,7 +10,7 @@ func BenchmarkToPreallocatedBytes(b *testing.B) {
 	vn := big.NewInt(-100500)
 	buf := make([]byte, 4)
 
-	for i := 0; i < b.N; i++ {
+	for range b.N {
 		_ = ToPreallocatedBytes(v, buf[:0])
 		_ = ToPreallocatedBytes(vn, buf[:0])
 	}
diff --git a/pkg/encoding/bigint/fuzz_test.go b/pkg/encoding/bigint/fuzz_test.go
index b515773a0..33ad90dff 100644
--- a/pkg/encoding/bigint/fuzz_test.go
+++ b/pkg/encoding/bigint/fuzz_test.go
@@ -13,7 +13,7 @@ func FuzzFromBytes(f *testing.F) {
 	for _, tc := range testCases {
 		f.Add(tc.buf)
 	}
-	for i := 0; i < 50; i++ {
+	for range 50 {
 		for j := 1; j < MaxBytesLen; j++ {
 			b := make([]byte, j)
 			_, err := rand.Read(b)
diff --git a/pkg/io/binaryrw_test.go b/pkg/io/binaryrw_test.go
index 40bfdb097..e159570b6 100644
--- a/pkg/io/binaryrw_test.go
+++ b/pkg/io/binaryrw_test.go
@@ -218,7 +218,7 @@ func TestBufBinWriterErr(t *testing.T) {
 
 func TestBufBinWriterReset(t *testing.T) {
 	bw := NewBufBinWriter()
-	for i := 0; i < 3; i++ {
+	for i := range 3 {
 		bw.WriteU32LE(uint32(i))
 		assert.Nil(t, bw.Err)
 		_ = bw.Bytes()
diff --git a/pkg/neotest/basic.go b/pkg/neotest/basic.go
index 1540af862..3a0de0305 100644
--- a/pkg/neotest/basic.go
+++ b/pkg/neotest/basic.go
@@ -376,7 +376,7 @@ func (e *Executor) AddNewBlock(t testing.TB, txs ...*transaction.Transaction) *b
 // GenerateNewBlocks adds the specified number of empty blocks to the chain.
 func (e *Executor) GenerateNewBlocks(t testing.TB, count int) []*block.Block {
 	blocks := make([]*block.Block, count)
-	for i := 0; i < count; i++ {
+	for i := range count {
 		blocks[i] = e.AddNewBlock(t)
 	}
 	return blocks
diff --git a/pkg/neotest/signer.go b/pkg/neotest/signer.go
index b532b461f..a4188d39d 100644
--- a/pkg/neotest/signer.go
+++ b/pkg/neotest/signer.go
@@ -133,7 +133,7 @@ func (m multiSigner) Script() []byte {
 // SignHashable implements Signer interface.
 func (m multiSigner) SignHashable(magic uint32, item hash.Hashable) []byte {
 	var script []byte
-	for i := 0; i < m.m; i++ {
+	for i := range m.m {
 		sign := m.accounts[i].SignHashable(netmode.Magic(magic), item)
 		script = append(script, byte(opcode.PUSHDATA1), keys.SignatureLen)
 		script = append(script, sign...)
diff --git a/pkg/network/discovery_test.go b/pkg/network/discovery_test.go
index ad29aed81..3e401a3f7 100644
--- a/pkg/network/discovery_test.go
+++ b/pkg/network/discovery_test.go
@@ -96,7 +96,7 @@ func TestDefaultDiscoverer(t *testing.T) {
 	// Added addresses should end up in the pool and in the unconnected set.
 	// Done twice to check re-adding unconnected addresses, which should be
 	// a no-op.
-	for i := 0; i < 2; i++ {
+	for range 2 {
 		d.BackFill(set1...)
 		assert.Equal(t, len(set1), d.PoolCount())
 		set1D := d.UnconnectedPeers()
@@ -110,7 +110,7 @@ func TestDefaultDiscoverer(t *testing.T) {
 	// Request should make goroutines dial our addresses draining the pool.
 	d.RequestRemote(len(set1))
 	dialled := make([]string, 0)
-	for i := 0; i < len(set1); i++ {
+	for range set1 {
 		select {
 		case a := <-ts.dialCh:
 			dialled = append(dialled, a)
@@ -182,8 +182,8 @@ func TestDefaultDiscoverer(t *testing.T) {
 
 	dialledBad := make([]string, 0)
 	d.RequestRemote(len(set1))
-	for i := 0; i < connRetries; i++ {
-		for j := 0; j < len(set1); j++ {
+	for i := range connRetries {
+		for j := range set1 {
 			select {
 			case a := <-ts.dialCh:
 				dialledBad = append(dialledBad, a)
@@ -194,8 +194,8 @@ func TestDefaultDiscoverer(t *testing.T) {
 	}
 	require.Eventually(t, func() bool { return d.PoolCount() == 0 }, 2*time.Second, 50*time.Millisecond)
 	slices.Sort(dialledBad)
-	for i := 0; i < len(set1); i++ {
-		for j := 0; j < connRetries; j++ {
+	for i := range set1 {
+		for j := range connRetries {
 			assert.Equal(t, set1[i], dialledBad[i*connRetries+j])
 		}
 	}
@@ -222,7 +222,7 @@ func TestSeedDiscovery(t *testing.T) {
 	tryMaxWait = 1 // Don't waste time.
 
 	d.RequestRemote(len(seeds))
-	for i := 0; i < connRetries*2; i++ {
+	for range connRetries * 2 {
 		for range seeds {
 			select {
 			case <-ts.dialCh:
diff --git a/pkg/network/fuzz_test.go b/pkg/network/fuzz_test.go
index 7a5e89d79..b60652173 100644
--- a/pkg/network/fuzz_test.go
+++ b/pkg/network/fuzz_test.go
@@ -1,18 +1,18 @@
 package network
 
 import (
-	"math/rand"
+	"math/rand/v2"
 	"testing"
 
+	"github.com/nspcc-dev/neo-go/internal/random"
 	"github.com/nspcc-dev/neo-go/pkg/io"
 	"github.com/stretchr/testify/require"
 )
 
 func FuzzMessageDecode(f *testing.F) {
-	for i := 0; i < 100; i++ {
-		seed := make([]byte, rand.Uint32()%1000)
-		//nolint:staticcheck
-		rand.Read(seed)
+	for range 100 {
+		seed := make([]byte, rand.IntN(1000))
+		random.Fill(seed)
 		f.Add(seed)
 	}
 
diff --git a/pkg/network/message_test.go b/pkg/network/message_test.go
index 57cd82d75..0b88eb4a7 100644
--- a/pkg/network/message_test.go
+++ b/pkg/network/message_test.go
@@ -2,7 +2,7 @@ package network
 
 import (
 	"errors"
-	"math/rand"
+	"math/rand/v2"
 	"testing"
 	"time"
 
@@ -79,7 +79,7 @@ func BenchmarkMessageBytes(b *testing.B) {
 
 	b.ReportAllocs()
 	b.ResetTimer()
-	for i := 0; i < b.N; i++ {
+	for range b.N {
 		_, err := msg.Bytes()
 		if err != nil {
 			b.FailNow()
diff --git a/pkg/network/payload/address_test.go b/pkg/network/payload/address_test.go
index 187ea637b..e8365111f 100644
--- a/pkg/network/payload/address_test.go
+++ b/pkg/network/payload/address_test.go
@@ -44,7 +44,7 @@ func TestEncodeDecodeAddress(t *testing.T) {
 }
 
 func fillAddressList(al *AddressList) {
-	for i := 0; i < len(al.Addrs); i++ {
+	for i := range al.Addrs {
 		e, _ := net.ResolveTCPAddr("tcp", fmt.Sprintf("127.0.0.1:20%d", i))
 		al.Addrs[i] = NewAddressAndTime(e, time.Now(), capability.Capabilities{
 			{
diff --git a/pkg/network/payload/headers_test.go b/pkg/network/payload/headers_test.go
index 5318345f3..64805087f 100644
--- a/pkg/network/payload/headers_test.go
+++ b/pkg/network/payload/headers_test.go
@@ -56,7 +56,7 @@ func testHeadersEncodeDecode(t *testing.T, headers *Headers, expected int, retEr
 	assert.Equal(t, retErr, rErr)
 	assert.Equal(t, expected, len(headersDecode.Hdrs))
 
-	for i := 0; i < len(headersDecode.Hdrs); i++ {
+	for i := range headersDecode.Hdrs {
 		assert.Equal(t, headers.Hdrs[i].Version, headersDecode.Hdrs[i].Version)
 		assert.Equal(t, headers.Hdrs[i].Index, headersDecode.Hdrs[i].Index)
 		assert.Equal(t, headers.Hdrs[i].Script, headersDecode.Hdrs[i].Script)
diff --git a/pkg/network/server_test.go b/pkg/network/server_test.go
index 9d2c6f20b..50d70d7f0 100644
--- a/pkg/network/server_test.go
+++ b/pkg/network/server_test.go
@@ -218,7 +218,6 @@ func testGetBlocksByIndex(t *testing.T, cmd CommandType) {
 	expectsCmd := make([]CommandType, 10)
 	expectedHeight := make([][]uint32, 10)
 	for i := range ps {
-		i := i
 		ps[i] = newLocalPeer(t, s)
 		ps[i].messageHandler = func(t *testing.T, msg *Message) {
 			require.Equal(t, expectsCmd[i], msg.Command)
diff --git a/pkg/rpcclient/wsclient_test.go b/pkg/rpcclient/wsclient_test.go
index e1f0dd82e..c8e5b8e6a 100644
--- a/pkg/rpcclient/wsclient_test.go
+++ b/pkg/rpcclient/wsclient_test.go
@@ -356,7 +356,7 @@ func TestWSClientNonBlockingEvents(t *testing.T) {
 	wsc.subscriptionsLock.RUnlock()
 
 	// Check that receiver was closed after overflow.
-	for i := 0; i < chCap; i++ {
+	for range chCap {
 		_, ok := <-bCh
 		require.True(t, ok)
 	}
@@ -836,7 +836,7 @@ func TestWSConcurrentAccess(t *testing.T) {
 	require.NoError(t, err)
 	batchCount := 100
 	completed := &atomic.Int32{}
-	for i := 0; i < batchCount; i++ {
+	for range batchCount {
 		go func() {
 			_, err := wsc.GetBlockCount()
 			require.NoError(t, err)
diff --git a/pkg/services/notary/core_test.go b/pkg/services/notary/core_test.go
index 57ad22526..65758da14 100644
--- a/pkg/services/notary/core_test.go
+++ b/pkg/services/notary/core_test.go
@@ -4,7 +4,7 @@ import (
 	"errors"
 	"fmt"
 	"math/big"
-	"math/rand"
+	"math/rand/v2"
 	"sync"
 	"testing"
 	"time"
@@ -305,7 +305,7 @@ func TestNotary(t *testing.T) {
 			// check that tx size was updated
 			require.Equal(t, io.GetVarSize(completedTx), completedTx.Size())
 
-			for i := 0; i < len(completedTx.Scripts)-1; i++ {
+			for i := range len(completedTx.Scripts) - 1 {
 				_, err := bc.VerifyWitness(completedTx.Signers[i].Account, completedTx, &completedTx.Scripts[i], -1)
 				require.NoError(t, err)
 			}
@@ -393,7 +393,7 @@ func TestNotary(t *testing.T) {
 
 		var submittedRequests []*payload.P2PNotaryRequest
 		// sent only nSigs (m out of n) requests - it should be enough to complete min tx
-		for i := 0; i < nSigs; i++ {
+		for i := range nSigs {
 			submittedRequests = append(submittedRequests, requests[sendOrder[i]])
 
 			ntr1.OnNewRequest(requests[sendOrder[i]])
diff --git a/pkg/services/oracle/filter_test.go b/pkg/services/oracle/filter_test.go
index 27a08b433..76341127b 100644
--- a/pkg/services/oracle/filter_test.go
+++ b/pkg/services/oracle/filter_test.go
@@ -54,7 +54,7 @@ func TestFilter(t *testing.T) {
 func TestFilterOOM(t *testing.T) {
 	construct := func(depth, width int) string {
 		data := `$`
-		for i := 0; i < depth; i++ {
+		for range depth {
 			data = data + `[0`
 			for j := 1; j < width; j++ {
 				data = data + `,0`
diff --git a/pkg/services/oracle/oracle_test.go b/pkg/services/oracle/oracle_test.go
index 28017ef33..9fd02dcbd 100644
--- a/pkg/services/oracle/oracle_test.go
+++ b/pkg/services/oracle/oracle_test.go
@@ -419,7 +419,7 @@ func TestNotYetRunningOracle(t *testing.T) {
 
 	var req state.OracleRequest
 	var reqs = make(map[uint64]*state.OracleRequest)
-	for i := uint64(0); i < 3; i++ {
+	for i := range uint64(3) {
 		reqs[i] = &req
 	}
 	orc.AddRequests(reqs) // 0, 1, 2 added to pending.
diff --git a/pkg/services/rpcsrv/client_test.go b/pkg/services/rpcsrv/client_test.go
index 017a60b2e..9e2744238 100644
--- a/pkg/services/rpcsrv/client_test.go
+++ b/pkg/services/rpcsrv/client_test.go
@@ -1455,7 +1455,7 @@ func TestClient_IteratorSessions(t *testing.T) {
 	// storageItemsCount is the amount of storage items stored in Storage contract, it's hard-coded in the contract code.
 	const storageItemsCount = 255
 	expected := make([][]byte, storageItemsCount)
-	for i := 0; i < storageItemsCount; i++ {
+	for i := range storageItemsCount {
 		expected[i] = stackitem.NewBigInteger(big.NewInt(int64(i))).Bytes()
 	}
 	slices.SortFunc(expected, func(a, b []byte) int {
@@ -1483,7 +1483,7 @@ func TestClient_IteratorSessions(t *testing.T) {
 			set, err := c.TraverseIterator(sID, iID, maxNum)
 			require.NoError(t, err)
 			require.Equal(t, maxNum, len(set))
-			for i := 0; i < maxNum; i++ {
+			for i := range maxNum {
 				// According to the Storage contract code.
 				require.Equal(t, expected[start+i], set[i].Value().([]byte), start+i)
 			}
@@ -1503,7 +1503,7 @@ func TestClient_IteratorSessions(t *testing.T) {
 
 	t.Run("traverse, request more than exists", func(t *testing.T) {
 		sID, iID := prepareSession(t)
-		for i := 0; i < storageItemsCount/config.DefaultMaxIteratorResultItems; i++ {
+		for range storageItemsCount / config.DefaultMaxIteratorResultItems {
 			set, err := c.TraverseIterator(sID, iID, config.DefaultMaxIteratorResultItems)
 			require.NoError(t, err)
 			require.Equal(t, config.DefaultMaxIteratorResultItems, len(set))
@@ -1533,7 +1533,7 @@ func TestClient_IteratorSessions(t *testing.T) {
 			assert.Equal(t, 1, len(set))
 			wg.Done()
 		}
-		for i := 0; i < storageItemsCount; i++ {
+		for range storageItemsCount {
 			go check(t)
 		}
 		wg.Wait()
@@ -1677,7 +1677,7 @@ func TestClient_Iterator_SessionConfigVariations(t *testing.T) {
 		actual, err := c.TraverseIterator(res.Session, *iterator.ID, maxNum)
 		require.NoError(t, err)
 		require.Equal(t, maxNum, len(actual))
-		for i := 0; i < maxNum; i++ {
+		for i := range maxNum {
 			// According to the Storage contract code.
 			require.Equal(t, expected[i], actual[i].Value().([]byte), i)
 		}
@@ -1696,7 +1696,7 @@ func TestClient_Iterator_SessionConfigVariations(t *testing.T) {
 
 		// Fill in expected stackitems set during the first test.
 		expected = make([][]byte, storageItemsCount)
-		for i := 0; i < storageItemsCount; i++ {
+		for i := range storageItemsCount {
 			expected[i] = stackitem.NewBigInteger(big.NewInt(int64(i))).Bytes()
 		}
 		slices.SortFunc(expected, func(a, b []byte) int {
@@ -1761,7 +1761,7 @@ func TestClient_Iterator_SessionConfigVariations(t *testing.T) {
 		require.NotEmpty(t, iterator.Values)
 		require.True(t, iterator.Truncated)
 		require.Equal(t, rpcSrv.config.MaxIteratorResultItems, len(iterator.Values))
-		for i := 0; i < rpcSrv.config.MaxIteratorResultItems; i++ {
+		for i := range rpcSrv.config.MaxIteratorResultItems {
 			// According to the Storage contract code.
 			require.Equal(t, expected[i], iterator.Values[i].Value().([]byte), i)
 		}
diff --git a/pkg/services/rpcsrv/params/types_test.go b/pkg/services/rpcsrv/params/types_test.go
index d4bab190b..2f2123298 100644
--- a/pkg/services/rpcsrv/params/types_test.go
+++ b/pkg/services/rpcsrv/params/types_test.go
@@ -20,7 +20,7 @@ func BenchmarkUnmarshal(b *testing.B) {
 		b.Run("unmarshal", func(b *testing.B) {
 			b.ReportAllocs()
 			b.ResetTimer()
-			for i := 0; i < b.N; i++ {
+			for range b.N {
 				b.StopTimer()
 				in := new(In)
 				b.StartTimer()
@@ -33,7 +33,7 @@ func BenchmarkUnmarshal(b *testing.B) {
 		b.Run("decode data", func(b *testing.B) {
 			b.ReportAllocs()
 			b.ResetTimer()
-			for i := 0; i < b.N; i++ {
+			for range b.N {
 				b.StopTimer()
 				r := NewRequest()
 				r.In = new(In)
diff --git a/pkg/services/rpcsrv/server_helper_test.go b/pkg/services/rpcsrv/server_helper_test.go
index 3595bacd6..35ef5ae49 100644
--- a/pkg/services/rpcsrv/server_helper_test.go
+++ b/pkg/services/rpcsrv/server_helper_test.go
@@ -101,7 +101,7 @@ func getTestBlocks(t *testing.T) []*block.Block {
 	nBlocks := br.ReadU32LE()
 	require.Nil(t, br.Err)
 	blocks := make([]*block.Block, 0, int(nBlocks))
-	for i := 0; i < int(nBlocks); i++ {
+	for range nBlocks {
 		_ = br.ReadU32LE()
 		b := block.New(false)
 		b.DecodeBinary(br)
diff --git a/pkg/services/rpcsrv/server_test.go b/pkg/services/rpcsrv/server_test.go
index db3e04f1d..51bf37e68 100644
--- a/pkg/services/rpcsrv/server_test.go
+++ b/pkg/services/rpcsrv/server_test.go
@@ -2974,7 +2974,7 @@ func testRPCProtocol(t *testing.T, doRPCCall func(string, string, *testing.T) []
 		for _, tx := range mp.GetVerifiedTransactions() {
 			expected = append(expected, tx.Hash())
 		}
-		for i := 0; i < 5; i++ {
+		for range 5 {
 			tx := transaction.New([]byte{byte(opcode.PUSH1)}, 0)
 			tx.Signers = []transaction.Signer{{Account: util.Uint160{1, 2, 3}}}
 			assert.NoError(t, mp.Add(tx, &FeerStub{}))
@@ -4138,7 +4138,7 @@ func BenchmarkHandleIn(b *testing.B) {
 	do := func(b *testing.B, req []byte) {
 		b.ReportAllocs()
 		b.ResetTimer()
-		for i := 0; i < b.N; i++ {
+		for range b.N {
 			b.StopTimer()
 			in := new(params.In)
 			b.StartTimer()
diff --git a/pkg/services/rpcsrv/subscription_test.go b/pkg/services/rpcsrv/subscription_test.go
index 0017740c3..7466f18e7 100644
--- a/pkg/services/rpcsrv/subscription_test.go
+++ b/pkg/services/rpcsrv/subscription_test.go
@@ -148,7 +148,7 @@ func TestSubscriptions(t *testing.T) {
 				break
 			}
 		}
-		for i := 0; i < len(b.Transactions); i++ {
+		for i := range b.Transactions {
 			if i > 0 {
 				resp = getNotification(t, respMsgs)
 			}
@@ -460,7 +460,7 @@ func TestFilteredBlockSubscriptions(t *testing.T) {
 	blockSubID := callSubscribe(t, c, respMsgs, `["block_added", {"primary":3}]`)
 
 	var expectedCnt int
-	for i := 0; i < numBlocks; i++ {
+	for i := range numBlocks {
 		primary := uint32(i % 4)
 		if primary == 3 {
 			expectedCnt++
@@ -469,7 +469,7 @@ func TestFilteredBlockSubscriptions(t *testing.T) {
 		require.NoError(t, chain.AddBlock(b))
 	}
 
-	for i := 0; i < expectedCnt; i++ {
+	for range expectedCnt {
 		var resp = new(neorpc.Notification)
 		select {
 		case body := <-respMsgs:
@@ -493,7 +493,7 @@ func TestHeaderOfAddedBlockSubscriptions(t *testing.T) {
 	headerSubID := callSubscribe(t, c, respMsgs, `["header_of_added_block", {"primary":3}]`)
 
 	var expectedCnt int
-	for i := 0; i < numBlocks; i++ {
+	for i := range numBlocks {
 		primary := uint32(i % 4)
 		if primary == 3 {
 			expectedCnt++
@@ -502,7 +502,7 @@ func TestHeaderOfAddedBlockSubscriptions(t *testing.T) {
 		require.NoError(t, chain.AddBlock(b))
 	}
 
-	for i := 0; i < expectedCnt; i++ {
+	for range expectedCnt {
 		var resp = new(neorpc.Notification)
 		select {
 		case body := <-respMsgs:
@@ -523,7 +523,7 @@ func TestMaxSubscriptions(t *testing.T) {
 	var subIDs = make([]string, 0)
 	_, _, c, respMsgs := initCleanServerAndWSClient(t)
 
-	for i := 0; i < maxFeeds+1; i++ {
+	for i := range maxFeeds + 1 {
 		var s string
 		resp := callWSGetRaw(t, c, `{"jsonrpc": "2.0", "method": "subscribe", "params": ["block_added"], "id": 1}`, respMsgs)
 		if i < maxFeeds {
@@ -606,7 +606,7 @@ func TestWSClientsLimit(t *testing.T) {
 			var wg sync.WaitGroup
 
 			// Dial effectiveClients connections in parallel
-			for i := 0; i < effectiveClients; i++ {
+			for i := range effectiveClients {
 				wg.Add(1)
 				j := i
 				go func() {
@@ -658,11 +658,11 @@ func TestSubscriptionOverflow(t *testing.T) {
 	require.NotNil(t, resp.Result)
 
 	// Push a lot of new blocks, but don't read events for them.
-	for i := 0; i < blockCnt; i++ {
+	for range blockCnt {
 		b := testchain.NewBlock(t, chain, 1, 0)
 		require.NoError(t, chain.AddBlock(b))
 	}
-	for i := 0; i < blockCnt; i++ {
+	for range blockCnt {
 		resp := getNotification(t, respMsgs)
 		if resp.Event != neorpc.BlockEventID {
 			require.Equal(t, neorpc.MissedEventID, resp.Event)
diff --git a/pkg/services/stateroot/service_test.go b/pkg/services/stateroot/service_test.go
index 224837423..56f6c953f 100644
--- a/pkg/services/stateroot/service_test.go
+++ b/pkg/services/stateroot/service_test.go
@@ -39,7 +39,7 @@ import (
 func testSignStateRoot(t *testing.T, r *state.MPTRoot, pubs keys.PublicKeys, accs ...*wallet.Account) []byte {
 	n := smartcontract.GetMajorityHonestNodeCount(len(accs))
 	w := io.NewBufBinWriter()
-	for i := 0; i < n; i++ {
+	for i := range n {
 		sig := accs[i].PrivateKey().SignHashable(uint32(netmode.UnitTestNet), r)
 		emit.Bytes(w.BinWriter, sig)
 	}
@@ -323,7 +323,7 @@ func TestStateroot_GetLatestStateHeight(t *testing.T) {
 	basicchain.Init(t, "../../../", e)
 
 	m := bc.GetStateModule()
-	for i := uint32(0); i < bc.BlockHeight(); i++ {
+	for i := range bc.BlockHeight() {
 		r, err := m.GetStateRoot(i)
 		require.NoError(t, err)
 		h, err := bc.GetStateModule().GetLatestStateHeight(r.Root)
diff --git a/pkg/smartcontract/contract_test.go b/pkg/smartcontract/contract_test.go
index 8ff51deb0..6d02d5564 100644
--- a/pkg/smartcontract/contract_test.go
+++ b/pkg/smartcontract/contract_test.go
@@ -24,7 +24,7 @@ func TestCreateMultiSigRedeemScript(t *testing.T) {
 	br := io.NewBinReaderFromBuf(out)
 	assert.Equal(t, opcode.PUSH3, opcode.Opcode(br.ReadB()))
 
-	for i := 0; i < len(validators); i++ {
+	for i := range validators {
 		assert.EqualValues(t, opcode.PUSHDATA1, br.ReadB())
 		bb := br.ReadVarBytes()
 		require.NoError(t, br.Err)
@@ -61,13 +61,13 @@ func TestCreateDefaultMultiSigRedeemScript(t *testing.T) {
 	checkM(2)
 
 	// 3 out of 4
-	for i := 0; i < 2; i++ {
+	for range 2 {
 		addKey()
 	}
 	checkM(3)
 
 	// 5 out of 6
-	for i := 0; i < 2; i++ {
+	for range 2 {
 		addKey()
 	}
 	checkM(5)
@@ -77,7 +77,7 @@ func TestCreateDefaultMultiSigRedeemScript(t *testing.T) {
 	checkM(5)
 
 	// 7 out of 10
-	for i := 0; i < 3; i++ {
+	for range 3 {
 		addKey()
 	}
 	checkM(7)
diff --git a/pkg/smartcontract/manifest/manifest_test.go b/pkg/smartcontract/manifest/manifest_test.go
index 308829e0e..0357c9a10 100644
--- a/pkg/smartcontract/manifest/manifest_test.go
+++ b/pkg/smartcontract/manifest/manifest_test.go
@@ -4,6 +4,7 @@ import (
 	"encoding/json"
 	"fmt"
 	"math/big"
+	"slices"
 	"testing"
 
 	"github.com/nspcc-dev/neo-go/internal/random"
@@ -272,7 +273,7 @@ func TestIsValid(t *testing.T) {
 	m.Groups = m.Groups[:0]
 
 	t.Run("invalid, unserializable", func(t *testing.T) {
-		for i := 0; i < stackitem.MaxSerialized; i++ {
+		for i := range stackitem.MaxSerialized {
 			m.ABI.Events = append(m.ABI.Events, Event{
 				Name:       fmt.Sprintf("Event%d", i),
 				Parameters: []Parameter{},
@@ -382,19 +383,19 @@ func TestManifest_FromStackItemErrors(t *testing.T) {
 	errCases := map[string]stackitem.Item{
 		"not a struct":                     stackitem.NewArray([]stackitem.Item{}),
 		"invalid length":                   stackitem.NewStruct([]stackitem.Item{}),
-		"invalid name type":                stackitem.NewStruct(append([]stackitem.Item{stackitem.NewInterop(nil)}, goodSI[1:]...)),
-		"invalid Groups type":              stackitem.NewStruct(append([]stackitem.Item{name, stackitem.Null{}}, goodSI[2:]...)),
-		"invalid group":                    stackitem.NewStruct(append([]stackitem.Item{name, stackitem.NewArray([]stackitem.Item{stackitem.Null{}})}, goodSI[2:]...)),
-		"invalid Features type":            stackitem.NewStruct(append([]stackitem.Item{name, groups, stackitem.Null{}}, goodSI[3:]...)),
-		"invalid supported standards type": stackitem.NewStruct(append([]stackitem.Item{name, groups, features, stackitem.Null{}}, goodSI[4:]...)),
-		"invalid supported standard":       stackitem.NewStruct(append([]stackitem.Item{name, groups, features, stackitem.NewArray([]stackitem.Item{stackitem.Null{}})}, goodSI[4:]...)),
-		"invalid ABI":                      stackitem.NewStruct(append([]stackitem.Item{name, groups, features, sStandards, stackitem.Null{}}, goodSI[5:]...)),
-		"invalid Permissions type":         stackitem.NewStruct(append([]stackitem.Item{name, groups, features, sStandards, abi, stackitem.Null{}}, goodSI[6:]...)),
-		"invalid permission":               stackitem.NewStruct(append([]stackitem.Item{name, groups, features, sStandards, abi, stackitem.NewArray([]stackitem.Item{stackitem.Null{}})}, goodSI[6:]...)),
-		"invalid Trusts type":              stackitem.NewStruct(append([]stackitem.Item{name, groups, features, sStandards, abi, permissions, stackitem.NewInterop(nil)}, goodSI[7:]...)),
-		"invalid trust":                    stackitem.NewStruct(append([]stackitem.Item{name, groups, features, sStandards, abi, permissions, stackitem.NewArray([]stackitem.Item{stackitem.NewInterop(nil)})}, goodSI[7:]...)),
-		"invalid Uint160 trust":            stackitem.NewStruct(append([]stackitem.Item{name, groups, features, sStandards, abi, permissions, stackitem.NewArray([]stackitem.Item{stackitem.NewByteArray([]byte{1, 2, 3})})}, goodSI[7:]...)),
-		"invalid extra type":               stackitem.NewStruct([]stackitem.Item{name, groups, features, sStandards, abi, permissions, trusts, stackitem.Null{}}),
+		"invalid name type":                stackitem.NewStruct(slices.Concat([]stackitem.Item{stackitem.NewInterop(nil)}, goodSI[1:])),
+		"invalid Groups type":              stackitem.NewStruct(slices.Concat(goodSI[:1], []stackitem.Item{stackitem.Null{}}, goodSI[2:])),
+		"invalid group":                    stackitem.NewStruct(slices.Concat(goodSI[:1], []stackitem.Item{stackitem.NewArray([]stackitem.Item{stackitem.Null{}})}, goodSI[2:])),
+		"invalid Features type":            stackitem.NewStruct(slices.Concat(goodSI[:2], []stackitem.Item{stackitem.Null{}}, goodSI[3:])),
+		"invalid supported standards type": stackitem.NewStruct(slices.Concat(goodSI[:3], []stackitem.Item{stackitem.Null{}}, goodSI[4:])),
+		"invalid supported standard":       stackitem.NewStruct(slices.Concat(goodSI[:3], []stackitem.Item{stackitem.NewArray([]stackitem.Item{stackitem.Null{}})}, goodSI[4:])),
+		"invalid ABI":                      stackitem.NewStruct(slices.Concat(goodSI[:4], []stackitem.Item{stackitem.Null{}}, goodSI[5:])),
+		"invalid Permissions type":         stackitem.NewStruct(slices.Concat(goodSI[:5], []stackitem.Item{stackitem.Null{}}, goodSI[6:])),
+		"invalid permission":               stackitem.NewStruct(slices.Concat(goodSI[:5], []stackitem.Item{stackitem.NewArray([]stackitem.Item{stackitem.Null{}})}, goodSI[6:])),
+		"invalid Trusts type":              stackitem.NewStruct(slices.Concat(goodSI[:6], []stackitem.Item{stackitem.NewInterop(nil)}, goodSI[7:])),
+		"invalid trust":                    stackitem.NewStruct(slices.Concat(goodSI[:6], []stackitem.Item{stackitem.NewArray([]stackitem.Item{stackitem.NewInterop(nil)})}, goodSI[7:])),
+		"invalid Uint160 trust":            stackitem.NewStruct(slices.Concat(goodSI[:6], []stackitem.Item{stackitem.NewArray([]stackitem.Item{stackitem.NewByteArray([]byte{1, 2, 3})})}, goodSI[7:])),
+		"invalid extra type":               stackitem.NewStruct(slices.Concat(goodSI[:7], []stackitem.Item{stackitem.Null{}})),
 	}
 	for name, errCase := range errCases {
 		t.Run(name, func(t *testing.T) {
diff --git a/pkg/util/bench_test.go b/pkg/util/bench_test.go
index 916d5b41e..df8281276 100644
--- a/pkg/util/bench_test.go
+++ b/pkg/util/bench_test.go
@@ -7,7 +7,7 @@ import (
 func BenchmarkUint256MarshalJSON(b *testing.B) {
 	v := Uint256{0x01, 0x02, 0x03}
 
-	for i := 0; i < b.N; i++ {
+	for range b.N {
 		_, _ = v.MarshalJSON()
 	}
 }
diff --git a/pkg/util/uint256_test.go b/pkg/util/uint256_test.go
index c4e943240..9fd184739 100644
--- a/pkg/util/uint256_test.go
+++ b/pkg/util/uint256_test.go
@@ -101,7 +101,7 @@ func BenchmarkUint256DecodeStringLE(b *testing.B) {
 
 	b.ResetTimer()
 	b.ReportAllocs()
-	for i := 0; i < b.N; i++ {
+	for range b.N {
 		_, err := util.Uint256DecodeStringLE(a)
 		if err != nil {
 			b.FailNow()
diff --git a/pkg/vm/bench_test.go b/pkg/vm/bench_test.go
index d1ad21f24..1df58b6fd 100644
--- a/pkg/vm/bench_test.go
+++ b/pkg/vm/bench_test.go
@@ -10,7 +10,7 @@ import (
 )
 
 func benchScript(t *testing.B, script []byte) {
-	for n := 0; n < t.N; n++ {
+	for range t.N {
 		t.StopTimer()
 		vm := load(script)
 		t.StartTimer()
@@ -40,7 +40,7 @@ func BenchmarkScriptPushPop(t *testing.B) {
 	for _, i := range []int{4, 16, 128, 1024} {
 		t.Run(strconv.Itoa(i), func(t *testing.B) {
 			var script = make([]byte, i*2)
-			for p := 0; p < i; p++ {
+			for p := range i {
 				script[p] = byte(opcode.PUSH1)
 				script[i+p] = byte(opcode.DROP)
 			}
@@ -53,7 +53,7 @@ func BenchmarkIsSignatureContract(t *testing.B) {
 	b64script := "DCED2eixa9myLTNF1tTN4xvhw+HRYVMuPQzOy5Xs4utYM25BVuezJw=="
 	script, err := base64.StdEncoding.DecodeString(b64script)
 	require.NoError(t, err)
-	for n := 0; n < t.N; n++ {
+	for range t.N {
 		_ = IsSignatureContract(script)
 	}
 }
diff --git a/pkg/vm/contract_checks_test.go b/pkg/vm/contract_checks_test.go
index 3a3007131..fcbd18640 100644
--- a/pkg/vm/contract_checks_test.go
+++ b/pkg/vm/contract_checks_test.go
@@ -65,7 +65,7 @@ func TestIsSignatureContract(t *testing.T) {
 
 func testMultisigContract(t *testing.T, n, m int) []byte {
 	pubs := make(keys.PublicKeys, n)
-	for i := 0; i < n; i++ {
+	for i := range n {
 		priv, err := keys.NewPrivateKey()
 		require.NoError(t, err)
 		pubs[i] = priv.PublicKey()
diff --git a/pkg/vm/opcode/isvalid_test.go b/pkg/vm/opcode/isvalid_test.go
index 2361c4ee8..700220fa2 100644
--- a/pkg/vm/opcode/isvalid_test.go
+++ b/pkg/vm/opcode/isvalid_test.go
@@ -9,7 +9,7 @@ func BenchmarkIsValid(t *testing.B) {
 	// Just so that we don't always test the same opcode.
 	script := []Opcode{NOP, ADD, SYSCALL, APPEND, 0xff, 0xf0}
 	l := len(script)
-	for n := 0; n < t.N; n++ {
+	for n := range t.N {
 		_ = IsValid(script[n%l])
 	}
 }
diff --git a/pkg/vm/opcodebench_test.go b/pkg/vm/opcodebench_test.go
index e64405e78..abd43f891 100644
--- a/pkg/vm/opcodebench_test.go
+++ b/pkg/vm/opcodebench_test.go
@@ -12,7 +12,7 @@ import (
 
 func benchOpcodeInt(t *testing.B, f func() *VM, fail bool) {
 	t.ResetTimer()
-	for n := 0; n < t.N; n++ {
+	for range t.N {
 		t.StopTimer()
 		v := f()
 		t.StartTimer()
@@ -82,11 +82,11 @@ func exceptParamPushVM(op opcode.Opcode, param []byte, ilen int, elen int, excep
 		if ilen != 0 {
 			eCtx := newExceptionHandlingContext(1, 2)
 			v.Context().tryStack.PushVal(eCtx)
-			for i := 0; i < ilen; i++ {
+			for range ilen {
 				v.call(v.Context(), 0)
 			}
 		} else if elen != 0 {
-			for i := 0; i < elen; i++ {
+			for range elen {
 				eCtx := newExceptionHandlingContext(1, 2)
 				v.Context().tryStack.PushVal(eCtx)
 			}
@@ -146,7 +146,7 @@ func arrayOfIfaces(size int) []any {
 
 func bigMap() *stackitem.Map {
 	var m = stackitem.NewMap()
-	for i := 0; i < 1024; i++ {
+	for i := range 1024 {
 		m.Add(stackitem.Make(i), stackitem.Make(i))
 	}
 	return m
diff --git a/pkg/vm/ref_counter_test.go b/pkg/vm/ref_counter_test.go
index 8da020671..2c2b3752d 100644
--- a/pkg/vm/ref_counter_test.go
+++ b/pkg/vm/ref_counter_test.go
@@ -51,7 +51,7 @@ func BenchmarkRefCounter_Add(b *testing.B) {
 	rc := newRefCounter()
 
 	b.ResetTimer()
-	for i := 0; i < b.N; i++ {
+	for range b.N {
 		rc.Add(a)
 	}
 }
@@ -61,7 +61,7 @@ func BenchmarkRefCounter_AddRemove(b *testing.B) {
 	rc := newRefCounter()
 
 	b.ResetTimer()
-	for i := 0; i < b.N; i++ {
+	for range b.N {
 		rc.Add(a)
 		rc.Remove(a)
 	}
diff --git a/pkg/vm/stack_test.go b/pkg/vm/stack_test.go
index e596a5eef..8a18cf50b 100644
--- a/pkg/vm/stack_test.go
+++ b/pkg/vm/stack_test.go
@@ -18,7 +18,7 @@ func TestPushElement(t *testing.T) {
 
 	assert.Equal(t, len(elems), s.Len())
 
-	for i := 0; i < len(elems); i++ {
+	for i := range elems {
 		assert.Equal(t, elems[len(elems)-1-i], s.Peek(i))
 	}
 }
@@ -386,7 +386,7 @@ func TestPopSigElements(t *testing.T) {
 
 func makeElements(n int) []Element {
 	elems := make([]Element, n)
-	for i := 0; i < n; i++ {
+	for i := range n {
 		elems[i] = NewElement(i)
 	}
 	return elems
diff --git a/pkg/vm/stackitem/item_test.go b/pkg/vm/stackitem/item_test.go
index afdfee213..66a0afe07 100644
--- a/pkg/vm/stackitem/item_test.go
+++ b/pkg/vm/stackitem/item_test.go
@@ -411,13 +411,13 @@ func TestEqualsDeepStructure(t *testing.T) {
 	const perStruct = 4
 	var items = []Item{}
 	var num int
-	for i := 0; i < perStruct; i++ {
+	for range perStruct {
 		items = append(items, Make(0))
 		num++
 	}
 	var layerUp = func(sa *Struct, num int) (*Struct, int) {
 		items := []Item{}
-		for i := 0; i < perStruct; i++ {
+		for range perStruct {
 			clon, err := sa.Clone()
 			require.NoError(t, err)
 			items = append(items, clon)
@@ -427,7 +427,7 @@ func TestEqualsDeepStructure(t *testing.T) {
 		return NewStruct(items), num
 	}
 	var sa = NewStruct(items)
-	for i := 0; i < 4; i++ {
+	for range 4 {
 		sa, num = layerUp(sa, num)
 	}
 	require.Less(t, num, MaxComparableNumOfItems)
@@ -528,7 +528,7 @@ func TestNewVeryBigInteger(t *testing.T) {
 func TestStructClone(t *testing.T) {
 	st0 := Struct{}
 	st := Struct{value: []Item{&st0}}
-	for i := 0; i < MaxClonableNumOfItems-1; i++ {
+	for range MaxClonableNumOfItems - 1 {
 		nst, err := st.Clone()
 		require.NoError(t, err)
 		st = Struct{value: []Item{nst}}
diff --git a/pkg/vm/stackitem/json_test.go b/pkg/vm/stackitem/json_test.go
index b30a2a9f9..b8e691257 100644
--- a/pkg/vm/stackitem/json_test.go
+++ b/pkg/vm/stackitem/json_test.go
@@ -208,7 +208,7 @@ func TestToJSONCornerCases(t *testing.T) {
 // getBigArray returns array takes up a lot of storage when serialized.
 func getBigArray(depth int) *Array {
 	arr := NewArray([]Item{})
-	for i := 0; i < depth; i++ {
+	for range depth {
 		arr = NewArray([]Item{arr, arr})
 	}
 	return arr
@@ -219,7 +219,7 @@ func BenchmarkToJSON(b *testing.B) {
 
 	b.ResetTimer()
 	b.ReportAllocs()
-	for i := 0; i < b.N; i++ {
+	for range b.N {
 		_, err := ToJSON(arr)
 		if err != nil {
 			b.FailNow()
diff --git a/pkg/vm/stackitem/serialization_test.go b/pkg/vm/stackitem/serialization_test.go
index 4c9fc3a92..358136636 100644
--- a/pkg/vm/stackitem/serialization_test.go
+++ b/pkg/vm/stackitem/serialization_test.go
@@ -65,7 +65,7 @@ func TestSerialize(t *testing.T) {
 		testSerialize(t, ErrRecursive, arr)
 
 		items := make([]Item, 0, MaxDeserialized-1)
-		for i := 0; i < MaxDeserialized-1; i++ {
+		for range MaxDeserialized - 1 {
 			items = append(items, zeroByteArray)
 		}
 		testSerialize(t, nil, newItem(items))
@@ -172,12 +172,12 @@ func TestSerialize(t *testing.T) {
 		testSerialize(t, ErrTooBig, m)
 
 		m = NewMap()
-		for i := 0; i < MaxDeserialized/2-1; i++ {
+		for i := range MaxDeserialized/2 - 1 {
 			m.Add(Make(i), zeroByteArray)
 		}
 		testSerialize(t, nil, m)
 
-		for i := 0; i <= MaxDeserialized; i++ {
+		for i := range MaxDeserialized + 1 {
 			m.Add(Make(i), zeroByteArray)
 		}
 		_, err := Serialize(m)
@@ -265,7 +265,7 @@ func TestMapDeserializationError(t *testing.T) {
 
 func TestDeserializeTooManyElements(t *testing.T) {
 	item := Make(0)
-	for i := 0; i < MaxDeserialized-1; i++ { // 1 for zero inner element.
+	for range MaxDeserialized - 1 { // 1 for zero inner element.
 		item = Make([]Item{item})
 	}
 	data, err := Serialize(item)
@@ -283,7 +283,7 @@ func TestDeserializeTooManyElements(t *testing.T) {
 func TestDeserializeLimited(t *testing.T) {
 	customLimit := MaxDeserialized + 1
 	item := Make(0)
-	for i := 0; i < customLimit-1; i++ { // 1 for zero inner element.
+	for range customLimit - 1 { // 1 for zero inner element.
 		item = Make([]Item{item})
 	}
 	data, err := SerializeLimited(item, customLimit) // tiny hack to avoid serialization error.
@@ -307,7 +307,7 @@ func BenchmarkEncodeBinary(b *testing.B) {
 
 	b.ResetTimer()
 	b.ReportAllocs()
-	for i := 0; i < b.N; i++ {
+	for range b.N {
 		w.Reset()
 		EncodeBinary(arr, w.BinWriter)
 		if w.Err != nil {
@@ -320,7 +320,7 @@ func BenchmarkSerializeSimple(b *testing.B) {
 	s := NewStruct(nil)
 	s.Append(Make(100500))
 	s.Append(Make("1aada0032aba1ef6d1f0")) // Mimicking uint160.
-	for i := 0; i < b.N; i++ {
+	for range b.N {
 		_, err := Serialize(s)
 		if err != nil {
 			b.FailNow()
diff --git a/pkg/vm/vm_test.go b/pkg/vm/vm_test.go
index e82b78eca..b8dc08b8f 100644
--- a/pkg/vm/vm_test.go
+++ b/pkg/vm/vm_test.go
@@ -8,7 +8,7 @@ import (
 	"fmt"
 	"math"
 	"math/big"
-	"math/rand"
+	"math/rand/v2"
 	"strings"
 	"testing"
 
@@ -144,7 +144,7 @@ func checkVMFailed(t *testing.T, vm *VM) {
 
 func TestStackLimitPUSH1Good(t *testing.T) {
 	prog := make([]byte, MaxStackSize*2)
-	for i := 0; i < MaxStackSize; i++ {
+	for i := range MaxStackSize {
 		prog[i] = byte(opcode.PUSH1)
 	}
 	for i := MaxStackSize; i < MaxStackSize*2; i++ {
@@ -165,7 +165,7 @@ func TestStackLimitPUSH1Bad(t *testing.T) {
 }
 
 func TestPUSHINT(t *testing.T) {
-	for i := byte(0); i < 5; i++ {
+	for i := range byte(5) {
 		op := opcode.PUSHINT8 + opcode.Opcode(i)
 		t.Run(op.String(), func(t *testing.T) {
 			buf := random.Bytes((8 << i) / 8)
@@ -356,7 +356,7 @@ func TestCONVERT(t *testing.T) {
 // which equals to size*2+3 elements in total.
 func appendBigStruct(size uint16) []opcode.Opcode {
 	prog := make([]opcode.Opcode, size*2)
-	for i := uint16(0); i < size; i++ {
+	for i := range size {
 		prog[i*2] = opcode.PUSH0
 		prog[i*2+1] = opcode.NEWSTRUCT
 	}
@@ -594,7 +594,6 @@ func TestJMPs(t *testing.T) {
 	}
 
 	for i, tc := range testCases {
-		i := i
 		t.Run(tc.name, func(t *testing.T) {
 			for op := opcode.JMP; op < opcode.JMPLEL; op++ {
 				resOp := op
@@ -945,7 +944,7 @@ func makeRETProgram(t *testing.T, argCount, localCount int) []byte {
 	require.True(t, argCount+localCount <= 255)
 
 	fProg := []opcode.Opcode{opcode.INITSLOT, opcode.Opcode(localCount), opcode.Opcode(argCount)}
-	for i := 0; i < localCount; i++ {
+	for i := range localCount {
 		fProg = append(fProg, opcode.PUSH8, opcode.STLOC, opcode.Opcode(i))
 	}
 	fProg = append(fProg, opcode.RET)
@@ -968,7 +967,7 @@ func makeRETProgram(t *testing.T, argCount, localCount int) []byte {
 	for i := range args {
 		args[i] = opcode.PUSH7
 	}
-	for i := 0; i < callCount; i++ {
+	for range callCount {
 		ops = append(ops, args...)
 		ops = append(ops, opcode.LDSFLD0, opcode.CALLA)
 	}
@@ -1407,7 +1406,7 @@ func TestSETITEMMap(t *testing.T) {
 func TestSETITEMBigMapBad(t *testing.T) {
 	prog := makeProgram(opcode.SETITEM)
 	m := stackitem.NewMap()
-	for i := 0; i < MaxStackSize; i++ {
+	for i := range MaxStackSize {
 		m.Add(stackitem.Make(i), stackitem.Make(i))
 	}
 
@@ -1439,7 +1438,7 @@ func TestSETITEMBigMapGood(t *testing.T) {
 	vm := load(prog)
 
 	m := stackitem.NewMap()
-	for i := 0; i < MaxStackSize-3; i++ {
+	for i := range MaxStackSize - 3 {
 		m.Add(stackitem.Make(i), stackitem.Make(i))
 	}
 	vm.estack.Push(Element{value: m})
@@ -1714,7 +1713,7 @@ func TestROLLGood(t *testing.T) {
 func getCheckEStackFunc(items ...any) func(t *testing.T, v *VM) {
 	return func(t *testing.T, v *VM) {
 		require.Equal(t, len(items), v.estack.Len())
-		for i := 0; i < len(items); i++ {
+		for i := range items {
 			assert.Equal(t, stackitem.Make(items[i]), v.estack.Peek(i).Item())
 		}
 	}
@@ -1985,7 +1984,7 @@ func TestPACKGood(t *testing.T) {
 			assert.Equal(t, 2, vm.estack.Len())
 			a := vm.estack.Peek(0).Array()
 			assert.Equal(t, len(elements), len(a))
-			for i := 0; i < len(elements); i++ {
+			for i := range elements {
 				e := a[i].Value().(*big.Int)
 				assert.Equal(t, int64(elements[i]), e.Int64())
 			}
@@ -2009,7 +2008,7 @@ func TestPACK_UNPACK_MaxSize(t *testing.T) {
 	assert.Equal(t, 1+1+len(elements), int(vm.refs))
 	assert.Equal(t, 1+1+len(elements), vm.estack.Len()) // canary + length + elements
 	assert.Equal(t, int64(len(elements)), vm.estack.Peek(0).Value().(*big.Int).Int64())
-	for i := 0; i < len(elements); i++ {
+	for i := range elements {
 		e, ok := vm.estack.Peek(i + 1).Value().(*big.Int)
 		assert.True(t, ok)
 		assert.Equal(t, int64(elements[i]), e.Int64())
@@ -2033,7 +2032,7 @@ func TestPACK_UNPACK_PACK_MaxSize(t *testing.T) {
 	assert.Equal(t, 2, vm.estack.Len())
 	a := vm.estack.Peek(0).Array()
 	assert.Equal(t, len(elements), len(a))
-	for i := 0; i < len(elements); i++ {
+	for i := range elements {
 		e := a[i].Value().(*big.Int)
 		assert.Equal(t, int64(elements[i]), e.Int64())
 	}
@@ -2058,7 +2057,7 @@ func TestPACKMAP_UNPACK_PACKMAP_MaxSize(t *testing.T) {
 	assert.Equal(t, 2, vm.estack.Len())
 	m := vm.estack.Peek(0).value.(*stackitem.Map).Value().([]stackitem.MapElement)
 	assert.Equal(t, len(elements), len(m))
-	for i := 0; i < len(elements); i++ {
+	for i := range elements {
 		k := m[i].Key.Value().(*big.Int)
 		v := m[i].Value.Value().(*big.Int)
 		assert.Equal(t, int64(elements[i]), k.Int64())
@@ -2823,7 +2822,7 @@ func runWithTrace(t *testing.T, prog []byte) ([]util.Uint160, []int, []opcode.Op
 
 func makeProgram(opcodes ...opcode.Opcode) []byte {
 	prog := make([]byte, len(opcodes)+1) // RET
-	for i := 0; i < len(opcodes); i++ {
+	for i := range opcodes {
 		prog[i] = byte(opcodes[i])
 	}
 	prog[len(prog)-1] = byte(opcode.RET)
@@ -2842,7 +2841,7 @@ func randomBytes(n int) []byte {
 	const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
 	b := make([]byte, n)
 	for i := range b {
-		b[i] = charset[rand.Intn(len(charset))]
+		b[i] = charset[rand.IntN(len(charset))]
 	}
 	return b
 }