npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

hardhat-external-artifacts

v0.0.6

Published

Hardhat plugin that allow hardhat to know about them

Downloads

1,208

Readme

hardhat-external-artifacts

A Hardhat 3 plugin that allows users to provide external contract artifacts (ABIs) to Hardhat's EDR provider for decoding events, errors, and function calls in console output.

This is useful when interacting with contracts that were not compiled by the current Hardhat project (e.g., external protocols, deployed contracts from other projects).

Installation

npm install hardhat-external-artifacts
# or
pnpm add hardhat-external-artifacts
# or
yarn add hardhat-external-artifacts

Usage

Import the plugin and add it to the plugins array in your Hardhat configuration file:

// hardhat.config.ts
import type {HardhatUserConfig} from 'hardhat/config';
import HardhatExternalArtifactsPlugin from 'hardhat-external-artifacts';

const config: HardhatUserConfig = {
	plugins: [HardhatExternalArtifactsPlugin],
	solidity: '0.8.20',
	externalArtifacts: {
		// Load specific artifact files
		paths: [
			'./external-artifacts/WETH.json',
			'./external-artifacts/Uniswap/', // Loads all .json in directory
		],
		// Optional: specify solc version for synthetic compilations
		solcVersion: '0.8.20',
		// Optional: disable warnings for invalid artifacts
		warnOnInvalidArtifacts: true,
	},
};

export default config;

Loading from npm Packages

You can load artifacts directly from npm packages using the modules option. This uses Node.js module resolution, supporting both package exports and direct paths:

// hardhat.config.ts
import type {HardhatUserConfig} from 'hardhat/config';
import HardhatExternalArtifactsPlugin from 'hardhat-external-artifacts';

const config: HardhatUserConfig = {
	plugins: [HardhatExternalArtifactsPlugin],
	solidity: '0.8.20',
	externalArtifacts: {
		// Load from npm packages via module resolution
		modules: [
			'@my-org/contracts/artifacts',     // Uses package.json "exports"
			'some-package/dist/artifacts',     // Direct path in package
		],
	},
};

export default config;

Note: The modules option differs from paths in that it resolves module specifiers from node_modules using Node.js resolution, respecting the package's exports field. This is useful when a package explicitly exports its artifacts folder.

For example, if a package has:

{
  "name": "@my-org/contracts",
  "exports": {
    "./artifacts": "./dist/artifacts"
  }
}

Then using modules: ['@my-org/contracts/artifacts'] will correctly resolve to the exported path.

Using a Resolver Function

For more dynamic artifact loading, you can use a resolver function:

// hardhat.config.ts
import type {HardhatUserConfig} from 'hardhat/config';
import HardhatExternalArtifactsPlugin from 'hardhat-external-artifacts';

const config: HardhatUserConfig = {
	plugins: [HardhatExternalArtifactsPlugin],
	solidity: '0.8.20',
	externalArtifacts: {
		resolver: async () => {
			// Fetch from API, database, or any source
			return [
				{
					contractName: 'ERC20',
					sourceName: 'openzeppelin/ERC20.sol',
					abi: [
						/* ... */
					],
					bytecode: '0x...',
					deployedBytecode: '0x...',
				},
			];
		},
		solcVersion: '0.8.20',
	},
};

export default config;

Combined Approach

You can use both paths and resolver together:

// hardhat.config.ts
import type {HardhatUserConfig} from 'hardhat/config';
import HardhatExternalArtifactsPlugin from 'hardhat-external-artifacts';
import {loadDefiProtocolArtifacts} from './scripts/load-defi';

const config: HardhatUserConfig = {
	plugins: [HardhatExternalArtifactsPlugin],
	solidity: '0.8.20',
	externalArtifacts: {
		// Load from local directory
		paths: ['./vendor-artifacts/'],

		// Also fetch dynamically
		resolver: async () => loadDefiProtocolArtifacts(),

		// Use specific solc version for method ID computation
		solcVersion: '0.8.19',

		// Silence warnings for experimental use
		warnOnInvalidArtifacts: false,
	},
};

