Migrate from Solana to Paxeer EVM
Guide for Solana developers migrating to Paxeer EVM architecture mapping, Rust-to-Solidity patterns, and migration steps.
Paxeer offers a compelling value proposition for Solana developers: full EVM compatibility with the extensive Ethereum tooling ecosystem, combined with fast finality and high throughput. This guide helps Rust/Anchor developers translate their mental models and codebases to Solidity on Paxeer.
Why Solana Developers Choose Paxeer
- Fast block times - Comparable to Solana's speed, with instant finality
- EVM ecosystem access - Leverage Ethereum's mature tooling, audited contracts, and developer resources
- Full EVM compatibility - Deploy Solidity contracts unchanged
- No dependency declarations - Unlike Solana, Paxeer handles execution automatically
Understanding the Paradigm Shift
Before diving into code, it's essential to understand the fundamental architectural differences between Solana and EVM-based chains like Paxeer.
Execution Model Comparison
| Aspect | Solana | Paxeer EVM |
|---|---|---|
| Language | Rust (with Anchor framework) | Solidity |
| Account Model | Programs + Accounts (separated code and data) | Contracts (code and storage unified) |
| State Storage | Flat account data with owner programs | Contract storage slots (key-value) |
| Parallelization | Explicit (declare accounts upfront) | Optimistic (automatic conflict detection) |
| Finality | ~2.5-4.5 seconds (32 confirmations) | Instant (single block) |
| Fee Model | Compute units + priority fees + rent | Gas × Gas Price (no rent) |
| Cross-Contract Calls | CPI (Cross-Program Invocation) | Internal/External function calls |
| Deterministic Addresses | PDAs (Program Derived Addresses) | CREATE2 / ImmutableCreate2Factory |
| Token Standard | SPL Token | ERC-20 / ERC-721 / ERC-1155 |
| Dev Tooling | Anchor, Solana CLI, solana-web3.js | Hardhat, Foundry, ethers.js, viem |
Core Concept Mapping
Programs → Smart Contracts
On Solana, you write programs that are stateless executables. Data lives in separate accounts that programs can read and modify. On Paxeer EVM, smart contracts combine code and state in a single entity.
// Solana: Program is stateless, data in accounts
#[program]
pub mod counter {
use super::*;
pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
let counter = &mut ctx.accounts.counter;
counter.count = 0;
counter.authority = ctx.accounts.authority.key();
Ok(())
}
pub fn increment(ctx: Context<Increment>) -> Result<()> {
let counter = &mut ctx.accounts.counter;
counter.count += 1;
Ok(())
}
}
#[account]
pub struct Counter {
pub count: u64,
pub authority: Pubkey,
}
#[derive(Accounts)]
pub struct Initialize<'info> {
#[account(init, payer = authority, space = 8 + 8 + 32)]
pub counter: Account<'info, Counter>,
#[account(mut)]
pub authority: Signer<'info>,
pub system_program: Program<'info, System>,
}Key differences:
- No account space allocation needed - storage grows dynamically
- No explicit
Signervalidation -msg.senderis always authenticated - No system program imports - native operations are built into the EVM
PDAs → CREATE2 Deterministic Addresses
Solana's Program Derived Addresses (PDAs) let you create deterministic addresses from seeds. On EVM, you achieve similar functionality with CREATE2.
// Solana: PDA derivation
let (pda, bump) = Pubkey::find_program_address(
&[
b"vault",
user.key().as_ref(),
],
&program_id,
);
// In Anchor account validation
#[account(
seeds = [b"vault", user.key().as_ref()],
bump,
)]
pub vault: Account<'info, Vault>,CPI → Contract Calls
Solana's Cross-Program Invocation (CPI) becomes simple function calls in Solidity:
// Solana: CPI to token program
use anchor_spl::token::{self, Transfer};
let cpi_accounts = Transfer {
from: ctx.accounts.from_token_account.to_account_info(),
to: ctx.accounts.to_token_account.to_account_info(),
authority: ctx.accounts.authority.to_account_info(),
};
let cpi_program = ctx.accounts.token_program.to_account_info();
let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts);
token::transfer(cpi_ctx, amount)?;SPL Token → ERC-20
// Solana SPL Token - requires token accounts
#[derive(Accounts)]
pub struct TransferTokens<'info> {
#[account(mut)]
pub from: Account<'info, TokenAccount>,
#[account(mut)]
pub to: Account<'info, TokenAccount>,
pub authority: Signer<'info>,
pub token_program: Program<'info, Token>,
}Key ERC-20 differences from SPL:
- No Associated Token Accounts (ATAs) - balances are stored directly in the contract
- Approvals use
approve()/transferFrom()pattern - No mint/freeze authorities in basic implementation (add via
Ownable)
Fee Model Translation
Understanding the fee differences is crucial for accurate cost estimation:
| Solana Concept | Paxeer EVM Equivalent | Notes |
|---|---|---|
| Compute Units (CU) | Gas | Both measure computational work |
| Priority Fee | Gas Price | Higher price = faster inclusion |
| Rent | None | Paxeer has no rent; storage is permanent |
| Rent Exemption | N/A | No minimum balance requirements |
| Base Fee | Dynamic Base Fee | Paxeer doesn't burn base fee |
// Solana fee estimation
const computeUnits = 200_000;
const priorityFee = 1_000; // microlamports per CU
const rentExempt = await connection.getMinimumBalanceForRentExemption(accountSize);
// Paxeer EVM fee estimation
const gasLimit = 200_000n;
const gasPrice = await provider.getGasPrice();
const fee = gasLimit * gasPrice; // No rent to consider
No Rent on Paxeer!
Unlike Solana where accounts can be garbage collected if rent isn't paid, Paxeer EVM storage is permanent. This simplifies your application logic - no need to track rent-exempt minimums or worry about account closure.
Step 1: Set Up Your Development Environment
Install Required Tools
# Install Node.js (if not already installed)
# https://nodejs.org/
# Install Hardhat (recommended for Solana devs transitioning)
npm install --save-dev hardhat @nomicfoundation/hardhat-toolbox-mocha-ethers
# Or install Foundry (Rust-based, may feel more familiar)
curl -L https://foundry.paradigm.xyz | bash
foundryup
Configure for Paxeer
import { defineConfig, configVariable } from 'hardhat/config';
import hardhatToolboxMochaEthers from '@nomicfoundation/hardhat-toolbox-mocha-ethers';
export default defineConfig({
solidity: '0.8.22',
networks: {
paxeerMainnet: {
type: 'http',
chainId: 125,
url: 'https://public-rpc.paxeer.app/evm/reference',
accounts: [configVariable('PAXEER_PRIVATE_KEY')]
}
},
plugins: [hardhatToolboxMochaEthers]
});Store your deployer key in Hardhat's encrypted keystore (no plaintext .env file needed):
npx hardhat keystore set PAXEER_PRIVATE_KEYWallet Setup
Configure MetaMask or any EVM wallet with Paxeer:
const paxeerMainnet = {
chainId: '0x7d', // 125 in hex
chainName: 'Paxeer',
nativeCurrency: { name: 'Paxeer', symbol: 'HPX', decimals: 18 },
rpcUrls: ['https://public-rpc.paxeer.app/evm/reference'],
blockExplorerUrls: ['https://paxscan.io']
};
Step 2: Translate Your Solana Program
Common Pattern Translations
Initializing State
pub fn initialize(ctx: Context<Initialize>, initial_value: u64) -> Result<()> {
let state = &mut ctx.accounts.state;
state.value = initial_value;
state.authority = ctx.accounts.authority.key();
state.bump = ctx.bumps.state;
Ok(())
}
#[account]
pub struct State {
pub value: u64,
pub authority: Pubkey,
pub bump: u8,
}Access Control
// Solana: Check signer matches authority
pub fn restricted_action(ctx: Context<RestrictedAction>) -> Result<()> {
require!(
ctx.accounts.authority.key() == ctx.accounts.state.authority,
ErrorCode::Unauthorized
);
// ... action
Ok(())
}
#[derive(Accounts)]
pub struct RestrictedAction<'info> {
#[account(mut)]
pub state: Account<'info, State>,
pub authority: Signer<'info>,
}Error Handling
// Solana: Custom error enum
#[error_code]
pub enum ErrorCode {
#[msg("Insufficient balance")]
InsufficientBalance,
#[msg("Invalid amount")]
InvalidAmount,
#[msg("Unauthorized")]
Unauthorized,
}
// Usage
require!(amount > 0, ErrorCode::InvalidAmount);Events/Logs
// Solana: Emit event macro
use anchor_lang::prelude::*;
#[event]
pub struct TransferEvent {
pub from: Pubkey,
pub to: Pubkey,
pub amount: u64,
}
// Emit
emit!(TransferEvent {
from: ctx.accounts.from.key(),
to: ctx.accounts.to.key(),
amount,
});Step 3: Frontend Migration
SDK Comparison
| Solana | Paxeer EVM | Notes |
|---|---|---|
@solana/web3.js | ethers.js / viem | Core blockchain interaction |
@coral-xyz/anchor | typechain | Type-safe contract interaction |
@solana/wallet-adapter | wagmi / RainbowKit | Wallet connection |
| Phantom, Solflare | MetaMask, Rabby, Backpack | Popular wallets |
Code Translation
import { Connection, PublicKey } from '@solana/web3.js';
import { Program, AnchorProvider } from '@coral-xyz/anchor';
// Connect
const connection = new Connection('https://api.mainnet-beta.solana.com');
const provider = new AnchorProvider(connection, wallet, {});
const program = new Program(idl, programId, provider);
// Read state
const state = await program.account.state.fetch(stateAddress);
// Send transaction
const tx = await program.methods
.increment()
.accounts({
state: stateAddress,
authority: wallet.publicKey
})
.rpc();Step 4: Testing Your Migrated Code
Test Framework Comparison
import * as anchor from '@coral-xyz/anchor';
import { Program } from '@coral-xyz/anchor';
import { Counter } from '../target/types/counter';
describe('counter', () => {
const provider = anchor.AnchorProvider.env();
anchor.setProvider(provider);
const program = anchor.workspace.Counter as Program<Counter>;
it('Initializes', async () => {
const counter = anchor.web3.Keypair.generate();
await program.methods.initialize().accounts({ counter: counter.publicKey }).signers([counter]).rpc();
const account = await program.account.counter.fetch(counter.publicKey);
expect(account.count.toNumber()).to.equal(0);
});
});Step 5: Deploy and Verify
Deploy to Mainnet
# Hardhat
npx hardhat run scripts/deploy.ts --network paxeerMainnet
# Foundry
forge create --rpc-url https://public-rpc.paxeer.app/evm/reference \
--private-key $PRIVATE_KEY \
src/Counter.sol:Counter
Verify Contract
# Hardhat
npx hardhat verify --network paxeerMainnet <CONTRACT_ADDRESS>
# Foundry
forge verify-contract \
--chain-id 125 \
--verifier sourcify \
<CONTRACT_ADDRESS> \
src/Counter.sol:Counter
Common Migration Pitfalls
1. Expecting Rent
// ❌ Wrong: No need to check rent exemption
require(address(this).balance >= rentExempt, "Not rent exempt");
// ✅ Correct: Just use the contract normally
// Storage persists without rent payments
2. Manual Account Validation
// ❌ Wrong: Over-engineering account checks (Solana habit)
require(accountOwner == expectedOwner, "Invalid account owner");
// ✅ Correct: EVM handles this via contract addresses
// msg.sender is already authenticated
3. Expecting Explicit Parallelization
// ❌ Wrong: Trying to declare "accounts" for parallelization
function swap(address[] memory accounts) external { ... }
// ✅ Correct: Write normal code, Paxeer handles execution
function swap(address tokenIn, address tokenOut, uint256 amount) external { ... }
4. Using Lamports Mental Model
// ❌ Wrong: Solana-style lamports
uint256 amount = 1_000_000_000; // 1 SOL in lamports
// ✅ Correct: Use wei (18 decimals for HPX)
uint256 amount = 1 ether; // 1 HPX = 1e18 wei
uint256 amount = 1e18; // Same thing
Ecosystem Infrastructure
Available on Paxeer
| Component | Solana Equivalent | Paxeer Address/Info |
|---|---|---|
| Multicall3 | N/A | 0xcA11bde05977b3631167028862bE2a173976CA11 |
| Permit2 | N/A | 0xB952578f3520EE8Ea45b7914994dcf4702cEe578 |
| CREATE2 Factory | N/A | 0x0000000000FFe8B47B3e2130213B802212439497 |
| Oracle | Pyth Network, Switchboard | Pyth, Redstone, Chainlink |
| Bridge | Wormhole | LayerZero, Wormhole, more |
Helpful Resources
Learning Solidity
- EVM with Foundry - Foundry setup guide and starter tutorial
- EVM with Hardhat - Hardhat setup guide and starter tutorial
- Solidity Resources - Curated solidity learning resources
- CryptoZombies - Interactive Solidity tutorial
- Solidity by Example - Pattern reference
Paxeer-Specific
- Divergence from Ethereum - Technical differences
- Ecosystem Contracts - Canonical addresses