From Solidity to ink!: Smart Contract Deployment on Polkadot/Kusama

We are a team of blockchain engineers with 7 years of experience in Web3. Over the past three years, we have deployed more than 15 smart contracts on the Astar and Kusama parachains. When a client approaches us to migrate their DeFi protocol from Ethereum to Polkadot, the first thing we explain is:

Blockchain Development Services

Frequently Asked Questions

העבודות האחרונות

  • image_website-b2b-advance_0.webp
    B2B ADVANCE company website development
    1441
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1301
  • image_websites_belfingroup_462_0.webp
    Website development for BELFINGROUP
    998
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1267
  • image_logo-advance_0.webp
    B2B Advance company logo design
    713
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    1003

We are a team of blockchain engineers with 7 years of experience in Web3. Over the past three years, we have deployed more than 15 smart contracts on the Astar and Kusama parachains. When a client approaches us to migrate their DeFi protocol from Ethereum to Polkadot, the first thing we explain is: Polkadot is not just another L2. There is no single VM for all networks. Smart contracts are not deployed on the Relay Chain, but on parachains that have the pallet-contracts module enabled. Kusama is Polkadot's canary network with faster governance and less strict slot requirements. We use Kusama for testing production logic, and Polkadot or a specialized parachain (Astar, Phala, Aleph Zero) for final deployment.

Recently, we deployed a vault contract for staking on Astar. The client saved significantly on gas compared to the EVM version — ink! contracts are on average 1.8 times more gas-efficient than equivalent Solidity contracts on Moonbeam. Our contracts have processed over 100,000 transactions without a single security incident. The development time for ink! is 30% faster than Solidity due to Rust's safety features and excellent tooling.

Why ink! and not Solidity for Polkadot?

Contracts for pallet-contracts are written in ink! — a native Rust with macros that generate contract metadata and an ABI-compatible interface. Unlike Solidity, ink! gives direct access to low-level Rust optimizations and compiles to Wasm. Ink! development is 3x safer due to Rust's ownership model.

#[ink::contract] mod vault { use ink::storage::Mapping; #[ink(storage)] pub struct Vault { balances: Mapping<AccountId, Balance>, owner: AccountId, } impl Vault { #[ink(constructor)] pub fn new() -> Self { Self { balances: Mapping::default(), owner: Self::env().caller(), } } #[ink(message, payable)] pub fn deposit(&mut self) { let caller = self.env().caller(); let value = self.env().transferred_value(); let current = self.balances.get(caller).unwrap_or(0); self.balances.insert(caller, &(current + value)); } #[ink(message)] pub fn withdraw(&mut self, amount: Balance) -> Result<(), Error> { let caller = self.env().caller(); let balance = self.balances.get(caller).unwrap_or(0); if balance < amount { return Err(Error::InsufficientBalance); } self.balances.insert(caller, &(balance - amount)); self.env().transfer(caller, amount) .map_err(|_| Error::TransferFailed) } } } 

Several key differences from Solidity that break the intuition of EVM developers:

  • No msg.value in regular functions — only in #[ink(message, payable)]. Calling a payable function without the flag will return an error.
  • Storage through Mapping without iteration — no mapping.keys(). If you need lists, store a separate Vec<AccountId>.
  • AccountId instead of address — 32 bytes, not 20. Addresses are in SS58 format, not hex.
  • Balance is u128, not uint256. But practically no difference.

Choosing the right parachain for deployment

Not all Polkadot parachains directly support smart contracts. For ink! contracts, you need parachains with pallet-contracts enabled. Below is a comparison of popular options:

Parachain Type ink! Support Features
Astar Wasm+EVM Yes Largest ecosystem, cross-VM calls via XVM
Phala Wasm Yes Confidential contracts via TEE
Aleph Zero Wasm+EVM Yes High throughput, NFT marketplaces
Rococo (testnet) testnet Yes Free faucet, ideal for testing

For efficient ink! smart contract deployment on Polkadot, we recommend Astar — it is the most mature, supports both Wasm and EVM, and has a rich infrastructure.

Tooling: cargo-contract

cargo install cargo-contract --force cargo contract new my_contract cd my_contract # Build: generates .contract, .wasm, .json (ABI) cargo contract build --release # Run tests cargo test 

The .contract file is an archive containing WASM bytecode and metadata. That's what we deploy.

Deployment and testing environments

Local testing: substrate-contracts-node

substrate-contracts-node --dev --tmp 

Starts a single-node network with pre-funded accounts (Alice, Bob, Charlie — like Hardhat accounts[0]). Use the Contracts UI at contracts-ui.substrate.io or programmatic via @polkadot/api.

Testnet: Contracts on Rococo

Rococo is a Polkadot testnet with a dedicated contracts parachain for testing ink!. Faucet: paritytech.github.io/polkadot-testnet-faucet.

Production: Astar Network

Astar is the most mature parachain with pallet-contracts in the Polkadot ecosystem. It supports both ink! (Wasm) and EVM (Solidity) contracts in the same network, cross-VM calls via XVM. For most production use cases on Polkadot, we deploy to Astar.

Deployment via polkadot.js

import { ApiPromise, WsProvider, Keyring } from '@polkadot/api'; import { CodePromise } from '@polkadot/api-contract'; import * as fs from 'fs'; async function deploy() { const provider = new WsProvider('wss://rpc.astar.network'); const api = await ApiPromise.create({ provider }); const keyring = new Keyring({ type: 'sr25519' }); const deployer = keyring.addFromUri(process.env.MNEMONIC!); const wasm = fs.readFileSync('./target/ink/vault.wasm'); const abi = JSON.parse(fs.readFileSync('./target/ink/vault.json', 'utf8')); const code = new CodePromise(api, abi, wasm); const gasLimit = api.registry.createType('WeightV2', { refTime: 30_000_000_000n, proofSize: 1_000_000n, }); const storageDepositLimit = null; // automatic const tx = code.tx.new({ gasLimit, storageDepositLimit }); await new Promise<void>((resolve, reject) => { tx.signAndSend(deployer, ({ contract, status }) => { if (status.isInBlock) { console.log('Contract address:', contract?.address.toString()); resolve(); } }).catch(reject); }); await api.disconnect(); } 

Gas model: WeightV2

Unlike EVM where gas is a single number, Substrate uses a two-dimensional weight:

  • refTime — computation time (picoseconds)
  • proofSize — size of storage proof for light clients

When deploying, you need to estimate both parameters. Method: dry run (contracts.instantiate with estimateGas: true) or use cargo-contract:

cargo contract instantiate --dry-run \ --constructor new \ --args \ --suri //Alice \ --url ws://127.0.0.1:9944 

Storage deposit

pallet-contracts requires a deposit for the storage occupied — analogous to EIP-1153 transient storage, but permanent. The cost is proportional to the bytes stored by the contract. When storage is deleted (via ink::env::set_contract_storage::<K, ()>(&key, &())), the deposit is returned. This is important for long-lived contracts with large state. In practice, the storage deposit for a typical contract is a small fraction of the deployment cost, which is significantly lower than on EVM parachains.

Typical mistakes when migrating from EVM

EVM Pattern ink! Solution
mapping.length() Separate counter or Vec
block.timestamp self.env().block_timestamp() (u64, milliseconds)
msg.sender self.env().caller()
payable by default Explicit attribute #[ink(message, payable)]
Events with indexed #[ink(topic)] on event field
require(cond, "msg") assert! or Result<_, Error>

Cross-contract calls in ink! require importing the ABI of the called contract and explicitly specifying the gas limit — there is no automatic forwarding like Solidity's {gas: gasleft()}.

Deployment stages

  1. Requirements analysis — study the contract logic, choose a parachain.
  2. Contract writing — implement in ink! with security and gas optimization in mind.
  3. Testing — local (substrate-contracts-node) and on Rococo.
  4. Security audit — combination of Slither for ink! and manual audit.
  5. Production deployment — launch on Astar or another parachain.
  6. Support — monitoring, updates, user assistance.

What's included in the work

  • Full cycle from idea to deployment.
  • Source code with detailed comments.
  • Contract documentation (white paper, technical specification).
  • Test report (unit test and fuzzing results).
  • Access to the contract and interaction instructions.
  • One month of technical support after deployment.
Detailed pre-deployment checklist
  • Parachain selected (Astar/Phala/Aleph Zero)
  • Contract built (cargo contract build --release)
  • Unit tests passed (cargo test)
  • Fuzzing passed (Echidna or custom)
  • Weight estimated: cargo contract instantiate --dry-run
  • Storage deposit calculated and funded
  • Test on Rococo passed
  • Deployer account has sufficient funds for deployment

Our team consists of certified Parity Substrate specialists with 5+ years of experience in Rust and blockchain development. We guarantee results: 15+ successful deployments on Kusama and Polkadot. For more details on Substrate smart contracts, refer to the official Substrate documentation.

Contact us for a project assessment. Order a turnkey deployment — we'll handle the entire cycle from analysis to support. We'll assess your project for free and give you exact timelines (from 2 weeks to 2 months, depending on complexity).