diff --git a/go.mod b/go.mod
index 91ce2782224..4c3ccc4d5ce 100644
--- a/go.mod
+++ b/go.mod
@@ -2,6 +2,8 @@ module github.com/ava-labs/libevm
go 1.20
+retract v1.13.14-0.1.0-rc.1 // bad semver format ("0-rc" grouping) considered > .rc-2
+
require (
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.2.0
github.com/Microsoft/go-winio v0.6.1
diff --git a/params/version.libevm.go b/params/version.libevm.go
new file mode 100644
index 00000000000..463e6f7420d
--- /dev/null
+++ b/params/version.libevm.go
@@ -0,0 +1,93 @@
+// Copyright 2024 the libevm authors.
+//
+// The libevm additions to go-ethereum are free software: you can redistribute
+// them and/or modify them under the terms of the GNU Lesser General Public License
+// as published by the Free Software Foundation, either version 3 of the License,
+// or (at your option) any later version.
+//
+// The libevm additions are distributed in the hope that they will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
+// General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see
+// .
+
+package params
+
+import "fmt"
+
+const (
+ LibEVMVersionMajor = 0
+ LibEVMVersionMinor = 1
+ LibEVMVersionPatch = 0
+
+ libEVMReleaseType releaseType = betaRelease
+ libEVMReleaseCandidate uint = 0 // ignored unless [libEVMReleaseType] == [releaseCandidate]
+)
+
+// LibEVMVersion holds the textual version string of `libevm` modifications.
+//
+// Although compliant with [semver v2], it follows additional rules:
+//
+// 1. Major, minor, and patch MUST be the respective `geth` values;
+// 2. The first three pre-release identifiers MUST be a semver-compliant
+// triplet denoting the `libevm` "version";
+// 3. On the `main` (development) branch, the final identifier MUST be "alpha"
+// or "beta";
+// 3. If a production version, the final identifier MUST be "release"; and
+// 4. If a release candidate, the final two identifiers MUST be "rc" and an
+// incrementing numeric value.
+//
+// The benefits of this pattern are that (a) it captures all relevant
+// information; and (b) it follows an intuitive ordering under semver rules.
+// Precedence is determined first by the `geth` version then the `libevm`
+// version, with release candidates being lower than actual releases.
+//
+// The primary drawbacks is that it requires an explicit "release" identifier
+// because of the use of pre-release identifiers to capture the `libevm`
+// triplet.
+//
+// [semver v2]: https://semver.org/
+var LibEVMVersion = func() string {
+ v := libEVMSemver{
+ geth: semverTriplet{VersionMajor, VersionMinor, VersionPatch},
+ libEVM: semverTriplet{LibEVMVersionMajor, LibEVMVersionMinor, LibEVMVersionPatch},
+ typ: libEVMReleaseType,
+ rc: libEVMReleaseCandidate,
+ }
+ return v.String()
+}()
+
+type semverTriplet struct {
+ major, minor, patch uint
+}
+
+func (t semverTriplet) String() string {
+ return fmt.Sprintf("%d.%d.%d", t.major, t.minor, t.patch)
+}
+
+type releaseType string
+
+const (
+ // betaRelease MUST be used on `main` branch
+ betaRelease = releaseType("beta")
+ // Reserved for `release/*` branches
+ releaseCandidate = releaseType("rc")
+ productionRelease = releaseType("release")
+)
+
+type libEVMSemver struct {
+ geth, libEVM semverTriplet
+ typ releaseType
+ rc uint
+}
+
+func (v libEVMSemver) String() string {
+ suffix := v.typ
+ if suffix == releaseCandidate {
+ suffix = releaseType(fmt.Sprintf("%s.%d", suffix, v.rc))
+ }
+ return fmt.Sprintf("%s-%s.%s", v.geth, v.libEVM, suffix)
+}
diff --git a/params/version.libevm_test.go b/params/version.libevm_test.go
new file mode 100644
index 00000000000..b8a1d66e241
--- /dev/null
+++ b/params/version.libevm_test.go
@@ -0,0 +1,86 @@
+// Copyright 2024 the libevm authors.
+//
+// The libevm additions to go-ethereum are free software: you can redistribute
+// them and/or modify them under the terms of the GNU Lesser General Public License
+// as published by the Free Software Foundation, either version 3 of the License,
+// or (at your option) any later version.
+//
+// The libevm additions are distributed in the hope that they will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
+// General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see
+// .
+
+package params
+
+import (
+ "testing"
+
+ "golang.org/x/mod/semver"
+)
+
+func TestLibEVMVersioning(t *testing.T) {
+ // We have an unusual version structure as defined by [LibEVMVersion] that
+ // is easy to mess up, so it's easier to just automate it and test the
+ // ordering assumptions.
+
+ // This is a deliberate change-detector test to provide us with a copyable
+ // string of the current version, useful for git tagging.
+ const curr = "1.13.14-0.1.0.beta"
+ if got, want := LibEVMVersion, curr; got != want {
+ t.Errorf("got LibEVMVersion %q; want %q", got, want)
+ }
+
+ ordered := []libEVMSemver{
+ {
+ semverTriplet{1, 13, 14},
+ semverTriplet{0, 1, 0},
+ betaRelease,
+ 0, // ignored
+ },
+ {
+ semverTriplet{1, 13, 14},
+ semverTriplet{0, 1, 0},
+ releaseCandidate, 1,
+ },
+ {
+ semverTriplet{1, 13, 14},
+ semverTriplet{0, 1, 0},
+ releaseCandidate, 2,
+ },
+ {
+ semverTriplet{1, 13, 14},
+ semverTriplet{0, 1, 0},
+ productionRelease,
+ 0, // ignored,
+ },
+ {
+ semverTriplet{1, 13, 14},
+ semverTriplet{0, 1, 1}, // bump takes precedence
+ betaRelease, 0,
+ },
+ {
+ semverTriplet{1, 13, 14},
+ semverTriplet{0, 1, 1},
+ productionRelease, 0,
+ },
+ {
+ semverTriplet{1, 13, 15}, // bump takes precedence
+ semverTriplet{0, 1, 1},
+ betaRelease, 0,
+ },
+ }
+
+ for i, low := range ordered[:len(ordered)-1] {
+ // The `go mod` semver package requires the "v" prefix, which
+ // technically isn't valid semver.
+ lo := "v" + low.String()
+ hi := "v" + ordered[i+1].String()
+ if got := semver.Compare(lo, hi); got != -1 {
+ t.Errorf("Version pattern is not strictly ordered; semver.Compare(%q, %q) = %d", lo, hi, got)
+ }
+ }
+}