Poseidon hashing for Polkavm with solidity compatibility
- Rust 75.3%
- TypeScript 19.4%
- Makefile 4.6%
- Shell 0.7%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| .cargo | ||
| .forgejo/workflows | ||
| src | ||
| ts | ||
| .gitignore | ||
| build.sh | ||
| Cargo.lock | ||
| Cargo.toml | ||
| erc20.polkavm | ||
| erc20_new.polkavm | ||
| LICENSE | ||
| Makefile | ||
| poseidon.polkavm | ||
| README.md | ||
| riscv64emac-unknown-none-polkavm.json | ||
| rust-toolchain.toml | ||
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 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
- Go to
tsfolder,mv env.example .env. Input a key with some token in Westend Asset Hub. - Run
yarnto install dependency - Run
yarn erc20to 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:
- Ensure field arithmetic remains correct
- Verify round constants match reference implementation
- Test with known input/output pairs
- Build and test on PolkaVM before deployment