Skip to main content

vyupgrade

A compiler-backed tool for upgrading Vyper contracts across language versions. It rewrites legacy syntax to a chosen target compiler, then proves the rewrite is safe by compiling the source and the result and comparing their ABI, method identifiers, and storage layout.

It covers installable Vyper 0.1.0b* prereleases through 0.4.3, plus opt-in 0.5.0a1 through 0.5.0a3 alpha targets. Rules are version-gated: a given rewrite only fires when the migration from the source version to the target version actually crosses the compiler release that introduced the change.

Install

Run it once without installing:

uvx vyupgrade contracts/

Or install it as a tool:

uv tool install vyupgrade
vyupgrade contracts/

Usage

Preview the changes as a unified diff:

vyupgrade contracts/ --diff

Apply them in place and write a machine-readable report:

vyupgrade contracts/ --write --report-json vyupgrade-report.json

Fail without writing when files would change, for use in CI:

vyupgrade contracts/ --check

The target defaults to 0.4.3; pass --target-version to migrate to a different release, including an explicit alpha target such as 0.5.0a3.

Paths may be files or directories; directories are searched recursively for .vy and .vyi sources. The source version is inferred per file from its #pragma version (or legacy # @version) line. Pass --source-version to override the inference for files that have no pragma.

For broad source pragmas, rule gating uses the oldest satisfying compiler so historical migrations still run, while source validation uses the newest satisfying compiler no newer than the requested target.

How it validates

For each file, vyupgrade compiles the original under its source compiler and the rewritten output under the target compiler, then compares the two artifacts. A migration is only written back when every file still compiles under the target, the source validation succeeded, every required artifact is available, and ABI, method identifiers, and storage layout compare equal. This write decision is independent of diagnostic selection and rule version gating. Standalone .vyi inputs are target-compiled through a generated import harness.

Compiler subprocesses run through the bundled uv, using uv run --no-project --with vyper==<version> so each side gets the exact compiler it needs instead of inheriting an incompatible interpreter. When a file belongs to another project, the nearest pyproject.toml is read and any declared packages matching its Vyper imports (such as snekmate) are added to the compiler environment.

For 0.1.0b* source compilers, vyupgrade runs the compiler through a typed-ast compatibility wrapper so the legacy compiler sees pre-Python-3.8 AST node classes without requiring a local Python 3.6 or 3.7 interpreter. When an old compiler cannot produce a modern validation output format, that format is dropped and reported as unavailable. Writes then remain blocked unless the source-validation gap is explicitly accepted with --allow-unvalidated-source. Target compilers must produce every requested validation output.

The target compiler receives the exact migrated source bytes. Historical normalization is limited to copied dependencies in the temporary validation overlay and is not applied to files that would be written. Optional --format mamushi runs only against temporary staged candidates. The formatted bytes are read back into the migration plan, compiled again under the target compiler, and compared before any destination is changed. Formatter failure leaves every original untouched.

Writes recheck all planned inputs, reject generated symlinks and unsafe hard-linked or read-only replacements, and roll back already replaced files when a later write fails. Multi-file replacement is rollback-aware rather than globally atomic; a non-cooperating external writer can still race the final portable filesystem check. Reports distinguish original, validated candidate, and final on-disk hashes. If a post-write test command changes a planned file, the run exits nonzero and records the drift.

Dependency inference is intentionally conservative. Exact requirements, ordinary version ranges, and Git dependencies are supported. Project-specific specifier syntaxes that cannot be translated to a compiler environment, such as Poetry caret requirements, are skipped; use --compiler-search-paths, --source-vyper, or --target-vyper for unusual layouts.

