npm package for a smart contract: typing, ABI generation, and CI/CD

Contract, type, and environment versions diverge — and checks break for no reason. We automate the turnkey assembly of an npm package with a smart contract: types, schemas, and the update procedure are verified automatically. This reduces release preparation time and eliminates discrepancies between environments and the production network.

Blockchain Development Services

Frequently Asked Questions

Latest works

  • Development of a web application for FEEDME
    Development of a web application for FEEDME
    1376
  • Development of an online store for the company FURNORO
    Development of an online store for the company FURNORO
    1350
  • B2B Advance company logo design
    B2B Advance company logo design
    798
  • Development of a web application for Enviok
    Development of a web application for Enviok
    1088
  • AIDER company logo development
    AIDER company logo development
    1034
  • CRM development for Chasseurs
    CRM development for Chasseurs
    1130

Why an npm package with a smart contract diverges from the network

The contract is deployed, the npm package with the smart contract is published, checks pass. Then the contract is redeployed: a required argument is added to the constructor, the order of parameters in a method changes, an event is renamed.

The package remains the same — it contains an outdated interface description and an address that no longer matches the deployed code.

The build still passes because the compiler checks calls against the description inside the package, not against the chain.

The contract type mismatch does not manifest until the first real call: the code references the ABI from the package, while the network executes what was deployed.

Data is encoded according to the old schema, and the transaction reverts — or, worse, succeeds and writes something other than what the developer intended.

A contract call error surfaces in the production network and at the most inconvenient moment: at peak load, in an already approved scenario, in a call that previously worked reliably.

After that comes investigation, manual artifact reconciliation, and the question of why “the build passes but the call fails.”

The root cause is that the package lives a separate life from the network: addresses, types, and descriptions are updated manually and from memory.

Let us break down how this is closed — by generation from build artifacts, binding addresses to a specific network, and checks that stop the pipeline before publication.

What typing an npm package with a smart contract provides

Typing contract calls is built on one principle: the npm package is assembled from the same sources as the contract itself.

Each method is matched with a signature containing argument and return value types, and the interface description is produced by generation at build time — the ABI is no longer edited manually.

In TypeScript terms, this is an ordinary module with declared types: a call with shuffled argument order simply does not compile.

Next, type checking at build time comes into play.

A mismatch between the contract code and the interface description breaks the package build, not the transaction on the network: the error is visible on the developer’s machine, before sending and before writing to the blockchain.

The package version is tightly bound to the contract version — it is technically impossible to ship a client built against an outdated interface.

Reducing build time is helped by an artifact cache: when one contract is edited, only the affected units are rebuilt, and the rest is taken from cache.

A full build takes minutes, an incremental one takes tens of seconds, and the difference is noticeable when the package is rebuilt on every change in the repository.

This eliminates an entire class of failures — those caused by manual synchronization of the interface description and code. Based on our implementations, 8-9 out of 10 mismatches are caught by type checking before launch, not on the network.

Build options: manual upload versus autogeneration

Building a package with types and ABI is a choice between manually uploading artifacts and autogenerating from compilation results.

The difference is almost invisible on one contract, but it shows up by the third release and the second network: the manual scheme quietly diverges from what is actually deployed, and the package consumer learns about it from a call error.

Option When it fits Limitations
Manual artifact upload one or two contracts, infrequent releases, one-off integrations the version in the package diverges from the one deployed on the network, there is no change history, and the error is caught on the consumer side
Autogeneration of types from compilation results several contracts, frequent releases, several consumer teams a fixed set of output files is required; changing signatures breaks the consumer’s build without warning
Autogeneration with a manual gate before publication several networks, external consumers, preparation for audit address reconciliation and review of ABI mismatches are required before releasing a version
Manual build with automatic compliance checking the contract is updated rarely but is critical manual steps remain, and automation only signals a mismatch

Automation removes routine work, but it does not cancel control points: reconciling addresses with the environment, reviewing changes in the interface description, and freezing the version before release.

The manual option is justified where the contract is updated once every few months and there is a single consumer — then uploading artifacts is cheaper than maintaining a continuous build pipeline.

The choice of approach determines how much manual work remains in each release and exactly where the error will reach the package consumer.

How we implement it: from repository audit to handover

A package for a smart contract is often assembled by hand: types are generated by a separate script, the ABI is uploaded from artifacts, and the version in the registry lives separately from the contract version.

After several releases, the types diverge from the ABI, and consumers’ builds break. Here is how we turn this into a reproducible process.

  1. Repository audit. We analyze where the contracts are stored, how types are generated, what goes into the published package, and what consumers import. The output is a map of mismatches between the local build and what is already published.
  2. Version publication scheme. We bind the package version to the contract version and network: prerelease labels for the test network, stable tags for the mainnet. The changelog records changes to the ABI and types between releases.
  3. Automatic build setup. Compilation, generation of ABI and types, and reconciliation of artifacts with sources. The build runs on every commit, while publication happens only by tag, so intermediate builds do not enter the registry.
  4. Artifact verification on the test network. We deploy the contract, connect the built package as an external consumer, and run scenarios: reconcile the ABI with the deployed code and check calls on a real network, not against stubs.
  5. Release and rollback scheme. We publish by tag, preserve previous versions and labels — rollback becomes the release of a previous version, not rewriting history. Consumers retain a predictable update path.
  6. Handover and team training. We show how to make a change in the contract and take it through to publication, provide documentation on the release cycle and build on-call duty — after that, the team releases the package independently.

Deliverables: types, schemas, update regulations

A smart contract without supporting infrastructure is a source file that no one will be able to build a month later.

