ERC-2678 Smart Contract Packaging Standard v3 (Revised Ethereum Smart Contract Packaging Standard (EthPM v3))

Final · Other · created 2020-05-26

Defines a format that holds a smart contract's source, compiler settings and deployment information in a single JSON document (the package manifest). It lets tools create, install and verify packages in a common format.

Why it is needed

A smart contract can be reused or verified only when its source code, compiler version and settings, compilation output and deployed addresses all match. Without a common format for holding this information, each development tool records packages in its own way, and a package built with one tool is hard to use in another. Also, if the file differs slightly each time it is saved even with the same content, the address changes in content addressable storage such as IPFS, so identity cannot be guaranteed. The previous version's format scattered compiler information in duplicate across contracts and did not match the metadata output produced by the Solidity compiler, which made it cumbersome for tools to handle. This standard fixes the manifest to be compatible with compiler output and sets strict serialization rules, providing a format that package management tools can use in common.

In depth

Serialization rules and content address consistency

The manifest is a single JSON object and must be compacted with no line breaks or extra whitespace. The keys of every object must be sorted alphabetically, and duplicate keys within the same object are invalid. The encoding must be UTF-8, and there must be no line break at the end of the document. For backward compatibility, manifest_version is prohibited as a top-level key. The reason for fixing the serialization this way is that packages are distributed on content addressable filesystems such as IPFS. It guarantees that the same set of contract assets always resolves to the same content address URI. Custom fields may be included, but they must carry an x- prefix to avoid name collisions with future versions.

Top-level field structure

The manifest field is required and its value must be ethpm/3. name must start with a lowercase letter, may use only lowercase letters, digits and dashes, must match the regular expression ^[a-z][-a-z0-9]{0,255}$, and cannot exceed 255 characters. version is recommended to follow semver rules, and name and version are each required if the other is present. meta holds authors, license (SPDX format recommended), description, keywords and links (website, documentation and repository keys recommended). In addition, the fields sources, contractTypes, compilers, deployments and buildDependencies are defined, and all are optional.

sources and the Source object

sources must contain the full source tree needed to recompile the contracts in this release. Each source must include either content (inline source) or urls (an array of URLs that resolve to the same file). If both are present, content must match the file contents at urls. If there is no content and the URL has no content hash either, checksum (algorithm and hash) is required. installPath must be a relative path starting with ./, cannot contain ../ to prevent access outside the source folder, and must be unique across all sources. type is recommended to be one of solidity, vyper, abi-json and solidity-ast-json, and a per-file license takes precedence over the license in the package meta.

contractTypes and deployments

A contract type object may have contractName, sourceId, deploymentBytecode, runtimeBytecode, abi, userdoc, and devdoc. The rule is that a package must include only types that come from its own sources, and must not include types from dependencies or abstract contracts. The keys of deployments are BIP122 URI chain definitions, and duplicate keys that point to the same blockchain are not allowed. Each contract instance requires contractType and address. contractType is either an alias in the package itself or a <package-name>:<contract-alias> reference to a dependency package, and address must match the regular expression ^0x[0-9a-fA-F]{40}$. transaction and block are optional, and if an instance has runtimeBytecode it replaces the one from the type.

Link References and Link Values

Slots that are filled in at deployment time, such as library addresses, are marked with a Link Reference. A link reference has an offsets array of start positions within the bytecode (counted from 0), a byte length, and an optional name, and it is invalid if it runs past the end of the bytecode or overlaps another. The values actually filled in are recorded as Link Value objects in linkDependencies. If type is literal, value is hex bytes with a 0x prefix. If it is reference, value is a contract instance name, and an instance inside the dependency tree is referenced in the format <p1>:<p2>:...:<pn>:<contract-instance>. Two link values cannot share the same offsets, and each link value must have a corresponding link reference. The length of the resolved value must equal the length of the link reference.

The compilers Array and buildDependencies

Compiler information goes in the top-level compilers array. Each object requires name and version. The version must follow semver format without OS information, or the <semver>-<commit-hash> format such as 0.4.8-commit.60cc1668 for nightly builds. settings holds the compilation settings and, for solc, must follow the official compiler input-output JSON description. The contractTypes array states which types were generated by this compiler. A type that declares runtimeBytecode must be attributed to some compiler object, and a single type cannot be attributed to more than one compiler. buildDependencies is the list of dependency packages. Its keys must be valid package names, and its values must be content-addressed URIs that resolve to a package with the same manifest version.

What Changed in v2 and Backwards Compatibility

v3 changes the manifest schema to be compatible with the compiler's metadata output, and widens the sources definition so it can hold more kinds of source file formats and be used as the compiler's JSON input. It moves the compiler definition into the top-level compilers array, which simplifies the links between compiler version, sources, and output and removes duplicated information, and it unifies key notation from snake_case to camelCase. The version identifier field changed from manifest_version to manifest. A v3 package must define manifest: ethpm/3 at the top level, and the manifest_version key is forbidden. Chain identification keeps using the BIP122 URI scheme used since v1. This proposal is a standard finalized with Final status.

Things to watch out for

Original ERC · ← ERCs