Options

  • --target-version — target Vyper version or spec (default 0.4.3).
  • --source-version — override the per-file inferred source version.
  • --diff — print a unified diff instead of the report.
  • --write — apply changes in place only after the validation decision passes.
  • --check — exit non-zero if any file would change; write nothing.
  • --aggressive — enable rewrites that change behavior or are not provably safe (e.g. enumflag).
  • --split-interfaces — move top-level interface blocks into sibling .vyi files and import them.
  • --select / --ignore — comma-separated rule codes to include or exclude.
  • --report-json PATH — write a JSON report of fixes, diagnostics, and validation results.
  • --format mamushi — format staged candidates, then revalidate the exact output before writing.
  • --test-command CMD — run a test command after a successful write, record its result, and fail when it does not pass.
  • --enable-decimals — treat decimals as enabled when reasoning about 0.4.x rules.
  • --source-vyper / --target-vyper — pin the exact compiler version for each side.
  • --source-python / --target-python — pin the Python interpreter for each compiler subprocess.
  • --compiler-search-paths — extra import search paths for the compiler.
  • --allow-unvalidated-source — write despite a failed source compile or unavailable source artifacts.
  • --allow-abi-change — write despite an ABI comparison mismatch.
  • --allow-method-id-change — write despite a method-identifier comparison mismatch.
  • --allow-storage-layout-change — write despite a storage-layout comparison mismatch.
  • --config PATH — read configuration from a specific pyproject.toml.

JSON reports include a top-level schema_version. Version 1 preserves the existing report envelope and field paths; new fields may be added compatibly. Consumers should treat a missing version as the legacy unversioned format and require a new schema version before relying on renamed, removed, or type-changed fields.

Configuration

Defaults can live in pyproject.toml under [tool.vyupgrade]. Command-line flags take precedence.

[tool.vyupgrade]
paths = ["contracts/"]
target-version = "0.4.3"
source-version = "infer"
report-json = "vyupgrade-report.json"
aggressive = false
split-interfaces = false
format = "none"
allow-unvalidated-source = false
allow-abi-change = false
allow-method-id-change = false
allow-storage-layout-change = false

Exit codes

  • 0 — success.
  • 1--check found files that would change.
  • 2 — target compilation or required target artifacts failed validation.
  • 3 — source compilation or source artifact availability failed validation.
  • 4 — usage error (no paths, or conflicting flags).
  • 5 — an error-severity diagnostic was raised.
  • 6 — the requested formatter failed or could not be run.
  • 7 — an unwaived ABI, method-identifier, or storage-layout mismatch blocked the write.
  • 8 — the post-write test command failed, timed out, or could not start.
  • 9 — migration planning or the rollback-aware write transaction failed.

Coverage

Rewrites carry a VY### code and diagnostics a VYD### code. Where the source intent cannot be proven safe, the change is reported as a manual-review diagnostic instead of being applied.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

vyupgrade-0.5.1.tar.gz (117.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

vyupgrade-0.5.1-py3-none-any.whl (141.4 kB view details)

Uploaded Python 3

File details

Details for the file vyupgrade-0.5.1.tar.gz.

File metadata

  • Download URL: vyupgrade-0.5.1.tar.gz
  • Upload date:
  • Size: 117.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for vyupgrade-0.5.1.tar.gz
Algorithm Hash digest
SHA256 88f96ce4d384eba0dd9d639d058b54a9f0ca5a6ce6191199c7a03e036fa346a6
MD5 bfee3a7a7c1cf10300298adbedb118ec
BLAKE2b-256 b2fa3286f079220b846bf540abedf4e069fac13b6f55cc91195eb532434db9b2

See more details on using hashes here.

Provenance

The following attestation bundles were made for vyupgrade-0.5.1.tar.gz:

Publisher: publish.yml on vyperlang/vyupgrade

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file vyupgrade-0.5.1-py3-none-any.whl.

File metadata

  • Download URL: vyupgrade-0.5.1-py3-none-any.whl
  • Upload date:
  • Size: 141.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for vyupgrade-0.5.1-py3-none-any.whl
Algorithm Hash digest
SHA256 77a0b43ddb5b0ae93200f740bcd29d368b091fef248d8d7c4582688118ba372f
MD5 4d2a9b3c235c054b3269ec74c5941c55
BLAKE2b-256 83eae9af86227d79aa0312ea56d2cff0267cfe285e422c88087e6692eac3ff97

See more details on using hashes here.

Provenance

The following attestation bundles were made for vyupgrade-0.5.1-py3-none-any.whl:

Publisher: publish.yml on vyperlang/vyupgrade

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page