vysort
Make vyper 0.3.4–0.3.7 bytecode deterministic.
These compiler versions emit nondeterministic bytecode for any contract whose call graph contains a decision point — a function calling ≥2 internal functions defined later in the file (vyper#3369). The internal-function sections get permuted per environment (and per run on linux), which blocks byte-exact verification: the verifier's recompile may never reproduce what the deployer's machine happened to emit.
vysort fixes this with no compiler modifications: it decodes the deployed layout straight off the on-chain bytecode, then reorders the source so internal function defs come first, in that exact order. The topsort then has zero decision points and a stock compiler produces the deployed bytecode everywhere, every run.
Install
uv tool install vysort
Or run from a checkout: uv run vysort ...
vysort itself runs on any modern python and depends only on uv. The
vyper-touching work runs in an ephemeral uv run environment with the
matching compiler: the vyper version is auto-detected from the source's
version pragma (override with --vyper), on python 3.10 by default
(override with --python). No old python or vyper install needed.
Verify your contract
If your vyper 0.3.x contract fails verification, this is the command:
vysort verify contract.vy --address 0x2cced4ff... --rpc-url https://eth.drpc.org
It fetches the deployed code and chain id from the RPC, recovers the deployed internal-function layout from the on-chain bytes, rewrites the source to force that layout, confirms the exact standard-json payload reproduces the runtime byte-for-byte (a preflight compile through vyper's own std-json entry point — the same path the verifier's binary takes), and submits it to sourcify's v2 API with a stock compiler version. No forks, no patched binaries, no special verifier support.
Use --dry-run to inspect the submission payload without sending it,
--creation-tx to help the creation match, --sourcify-url to target
another server, and -o to keep the rewritten source.
Note: creation matches are only guaranteed when __init__ calls ≤1 internal
function; the init-callee section of creation code is not forced by source
order. Runtime matches are always forceable.
Match without submitting
To recover the layout and prove the match locally — against on-chain code or a hex file — without involving a verifier:
vysort match contract.vy --address 0x2cced4ff... --rpc-url https://eth.drpc.org -o matched.vy
vysort match contract.vy --runtime runtime.hex -o matched.vy
--runtime expects deployed runtime bytecode (eth_getCode, cast code, or
Vyper's -f bytecode_runtime). If you accidentally pass Vyper -f bytecode
creation bytecode, vysort reports the embedded runtime offset instead of
falling into layout brute force.
The deployed layout is recovered in 2 compiles regardless of contract size:
one instrumented compile maps each internal function's section boundaries and
masks the layout-dependent address bytes, the deployed order is then decoded
straight off the on-chain bytes, and one reordered stock compile verifies it
byte-exactly — exact, or prefix when the deployed code carries an appended
immutable tail. If the decode hits an edge case, reachable layouts are
brute-forced one compile at a time as a fallback. For unaffected compiler
versions a single compile-and-compare runs instead. --evm-version istanbul
helps pre-berlin deployments whose nonreentrant lock constants differ.
The matched source written by -o is ordinary vyper that any stock compiler
of that version turns into the deployed bytecode — auxdata contains no source
hash, so the output is byte-identical to what the original source produces
under that ordering.
Developer curiosities
The remaining subcommands expose the machinery.
Analyze a contract for ordering nondeterminism:
vysort check contract.vy
{
"internal_fns": 2,
"decision_points": 1,
"reachable_layouts": 2,
"immune": false,
"env_layout": ["_triple", "_double"],
"layouts": [["_double", "_triple"], ["_triple", "_double"]]
}
immune: true means exactly one layout is reachable — the contract was never
at risk; this covers 93% of affected-band mainnet contracts. Otherwise
layouts (when small) enumerates every layout the deployer's heap could have
produced. The check is version-aware: sources targeting compilers outside the
affected 0.3.4–0.3.7 band short-circuit to immune: true without compiling.
Force an arbitrary layout by rewriting the source:
vysort reorder contract.vy _double,_triple -o forced.vy
vysort reorder contract.vy layout.json > forced.vy
The layout is a comma-separated list of internal function names or a JSON
file (["_double", "_triple"]). This is the forcing primitive match and
verify are built on.
Metadata
Release files for vysort 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| vysort-0.1.1.tar.gz | 13.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| vysort-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 30.7 kB
Release files / vysort-0.1.1.tar.gz
| Download URL | vysort-0.1.1.tar.gz |
|---|---|
| Size | 13.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
2f27d3a95b47ced288df5de9beffa479bd53267fc3266c94947043541811da8d
|
|
BLAKE2b-256 checksum How to use checksums |
3950bbe5ec8c2439015f4ee913a28a4eee2793b244555fa330d39b06ac47de22
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.11.20 {"installer":{"name":"uv","version":"0.11.20","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / vysort-0.1.1-py3-none-any.whl
| Download URL | vysort-0.1.1-py3-none-any.whl |
|---|---|
| Size | 17.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
cfe7df852b3cc1fb61f0c9b8e1aa38732093e8ad9371d4f229d5bc48857c0e0e
|
|
BLAKE2b-256 checksum How to use checksums |
c13b50b9ea669f071f18579264c77c1c90eecc6df96b67d6cb98110dad1894d7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.11.20 {"installer":{"name":"uv","version":"0.11.20","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|