Skip to main content
Version: v6

Deploy with Hardhat & Ethers.js

Hardhat is a popular Ethereum development framework. With @parity/hardhat-polkadot, you can use it to deploy and interact with ink! smart contracts on Polkadot-compatible environments.

This guide walks you through using Hardhat to deploy and interact with ink! smart contracts on Polkadot Hub.

Prerequisites

Before starting, ensure you have:

Project Setup

  1. Create a Hardhat Project
mkdir hardhat-example
cd hardhat-example
npm init -y
  1. Install Required Dependencies

To interact with Polkadot, Hardhat requires the following plugin to compile contracts to PolkaVM bytecode and to spawn a local node compatible with PolkaVM:

npm install --save-dev @parity/hardhat-polkadot
  1. Initialize the Project
npx hardhat-polkadot init
npm install
note

By default, this creates a basic project with a Solidity smart contract. Since we already have our ink! smart contract compiled, we'll skip the Solidity compilation step.

Configuration

Network Configuration

Update your hardhat.config.js with network settings for the Polkadot Hub Testnet:

require('@nomicfoundation/hardhat-toolbox');
require('@parity/hardhat-polkadot');

const { vars } = require('hardhat/config');

module.exports = {
// ... other config
networks: {
hardhat: {
// ... local config
},
polkadotHubTestnet: {
polkavm: true,
url: 'https://testnet-passet-hub-eth-rpc.polkadot.io',
accounts: [vars.get('PRIVATE_KEY')],
},
},
};

Private Key Setup

Export your private key and save it in your Hardhat environment:

npx hardhat vars set PRIVATE_KEY "INSERT_PRIVATE_KEY"

Replace INSERT_PRIVATE_KEY with your actual private key. For details on private key exportation, refer to How to export an account's private key.

Get Test Tokens

Use the Polkadot Hub Testnet faucet to fund your account with test PAS tokens.

Contract Deployment

Understanding Contract Files

After building your ink! smart contract with Solidity metadata, you'll find 3 key files in your contract's target directory:

  • <contract-name>.polkavm: Raw contract bytecode that will be deployed on-chain
  • <contract-name>.abi: Solidity ABI JSON format for contract interaction (used by ethers.js)
  • <contract-name>.json: Solidity contract metadata specification for verification

Deployment Script

Create a file called scripts/deploy.js:

const hre = require('hardhat');
const { join } = require('path');
const { readFileSync } = require('fs');

// Import the ABI of the contract from the flipper_evm.json file.
const abi = require("../../target/ink/flipper_evm.json").output.abi;

async function main() {
const [deployer] = await hre.ethers.getSigners();

// Fetch the bytecode of the contract.
const bytecodePath = join(__dirname, "../../target/ink", "flipper_evm.polkavm");
const bytecode = `0x${readFileSync(bytecodePath).toString('hex')}`;

const flipper = new hre.ethers.ContractFactory(abi, bytecode, deployer);

// Deploy the contract with the constructor arguments.
const contract = await flipper.deploy(true);
await contract.waitForDeployment();

// Get the address of the deployed contract.
const address = await contract.getAddress();
console.log(`Contract deployed at: ${address}`);
}

main().catch((error) => {
console.error(error);
process.exitCode = 1;
});

Run Deployment

npx hardhat run scripts/deploy.js --network polkadotHubTestnet

This will print the smart contract address once deployment is complete:

Contract deployed at: YOUR_CONTRACT_ADDRESS
note

Save this address - you'll need it to interact with your deployed contract.

Contract Interaction

Create a file called scripts/interact.js:

const hre = require('hardhat');

// Import the ABI of the contract from the flipper_evm.json file.
const abi = require("../../target/ink/flipper_evm.json").output.abi;

async function main() {
const [deployer] = await hre.ethers.getSigners();

// Replace with your deployed contract address
const contractAddress = 'INSERT_CONTRACT_ADDRESS';

const flipper = new hre.ethers.Contract(contractAddress, abi, deployer);

// Read contract state
const state = await flipper.get();
console.log(`State: ${state}`);

// Call the flip message
await flipper.flip();

// Read contract state again to see the change
const state_after = await flipper.get();
console.log(`State: ${state_after}`);
}

main().catch((error) => {
console.error(error);
process.exitCode = 1;
});

Run the interaction script:

npx hardhat run scripts/interact.js --network polkadotHubTestnet

Local Development Environment

For local testing, you can spin up a local testing environment:

Download Required Binaries

Download the latest release from ink-node releases. The release includes:

  • ink-node binary (the PolkaVM-compatible node)
  • eth-rpc adapter binary (adapts the node to EVM-style JSON-RPC)

Configure Local Network

Update your hardhat.config.js:

require('@nomicfoundation/hardhat-toolbox');
require('@parity/hardhat-polkadot');

module.exports = {
// ... other config
networks: {
hardhat: {
polkavm: true,
nodeConfig: {
nodeBinaryPath: 'INSERT_PATH_TO_INK_NODE',
rpcPort: 8000,
dev: true,
},
adapterConfig: {
adapterBinaryPath: 'INSERT_PATH_TO_ETH_RPC_ADAPTER',
dev: true,
},
},
localNode: {
polkavm: true,
url: `http://127.0.0.1:8545`,
},
},
};

Start Local Environment

npx hardhat node

This launches the ink-node (on localhost:8000) and the eth-rpc adapter (on localhost:8545).

Interact with Local Network

npx hardhat run scripts/interact.js --network localNode

Next Steps

With your contract deployed via Hardhat, you can now: