Poseidon hashing for Polkavm with solidity compatibility
  • Rust 75.3%
  • TypeScript 19.4%
  • Makefile 4.6%
  • Shell 0.7%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-07-20 23:46:42 +02:00
.cargo Add .cargo/config.toml 2026-01-30 19:06:44 +01:00
.forgejo/workflows Update .forgejo/workflows/rust_build.yaml 2026-02-09 10:03:54 +01:00
src updated format 2026-07-21 04:44:32 +07:00
ts updated format 2026-07-21 04:44:32 +07:00
.gitignore Initial commit 2025-08-25 19:33:43 +02:00
build.sh updated format 2026-07-21 04:44:32 +07:00
Cargo.lock updated format 2026-07-21 04:44:32 +07:00
Cargo.toml updated format 2026-07-21 04:44:32 +07:00
erc20.polkavm updated format 2026-07-21 04:44:32 +07:00
erc20_new.polkavm updated format 2026-07-21 04:44:32 +07:00
LICENSE Initial commit 2025-08-25 19:33:43 +02:00
Makefile updated format 2026-07-21 04:44:32 +07:00
poseidon.polkavm updated format 2026-07-21 04:44:32 +07:00
README.md updated format 2026-07-21 04:44:32 +07:00
riscv64emac-unknown-none-polkavm.json updated format 2026-07-21 04:44:32 +07:00
rust-toolchain.toml updated format 2026-07-21 04:44:32 +07:00

PoseidonPolkaVM

Poseidon hashing for Polkavm with solidity compatibility, making it more gas effective to do Poseidon hashing in dotsama

This is a port of Light-Poseidon that is compatible with circom and PolkaVM, allowing you to do poseidon hashing on polkavm.

Poseidon paper

Poseidon Hash Function

The hash(uint256[2]) function implements the Poseidon hash algorithm with:

  • Field: BN254 (alt_bn128)
  • Arity: 2 (takes two inputs)
  • Rounds: 8 full rounds + 57 partial rounds
  • S-box: x^5

Usage

After deploying this PolkaVM program, you can call it using this solidity interface:

pragma solidity ^0.8.30;

interface IPoseidonT3 {
    function hash(uint256[2] memory input) external pure returns (uint256);
}
# Call the hash function with two uint256 inputs
cast call <CONTRACT_ADDRESS> "hash(uint256[2]):(uint256)" "[123,456]" \
  --rpc-url <RPC_URL> --private-key <PRIVATE_KEY>

Example Results

Input Output
[0, 0] 5520371747610818048552497760483731695918538905235353263918705622436011791040
[1, 2] 3240460917331939313458239639914504962640333851982787431747809795119847651274
[123, 456] 10422317022970317265083564129867363010880980031113186224756990573079674352133

Architecture

┌─────────────────┐    ┌──────────────────┐    ┌─────────────────┐
│   Rust Source   │ -> │   PolkaVM Build  │ -> │  Smart Contract │
│   (src/erc20.rs)│    │   (build.sh)     │    │   (.polkavm)    │
└─────────────────┘    └──────────────────┘    └─────────────────┘

┌─────────────────┐    ┌──────────────────┐
│  TypeScript     │ -> │   Contract ABI   │
│  (ts/erc20.ts)  │    │   (JSON)         │
└─────────────────┘    └──────────────────┘

Installation

Rust

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

Polkatool

cargo install polkatool

Node, Typescript and Yarn

It is recommended to install via nvm.

# Download and install nvm:
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash

# in lieu of restarting the shell
\. "$HOME/.nvm/nvm.sh"

# Download and install Node.js:
nvm install 22

# Verify the Node.js version:
node -v # Should print "v22.15.0".
nvm current # Should print "v22.15.0".

# Verify npm version:
npm -v # Should print "10.9.2".
npm install -g typescript
npm install -g yarn

Build

Run ./build.sh, you will get an erc20.polkavm file for deployment and test

Build Process

# Build the PolkaVM contract
./build.sh

# The output is erc20.polkavm - ready for deployment

Deployment & Testing

  1. Go to ts folder, mv env.example .env. Input a key with some token in Westend Asset Hub.
  2. Run yarn to install dependency
  3. Run yarn erc20 to deploy ERC20 contract and interact with it.
user@user-X870-EAGLE-WIFI7:~/github/papermoon/polkavm-erc20-in-rust/ts$ yarn erc20
yarn run v1.22.22
$ ts-node main.ts
contract address is:  0xD670137a3CfAaF08B5395CDaaEaFf617672896D2
recipientBalance is:  1000000000000000000n
Name:  name
Symbol:  symbol
Decimals:  18n
Total Supply:  1234000000000000000000n
Balance:  1234000000000000000000n
Allowance:  0n
My balance after transfer:  1233n
Random wallet balance after transfer:  1n
Approve allowance:  2n
Approve allowance after transferFrom:  1n
Done in 38.25s.

Testing Poseidon Hash

Use cast to test the Poseidon hash function:

# Test Poseidon hash with different inputs
cast call CONTRACT_ADDRESS "hash(uint256[2]):(uint256)" "[123,456]" \
  --rpc-url http://eth-pas-hub.laissez-faire.trade:8545 --private-key <PRIVATE_KEY>

cast call CONTRACT_ADDRESS "hash(uint256[2]):(uint256)" "[1,2]" \
  --rpc-url http://eth-pas-hub.laissez-faire.trade:8545 --private-key <PRIVATE_KEY>

Implementation Details

Rust Contract (src/erc20.rs)

  • No-std environment for PolkaVM compatibility
  • Custom field arithmetic for BN254 operations
  • ABI decoding for Ethereum-compatible function calls
  • Memory management with SimpleAlloc

Poseidon Implementation (src/zk.rs)

  • Field element operations (Fr struct with 4x u64 limbs)
  • Round constants for BN254 Poseidon
  • MDS matrix for linear layer
  • S-box implementation (x^5 exponentiation)

Function Selector

  • Hash function: hash(uint256[2])
  • Selector: 0x561558fe
  • Input format: ABI-encoded array of two uint256 values

Contract Address

Latest deployment: 0xD670137a3CfAaF08B5395CDaaEaFf617672896D2 (Polkadot Asset Hub Testnet)

Contributing

When modifying the Poseidon implementation:

  1. Ensure field arithmetic remains correct
  2. Verify round constants match reference implementation
  3. Test with known input/output pairs
  4. Build and test on PolkaVM before deployment