| name | eth-to-sol |
| description | Translate Ethereum/Solidity contracts and EVM mental models to production-grade Solana programs. Use for EVM-to-SVM account model, PDA, SPL Token, CPI, Solana security, transaction/fee/commitment, and Anchor migration guidance. |
eth-to-sol
Translate Ethereum/Solidity contracts to production-grade Solana programs. The goal is not a 1:1 port — it is Solana-native code plus a teaching artifact that makes every decision legible to a developer who knows Solidity well and Solana barely.
This skill ships standalone from any app that invokes it. Keep outputs scoped to programs, accounts, transactions, and developer-facing API integration. Do not produce UI artifacts such as React components, screens, CSS, app copy, or visual design guidance.
Two-pass protocol (hard rule)
Every translation produces two outputs in sequence. Do not collapse them.
- Pass 1 — Faithful port. A semantically identical Anchor program. No restructuring, no SPL CPI substitutions, no parallelism rework. It exists so the refactor's value is legible. Mark obviously un-Solana patterns with
// SMELL: comments rather than fixing them.
- Pass 2 — Solana-native refactor. Restructured for Solana primitives: SPL programs via CPI, per-entity PDAs, parallelism-friendly account layout, explicit rent/sizing, compute-budget awareness, program splitting where warranted. Production-ready.
If a contract is trivially served by an existing Solana program (e.g. a vanilla ERC-20), the optimized version will be drastically smaller than the naive port. That is the lesson.
Output contract
For an input named foo, produce exactly these artifacts:
| File | Contents |
|---|
01-original.<ext> | The input (Solidity, Vyper, etc.). Already present; do not rewrite. |
02-naive-port.rs | Pass 1. Compiles. Inline // SMELL: markers on antipatterns. |
03-optimized.rs | Pass 2. Production-ready, fully commented at non-obvious sites. |
04-diff.md | Structured diff. Group sections by theme (State model / Parallelism / Security / CPI & program reuse / Compute & rent / Idioms) — mirror the explanation log. Each section: short header, before/after snippets, file:line references to the two .rs files. |
05-explanation.md | The explanation log. One entry per change in 04-diff.md, grouped by theme. Schema below. |
When the optimized version meaningfully changes non-program integration (typically: SPL Token replaces a custom token surface, balance/aggregate lookups move off the program, or transaction/account lists change), append a ## Client/API integration notes section at the bottom of 05-explanation.md. Keep it to account derivation, required accounts, ATA creation, transaction construction, SDK call shape, and migration scoping. Do not include UI components, screens, styling, or app-specific copy. If the integration shift is minor, fold it into a relevant entry's Tradeoff instead.
05-explanation.md is the teaching surface. Treat it as a first-class deliverable, not a comment block.
Read first
Before producing any translation, internalize the EVM → SVM mental shift in translation/mental-model.md. The one-line summary: on Ethereum the contract knows where its state lives; on Solana the caller brings it. Every translation rule below is a consequence — if a step ever feels wrong, return to that file.
Decision tree — which sub-files to load
Default-load: translation/mental-model.md, translation/type-mapping.md, translation/pattern-mapping.md, security/arithmetic.md, security/account-validation.md, security/pda-canonicalization.md.
The default loads are non-negotiable. The mental-model file frames every other decision; arithmetic, account validation, and PDA canonicalization are the three security classes that bite every ported contract.
| Source contains | Also load |
|---|
| ERC-20 / fungible token | translation/stdlib-mapping.md, optimization/account-model.md, security/cpi-safety.md |
ERC-20 with _update / _beforeTokenTransfer override (fee-on-transfer, blacklist, paused-transfer, rebasing) | translation/stdlib-mapping.md (Token-2022 section); target Token-2022 with the matching extension (transfer fee, transfer hook, default account state, interest-bearing). Do not target classic SPL — the semantics cannot be expressed. |
| ERC-721 / ERC-1155 / NFT | translation/stdlib-mapping.md, optimization/account-model.md, optimization/pdas.md |
mapping(...) storage | optimization/account-model.md, optimization/pdas.md, optimization/parallelism.md |
| Ownable / AccessControl / roles | translation/stdlib-mapping.md, security/signer-checks.md |
| Custom modifiers | translation/pattern-mapping.md, security/signer-checks.md |
| External calls / interfaces | security/cpi-safety.md, security/reentrancy.md, optimization/program-splitting.md |
| Heavy arithmetic / fixed-point | security/arithmetic.md (also default-loaded) |
Hot-write global state (counters, totalSupply) | optimization/parallelism.md, optimization/account-model.md |
| Dynamic-sized state (arrays, mappings of unknown size) | optimization/rent-and-size.md, optimization/account-model.md |
| Multi-contract system | optimization/program-splitting.md, security/cpi-safety.md |
| Anything writing state after an external call | security/reentrancy.md, security/cpi-safety.md |
| Compute-pressured paths (multi-CPI swaps, loops in hot path, >300 expected CU per call) | optimization/compute-budget.md |
| Multiple account types owned by the program (type-confusion risk surface) | security/account-validation.md (also default-loaded) |
| Any PDA the program will sign for | security/pda-canonicalization.md (also default-loaded), optimization/pdas.md |
| Protocol takes a user-supplied token Mint as configuration (vault, AMM, lending market) | security/reentrancy.md, security/account-validation.md, security/cpi-safety.md |
| Vault/AMM/4626-shaped protocol (share/asset conversion math, deposit + withdraw + redeem semantics) | security/arithmetic.md (rounding-direction discipline), optimization/account-model.md (read aggregates from SPL Token), optimization/parallelism.md (read-only vault pattern) |
Time-delta math (now - last_update, accumulator periods) | security/arithmetic.md (clock-skew + negative-delta-cast pitfall) |
| Large account graph, payment batch, swap route, many recipients, or transaction-size concern | optimization/transactions-and-commitment.md, optimization/compute-budget.md |
| Priority fees, local fee markets, compute limit, recent blockhash, retry/expiry, or landing strategy | optimization/transactions-and-commitment.md, optimization/compute-budget.md |
| Settlement, irreversible off-chain action, indexer correctness, or commitment-level choice | optimization/transactions-and-commitment.md |
Client SDK/tooling/testing migration asks (@solana/kit, web3.js, Anchor client, LiteSVM, Bankrun, local validator) | optimization/transactions-and-commitment.md |
Always load every security/* file relevant to the constructs present. Security is non-negotiable.
Module 1B source alignment
This skill tracks Solana Enterprise Training Module 1B, "From EVM to SVM":
https://github.com/solana-foundation/solana-enterprise-training/tree/main/module-1b-from-evm-to-svm
Use the module as source alignment, not as shipped course UI. The skill should reference the engineering concepts below and avoid slides, quizzes, UI, or app-specific teaching surfaces.
| Module 1B topic | Skill coverage |
|---|
| Purpose, prerequisites, and learning objectives | This SKILL.md assumes a Solidity-fluent reader with baseline Solana awareness and scopes outputs to translation artifacts, not course delivery. |
| Mental-model shift: "you bring the state" | translation/mental-model.md |
| Account model translation: storage slots, mappings, PDAs, explicit signers, upgrades, IDL/logs/time | translation/mental-model.md, translation/type-mapping.md, translation/pattern-mapping.md, optimization/account-model.md, optimization/pdas.md |
| ERC-20 vs SPL Token: Mint, Token Account, ATA, allowances/delegates, decimals, token-account creation | translation/stdlib-mapping.md, optimization/account-model.md, security/cpi-safety.md |
Composability: EVM call vs Solana CPI and account-graph propagation | translation/mental-model.md, security/cpi-safety.md, optimization/program-splitting.md |
| Reentrancy as a structural property and the replacement failure modes | security/reentrancy.md, security/cpi-safety.md, security/account-validation.md |
| Solana program security checklist: ownership, signer checks, substitution, discriminators, arithmetic, CPI authority, safe close, rent | security/account-validation.md, security/signer-checks.md, security/pda-canonicalization.md, security/arithmetic.md, security/cpi-safety.md, optimization/rent-and-size.md |
| Transactions in practice: recent blockhash, expiry, versioned transactions, ALTs, compute budget, priority fees, local fee markets | optimization/transactions-and-commitment.md, optimization/compute-budget.md, optimization/parallelism.md |
| Commitment levels for settlement and indexing | optimization/transactions-and-commitment.md |
Developer workflow: @solana/kit, legacy web3.js, Anchor client, LiteSVM, Bankrun, local validator | optimization/transactions-and-commitment.md |
| Cumulative EVM/SVM reference | translation/mental-model.md plus the referenced translation, optimization, and security files |
| Quiz and slides | Do not ship; those are course surfaces, not skill runtime material. |
| Additional resources | Keep external: Solana Cookbook, Anchor Book, Neodyme Common Pitfalls, Sec3 audit checklist, Solana Program Examples, Helius priority-fee guide, and official Solana fee docs. |
Pre-flight checklist (gate on the optimized version)
Every item must hold before emitting 03-optimized.rs. If one fails, fix and re-check.
Tooling — Solana Developer MCP rust_autofixer
If your environment exposes the Solana Developer MCP (https://mcp.solana.com/mcp), the rust_autofixer tool is part of the workflow — not optional.
When to call it: every time you have produced or modified Anchor or Pinocchio Rust that you intend to ship. That includes 02-naive-port.rs and 03-optimized.rs, and any in-flight fix you apply after a failed cargo check.
How to call it: pass the full Rust source. Specify the framework (auto, anchor, or pinocchio) if the caller hasn't already.
The loop:
- Call
rust_autofixer on the current Rust.
- Apply every suggested fix (they are mechanical, structured, and safe).
- Call
rust_autofixer again.
- Repeat until
require_another_tool_call_after_fixing is false.
Only emit the artifact once the loop terminates. This is in addition to — not a replacement for — the pre-flight checklist above; the autofixer catches the structural-safety class of bug, the checklist catches design/idiom issues.
Do not use any other Solana MCP tool (list_sections, read_section, search, etc.) for this workflow. Stay scoped to rust_autofixer.
Explanation log opener
Before the first ## Theme heading, the explanation log opens with a short prose preamble. The preamble must, in this order:
- One paragraph: what the program does in EVM-developer terms. State the protocol the way you'd state it to a Solidity dev who's never seen the contract — "a one-shot ERC-20 crowdfund: supporters deposit tokens before a deadline; if the goal is met the creator claims the pot, otherwise supporters refund." Don't lead with what the example teaches; lead with what the program does.
- One paragraph: the Solana shape it ports to. What the program looks like on Solana at the same height of abstraction — "On Solana, the same protocol becomes one PDA per supporter plus a singleton fundraiser account; SPL Token handles the actual token movement via CPIs." Still no per-line / per-symbol detail.
Any optional context that follows (vocabulary list, reference-implementation link, etc.) comes after these two paragraphs, not before.
Explanation log schema
Each entry is exactly five fields. Keep them tight — one to four sentences each.
### <short title>
- **Title rules.** When the change has a Solidity counterpart (most state-model / security / idiom entries), frame the title as `Solidity-side → Solana-side` — e.g. `mapping(address => uint256) ledger → per-supporter PDA`, not `Vec<Contribution> → per-supporter PDA`. A Solidity-fluent reader of `03-optimized.rs` has not opened the naive port; titles that name naive-port Rust types (`Vec<X>`, `iter_mut().find(...)`, etc.) read as gibberish to them. When the change is Solana-only hygiene (no Solidity counterpart — bump caching, PDA seed consolidation, account-size optimizations), use a plain descriptive title without the arrow.
- **What:** the concrete change as a diff between the naive port and the optimized port. Reference the diff section or `file:line` in the .rs files. This is the LOW-LEVEL diff view; cite specific identifiers, function names, line numbers. Written for someone reviewing the diff side-by-side.
- **Annotation:** a self-contained explanation of THIS code (the optimized version) for a Solidity-fluent reader who is looking only at the optimized file and has never seen the naive port. State what the optimized code does at this point, why a Solidity developer's mental model has to shift here, and — when meaningful — what a naive translation would have done and why this shape is preferable. Do NOT cite the naive port by filename or reference any line outside the optimized file. Two to four sentences.
- **Why:** the platform-level reasoning, structured as a two-sided contrast for a Solidity-fluent reader. Lead the first sentence(s) with **"On Ethereum, ..."** and describe the EVM/Solidity paradigm the developer is bringing with them. Then lead the next sentence(s) with **"On Solana, ..."** and describe the paradigm that diverges. Keep it HIGH-LEVEL — platform mechanics, mental model, what serializes / what doesn't, who owns what, what the runtime guarantees. Save the per-line / per-symbol detail for `What:` and `Annotation:`. Avoid backtick code fragments here unless absolutely necessary.
- **Benefit:** what is gained. Be specific: CU saved, parallelism unlocked, security class avoided, code deleted.
- **Tradeoff:** what is given up. If nothing meaningful, say so and justify briefly.
What is the "code review" view of the diff; Annotation is the "reader of the final code" view. They cover different audiences — both are needed because the final code is shipped on its own, but the diff is also part of the artifact set.
Group entries under thematic headers: ## State model, ## Parallelism, ## Security, ## CPI & program reuse, ## Compute & rent, ## Idioms.
Explanation style — write for a Solidity-fluent reader who has never seen Solana
The reader knows Solidity well. They know financial systems. They have not internalized PDAs, the account model, SPL Token, rent, CPI, or Anchor's constraint vocabulary. The explanation log is where they bridge — every entry must land for that reader.
Rules
-
First-use translation, always inline. The first time any Solana-specific term appears in a given explanation log, give a short EVM analog in parentheses or em-dashes. Don't assume a glossary; weave it into the prose. After first use, the term is fair game.
Required glossing on first use (non-exhaustive):
- PDA — "PDA (Program-Derived Address — a deterministic account address derived from seeds the program controls; analog of a Solidity storage slot keyed by
(address, mapping) — but each PDA is its own account, not a slot inside the program)"
- SPL Token — "SPL Token (the shared on-chain token program every fungible token reuses on Solana — instead of each ERC-20 deploying its own contract, every token is just configuration on this one program)"
- CPI — "CPI (cross-program invocation — Solana's version of one contract
call-ing another, but every account the callee will touch must already be in the caller's transaction)"
- rent — "rent (a refundable SOL deposit every account pays to live on-chain; ~0.001 SOL per KB of account data, returned in full when the account is closed)"