That is why the delivery includes not only the contract itself but the entire set the team will work with independently afterward: types, deployment scheme, secret policies, and version regulations. Below is what exactly remains in your repository.

  • Type and interface descriptions. A package with types for contract methods and its events: arguments, structures, error codes. Connected to the frontend and backend, it catches signature mismatches at build time — before sending a transaction, not after.
  • ABI and address generation. The build extracts the ABI from artifacts and substitutes contract addresses by network, so they are not edited manually with every update.
  • Deployment scheme. A sequence of steps with dependencies, constructor parameters, initialization of related contracts, and post-publication checks — deployment is reproduced one-to-one from it.
  • Secret access policies. Separation of signer roles for the test network and mainnet, key issuance only from storage, rotation, and prevention of keys entering code and logs.
  • Build documentation. Build and test commands, environment requirements, and the procedure for bringing up a local stand — a new engineer reaches their first commit without corresponding with the author.
  • Version release regulations. Numbering rules, what counts as a breaking change, mandatory checks before package publication, and the procedure for revoking an erroneous release.

Case: version mismatches between stands and the production network

The project is a liquidity protocol with several contracts, a frontend, an indexing service, and three stands.

Contract descriptions and addresses by network were stored directly in the application repository: they were updated manually after each deployment, and types for calls were added from memory.

Before: manual synchronization of descriptions

Before a release, an engineer copied descriptions from build artifacts, reconciled addresses for each network, and edited the types of the calling code — 12–15 manual edits per release. Releasing a version took 3–4 days, and most of the time went into reconciliation, not changes.

Every fifth integration run failed due to a description mismatch: an old interface fragment remained in the code, and the call went out with the wrong signature.

After: a package assembled from artifacts

Contracts are built in a single environment (Hardhat), and an npm package is generated from build artifacts: call types, descriptions, and addresses by network.

The package version is tightly linked to the contracts version, and a separate step in the pipeline reconciles its contents with a fresh build and stops the release on mismatch. Stands and the application update the dependency with one command, without manual copying.

  • Manual edits per release: 12–15 → 1–2 (network configuration only).
  • Version release time: 3–4 days → several hours.
  • Failures due to description mismatch: every fifth run → none over a quarter of observations.

Call stability stopped depending on an individual engineer’s discipline: incompatibility is caught at build time, not on a stand or in the mainnet.

How a local stand differs from the production network during verification

A local stand answers the question “is the contract logic correct,” while a test network answers “does the contract behave the same way in a live environment.” These are different classes of checks, and one cannot be substituted for the other: a local node comes up in seconds, state is snapshotted and rolled back, so scenarios are run in batches — boundary amounts, role permissions, call order.

Typing is checked here as well: the npm package generated from the ABI lets the compiler catch mismatches in arguments and addresses before launch.

But no local run will show the real gas cost, the confirmation queue, and the delay between sending a call and its inclusion in a block.

The test network adds what the stand lacks: public nodes with limits, the behavior of third-party contracts and oracles, and chain reorganization.

Only there can you see that an operation in a loop hits the gas limit or that a call fails on a contract that was locally replaced with a stub.

Typical environment errors are artifact mismatches. Locally, one instance is connected; on the network, another; the type file and ABI are assembled from different builds, and the call goes out with the wrong signature.

That is why verification before publication runs on reconciled artifacts: one ABI, one set of types, a fixed address.

The order is this: local run as a quick filter, then the test network, then publication of exactly the build that passed the checks. Artifacts are generated once and passed onward — otherwise mismatches between environments are inevitable.

Common doubts: types, artifacts, access, support

Doubts about integration usually arise when the package has already been assembled and the customer’s repository lives its own life.

Below are the things most often clarified before starting: how package versions and the deployed contract are synchronized, what to do with an already working scheme, and who retains access.

Will the package match an already deployed contract?

The wrapper is built around the address and ABI, so for a deployed contract, typing is generated from its artifacts, and the address is fixed in the environment config.

The package version is bound to the artifact fingerprint — a mismatch between the code in the repository and what is running on the network is caught at build time, not after rollout.

Can this be connected to the current repository without rewriting it?

Yes. The package is installed as a regular dependency, the editor picks up the types, and the wrappers lay on top of existing calls. Old code continues to work and is migrated to the typed layer gradually, one module at a time — development does not need to stop during migration.

How are secrets and deployment keys stored?

Keys do not enter the repository or the built artifacts. They live in the environment’s secret storage, access is granted by roles and only for the required operations, and logs retain addresses and build fingerprints without sensitive data.

What is included in support handover?

We hand over the repository, config schema, release process description, checks, and the procedure for rolling back to a previous version. Your engineers release updates themselves; we get involved when contract logic or access rules change.

Let us discuss the task: work plan and estimate for your repository

We do not give an estimate “by feel” — first we look at how the contract is built and published today.

Send a link to the repository and a couple of introductory details, and we will return with an implementation plan, work order, and clear boundaries: what we do and what remains on your side.

What is needed to start:

  • a link to the repository with contracts and read permissions — this is enough to analyze the structure;
  • how build artifacts are currently prepared: manually, with scripts, or through CI;
  • the list of networks and environments where contracts are deployed, and whether addresses are needed in the interface description;
  • requirements for types and the interface description format — if there are internal rules or preferences for naming;
  • which checks are mandatory before release: tests, linters, manual confirmation;
  • constraints on dependencies and private registries, if the package is not for public access.

The work order looks like this: repository and current build analysis, then a package prototype on one contract, then connecting the rest, setting up autogeneration of the interface description and types, and only after that — build in a continuous cycle and release with versioning. This way you see a working result early and can adjust requirements before anything is fixed.

Describe the task in two or three sentences and attach a link to the repository — we will return with an estimate and plan.