Asset Hub Integration
Asset Hub is the primary parachain for token management on Kusama/Polkadot. This guide covers integrating ZK applications with Asset Hub.
What is Asset Hub?
Asset Hub (formerly Statemint/Statemine) is a system parachain that provides:
- Fungible Tokens: Create and manage ERC20-like assets
- NFTs: Non-fungible token support
- Low Fees: Cheaper transactions than the relay chain
- EVM Compatibility: Smart contract support via Frontier
Network Configuration
Paseo Testnet (Recommended for Development)
| Parameter | Value |
|---|---|
| Network Name | Paseo Asset Hub |
| Chain ID | 420420422 |
| RPC URL | https://testnet-passet-hub-eth-rpc.polkadot.io |
| Block Explorer | https://blockscout-passet-hub.parity-testnet.parity.io/ |
| Faucet | https://faucet.polkadot.io/?parachain=1111 |
| Native Token | PAS |
Kusama Mainnet
| Parameter | Value |
|---|---|
| Network Name | Kusama Asset Hub |
| Chain ID | (TBD) |
| RPC URL | https://kusama-asset-hub-rpc.polkadot.io |
| Block Explorer | https://blockscout.kusama.network/ |
| Native Token | KSM |
Getting PAS Tokens
1. Create a Wallet
cast wallet new
Output:
Successfully created new keypair.
Address: 0x7E68B2bf528c96e9b9D140211391d4e5FBce033e
Private key: 0xf07706918ef3fac8d5c1856010f470fecf15dca5b30a1ad1e5f8b3c022d8e997
2. Fund from Faucet
Visit: https://faucet.polkadot.io/?parachain=1111
Enter your address and request test tokens.
3. Check Balance
cast balance 0x7E68B2bf528c96e9b9D140211391d4e5FBce033e \
--rpc-url https://testnet-passet-hub-eth-rpc.polkadot.io
Deploying Contracts
Using Remix
- Visit Remix for Polkadot
- Connect to Paseo Asset Hub network
- Write/paste your contract
- Deploy using your wallet
Using Foundry
# Create project
forge init my-zk-project
cd my-zk-project
# Add dependencies
forge install OpenZeppelin/openzeppelin-contracts
# Set RPC
export ETH_RPC_URL=https://testnet-passet-hub-eth-rpc.polkadot.io
export PRIVATE_KEY=your_private_key
# Deploy
forge create src/MyContract.sol:MyContract \
--rpc-url $ETH_RPC_URL \
--private-key $PRIVATE_KEY
Using Hardhat
// hardhat.config.js
module.exports = {
networks: {
paseo: {
url: "https://testnet-passet-hub-eth-rpc.polkadot.io",
accounts: [process.env.PRIVATE_KEY]
}
}
};
// Deploy script
async function main() {
const Contract = await ethers.getContractFactory("MyContract");
const contract = await Contract.deploy();
await contract.waitForDeployment();
console.log("Deployed to:", await contract.getAddress());
}
Example: ZK Verifier Deployment
1. Generate Verifier with snarkJS
# Export Solidity verifier
snarkjs zkey export verifierkey circuit_final.zkey verifier.sol
# Or export Groth16 verifier
snarkjs zkey export solidityverifier circuit_final.zkey verifier.sol
2. Deploy Verifier
# Deploy to Asset Hub
forge create src/verifier.sol:Groth16Verifier \
--rpc-url https://testnet-passet-hub-eth-rpc.polkadot.io \
--private-key $PRIVATE_KEY
3. Deploy Application Contract
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
import "./verifier.sol";
contract PolkadotDemo {
IGroth16Verifier public immutable verifier;
constructor() {
verifier = IGroth16Verifier(0x00D124b363e7F278aEFF220398254EdE169D307c);
}
event DidSomething(bool myresult);
function dosomething(
uint256[2] calldata _pA,
uint256[2][2] calldata _pB,
uint256[2] calldata _pC,
uint256[3] calldata _pubSignals
) external {
require(
verifier.verifyProof(_pA, _pB, _pC, _pubSignals),
"Invalid proof"
);
emit DidSomething(true);
}
}
Deploy:
forge create src/main.sol:PolkadotDemo \
--rpc-url https://testnet-passet-hub-eth-rpc.polkadot.io \
--private-key $PRIVATE_KEY
Testing Contracts
Using cast
# Call view function
cast call <CONTRACT_ADDRESS> \
"myFunction(uint256):(uint256)" \
"123" \
--rpc-url https://testnet-passet-hub-eth-rpc.polkadot.io
# Send transaction
cast send <CONTRACT_ADDRESS> \
"myFunction(uint256)" \
"123" \
--rpc-url https://testnet-passet-hub-eth-rpc.polkadot.io \
--private-key $PRIVATE_KEY
Using Foundry Tests
// test/MyContract.t.sol
pragma solidity ^0.8.28;
import "forge-std/Test.sol";
import "../src/MyContract.sol";
contract MyContractTest is Test {
MyContract public contract;
function setUp() public {
contract = new MyContract();
}
function testMyFunction() public {
uint256 result = contract.myFunction(123);
assertEq(result, 456);
}
}
Run tests:
forge test
Asset Hub Specific Features
ERC20 Token Deployment
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract MyToken is ERC20 {
constructor() ERC20("My Token", "MTK") {
_mint(msg.sender, 1000000 * 10 ** decimals());
}
}
Cross-Chain Messages (XCM)
Asset Hub supports XCM for cross-parachain communication:
// Example: Send tokens to another parachain
function sendToParachain(
uint32 paraId,
address recipient,
uint256 amount
) external {
// XCM integration would go here
// This is a simplified example
}
Gas Costs
| Operation | Gas Cost | PAS Cost (approx) |
|---|---|---|
| Transfer | 21,000 | 0.001 |
| ERC20 Transfer | 65,000 | 0.003 |
| Contract Deployment | 500,000+ | 0.02+ |
| ZK Verification | 100,000-300,000 | 0.005-0.015 |
Best Practices
1. Use Testnet First
Always test on Paseo before deploying to mainnet.
2. Verify Contracts
Use Blockscout for source verification:
forge verify-contract \
<CONTRACT_ADDRESS> \
src/MyContract.sol:MyContract \
--chain-id 420420422 \
--etherscan-api-key <KEY>
3. Monitor Gas
# Check gas price
cast gas-price --rpc-url https://testnet-passet-hub-eth-rpc.polkadot.io
4. Handle Reverts
try contract.myFunction(value) returns (uint256 result) {
// Success
} catch Error(string memory reason) {
// Handle revert message
} catch (bytes memory) {
// Handle low-level error
}
Troubleshooting
"Insufficient funds"
Ensure your wallet has PAS tokens for gas.
"Contract creation failed"
Check:
- Constructor arguments are correct
- Gas limit is sufficient
- No revert in constructor
"Transaction reverted"
Use cast logs to see events and debug:
cast logs --rpc-url https://testnet-passet-hub-eth-rpc.polkadot.io \
--from-block latest
Resources
Previous: Poseidon Hash | Next: Shielded Pools