export default config;

Configuration Options

| Option | Type | Default | Description | | ------------------------ | ----------------------------------- | ----------- | -------------------------------------------------------- | | paths | string[] | [] | Paths to artifact files or directories (relative/absolute) | | modules | string[] | [] | Module specifiers to resolve from node_modules | | resolver | () => Promise<ExternalArtifact[]> | undefined | Function that returns artifacts dynamically | | solcVersion | string | "0.8.20" | Solc version for synthetic compilations | | warnOnInvalidArtifacts | boolean | true | Whether to log warnings for malformed artifacts | | debug | boolean | false | Enable debug logging for troubleshooting |

Artifact Format

Simple Artifact

The minimum required format for an external artifact:

{
  "contractName": "MyContract",
  "sourceName": "contracts/MyContract.sol",
  "abi": [...],
  "bytecode": "0x...",
  "deployedBytecode": "0x..."
}

Rich Artifact

For better fidelity, you can provide a rich artifact with embedded compilation data:

{
  "contractName": "MyContract",
  "sourceName": "contracts/MyContract.sol",
  "abi": [...],
  "bytecode": "0x...",
  "deployedBytecode": "0x...",
  "solcInput": "{\"language\":\"Solidity\",...}",
  "metadata": "{\"compiler\":{\"version\":\"0.8.20+commit...\"},...}",
  "evm": {
    "bytecode": { "object": "...", "sourceMap": "...", ... },
    "deployedBytecode": { "object": "...", "sourceMap": "...", ... },
    "methodIdentifiers": { "transfer(address,uint256)": "a9059cbb" }
  }
}

Rich artifacts are typically produced by tools like hardhat-deploy that preserve full compilation output.

How It Works

  1. When a network connection is established (specifically for EDR/Hardhat Network), the plugin loads the configured external artifacts.

  2. Simple artifacts are grouped into a synthetic compilation with computed method identifiers.

  3. Rich artifacts (with embedded solcInput) get their own individual compilations for maximum fidelity.

  4. The compilations are added to the EDR provider using addCompilationResult(), enabling:

    • Event decoding in transaction logs
    • Error decoding for reverts
    • Function call decoding in stack traces

Troubleshooting

Contract still shows as <UnrecognizedContract>

If you see logs like:

eth_call
  Contract call:             <UnrecognizedContract>
  From:                      0x...
  To:                        0x...

This means EDR couldn't match the contract's bytecode. EDR identifies contracts by matching the deployed bytecode at the target address with bytecode from compilation results.

Possible causes:

  1. Bytecode mismatch: The deployedBytecode in your artifact doesn't match what's actually deployed. This can happen due to:

    • Different compiler versions or optimizer settings
    • Metadata hash differences (appended to bytecode by default)
    • Constructor arguments that set immutable values
  2. Missing bytecode: Your artifact has an empty or missing deployedBytecode field.

Debugging steps:

  1. Enable debug logging:

    externalArtifacts: {
      paths: ['./artifacts/'],
      debug: true,  // Enable debug output
    }
  2. Verify bytecode exists and has reasonable length in the debug output.

  3. Compare your artifact's deployedBytecode with the actual bytecode at the address:

    // In your test/script
    const actualCode = await connection.provider.request({
      method: 'eth_getCode',
      params: [contractAddress, 'latest'],
    });
    console.log('Actual bytecode length:', actualCode.length);
    console.log('Artifact bytecode length:', artifact.deployedBytecode.length);
  4. For forked networks, ensure you have the exact artifact that was used to deploy the contract on mainnet. Minor differences (like metadata hash) will prevent matching.

Rich artifacts vs Simple artifacts

Rich artifacts (with solcInput) provide better matching because they include the complete compilation data. If you have access to the original compilation output (e.g., from hardhat-deploy), prefer using rich artifacts.

Requirements

  • Hardhat 3.x
  • Node.js 22+

License

MIT