8 min read

Jump Into Solana — for EVM devs

Jump Into Solana — for EVM devs
Soooolana vs. EEEEEVM

I started with EVM development and then learned Solana development after two years. It was different in many senses, so I want to highlight some of the differences. Note: This is a developer's guide.

(Content will be corrected & updated as I get more time on hand...)


A crude analogy

An EVM contract is a class, a Solana program is a function.

Consider a contract (in TypeScript) that sets and returns money value. You might notice that the EVM contract is stateful, while the Solana program is stateless.

// EVM contract
class Contract {
	private _money: number;
	
	public getMoney() {
		return this._money;
	}
    
	public saveMoney(m: number) {
		this._money += m;
	}
}
// Solana program
function Program_getMoney(money_storage: Account) {
	const money = money_storage.bytes.slice(0, 4);
	return parseInt(money);
}

function Program_saveMoney(money_storage: Account, m: number) {
	money_storage.bytes[0] = m;
}

Stateful vs. Stateless

EVM contracts hold some data, while Solana programs don't hold any data. You can sort of think of it as running AWS EC2 vs. serverless Lambda.

Example: Token

On EVM, an ERC-20 contract stores the ledger of user balances. When an app needs to know the balance of its users, it calls balanceOf(address) to query the in-contract ledger.

On Solana, programs don't store any data — there's no ledger to track the balances! Instead, we directly input all the necessary data to the program every time we call it. Thus, the sole purpose of a program is to take in some data and modify them.

Q: Where do we store those data?
A: Accounts!


Accounts on Solana

Accounts are some blobs of data on Solana exposed by its public address. Everything on Solana is an account, for example:

  • User's wallet (signable by private key)
  • Program (executable data)
  • Some other data (to be used in programs)

In fact, SVM is a generalization of EVM: A "contract" is an account holding both the program (executable data) and "some other data" used in the program.

Rents for Accounts

When accounts are created on Solana, they need to store a minimum amount of SOL to meet "rent exemption." From the official doc:

Keeping accounts alive on Solana incurs a storage cost called rent because the blockchain cluster must actively maintain the data to process any future transactions.

This is different from Bitcoin and Ethereum, where storing accounts doesn't incur any costs.

If an account's SOL balance stays below the minimum rent-exemption amount, the account will eventually get purged. You can also "close" an account, which destroys the memory allocated for its data and returns the rent-exemption SOL (to the creator of the account).

"Everything is an account" enables parallelization

Imagine 1000 contracts are trying to read Bob's USDC balance to determine if he's a whale, all at the same time.

On EVM, 1000 contracts read the USDC balance sequentially (one by one). Contract_A reads Bob's balance, then Contract_B reads the balance, then Contract_C, ...

On Solana, 1000 programs read the USDC balance in parallel (all at once). SVM does some magic, and now Contract_A, Contract_B, ... reads Bob's balance at the same time.

Here's Mr. Bean. He counts the sheep one by one. Soon enough, he realizes that none of them moves (value isn't changing), so he counts them all at once... sort of.


No msg.sender or tx.origin

Calling a function on SVM program is just passing all the necessary accounts, which means the program has no context of "who is calling" itself. On EVM, we get msg.sender and tx.origin because a transaction is always initiated by EOA (user wallet).

On SVM, the closest we have to msg.sender is "is_signer = true" flag for the input accounts.


Hotspots + Local Fee Market

On EVM, everyone pays the same gas fee, plus the tips. All transactions have their positions in a block, and they get executed sequentially (hence the example above). All contracts are accessed at most once per transaction in EVM!

On Solana, accounts are accessed many times simultaneously! As long as no one is modifying an account's data, SVM can guarantee that the data will be the same for all those accesses.

Hotspots are programs that are accessed many times for writes. If 100 programs try to write to an account (e.g. NFT mint program) at the same time, it becomes a hotspot. SVM can identify hotspots and mitigate chain congestion by introducing a local fee market.

hotspot => write contention for accounts

Local Fee Market

Recall that EVM has a one-gas-fee-for-all-users model. SVM can create multiple fees for particular users of the chain, by introducing localized fee markets.

The APE sage, EVM vs. SVM

It's 2021, and the APE token mint is going viral. Everyone is calling the "mint" function, paying crazy amounts of tips.

On EVM, the gas fee skyrockets to $100+. EVERYONE has to pay that gas to get their transactions included anytime soon. If you're trying to buy coffee with USDC, well, you need to pay $100 to pay $5.

On SVM, the APE program is categorized as a hotspot and gets a limit on how much block space it can occupy. All minters fight for that limited sub-block space, while other non-minters can use the rest of the block space for sub-cent transactions.


Programs deal with Accounts

So, "everything is an account" is unique in Solana. For programs to work correctly, they need to know which accounts they can modify.

For example, to transfer tokens, you have to input some data accounts like:

user_account = readable, signer
user_another_account = readable, writable (mut)
user_associated_token_account = readable (mut)

Wait... what is an Associated Token Account (mut)?
> Let's dive into PDA first.


Program Derived Address (PDA)

From Anchor's doc:

Unlike normal addresses, PDAs are not public keys and therefore do not have an associated private key. There are two use cases for PDAs. They provide a mechanism to build hashmap-like structures on-chain and they allow programs to sign instructions.

PDAs don't have any private key associated — they are accounts owned by programs (as the name implies), which are also accounts. You get PDAs by deriving the public key using "seeds" (words) and bumping them off the elliptic curve.

A rough analogy: a PDA account is a programmable EOA/Contract on EVM, where the owner is a contract!

From https://solanacookbook.com/core-concepts/pdas.html

How do we "prove" that a program owns a PDA account? The seeds used to derive the PDA include the program's ID (contract address). SVM ensures that PDAs are marked as writable (content can be modified) only when the signer's ID matches the program ID in the seed.

One prominent use case of PDA is:

"Associated" Token Accounts

One question I had when learning Solana: If there's no ERC-20 contract storing the balance of each user on Solana, how do we know who owns how much?

Answer: There are programs called Token Program and Associated Token Program (ATP). In particular, ATP manages PDAs that store the token balance of each user.

Token Program allows creating and interacting with SPL tokens. Here, the SPL token is like the ERC-20 standard. ATP creates and controls Associated Token Accounts (ATA), which are PDAs!

An ATA stores the token balance of a user. Revisiting the token example, if you need to transfer Alice's USDC to Bob, you need to input the following accounts:

  • USDC token "mint" — stores all metadata of a token, e.g. total supply
  • Alice's ATA — stores the Alice's USDC balance
  • Bob's ATA — stores the Bob's USDC balance
  • Token Program — debits Alice's ATA & credits Bob's ATA
  • Associated Token Program — manages ATAs

Token Accounts

ATP is the recommended way to manage the token balance of users, but not the only way. In fact, ATP is just an abstraction to manage Token Accounts, which stores the token balance of each user.

When ATP creates (associated) Token Accounts, it derives the PDAs from its program ID. Thus, ATAs are only modifiable by calling the ATP's functions. This, by design, ensures that the token accounts are handled correctly.

You can also write a program that derives your own PDA for Token Accounts. Those PDA accounts would be owned by the program.

Token Mints

Token mints are accounts that hold the token's metadata, e.g. USDC's total supply and mint admin (authority). The name is a bit confusing because it sounds like it can "mint" like EVM, but it's actually just a blob of data for a token.


Anyone can pay for transactions!

EVM requires the contract caller (EOA) to pay for the transaction fee.

On Solana, there is a feePayer attached to a transaction that pays for the fee. Usually, the feePayer is set to the person sending the transaction. However, one straightforward application is:

  • Alice signs a transaction to transfer USDC to the app
  • App receives the signed transaction, attaches it as a feePayer and signs it
  • App sends the transaction to the blockchain

Transactions can have more than one smart contract call!

On EVM, a transaction is atomic. If the transaction interacts with a smart contract, it packs the function signature, followed by the parameters.

On SVM, interactions with a program (smart contract) are known as instructions, which are atomic. Instructions can be packed into a transaction, which has a limit of 1232 bytes. So, if there are three instructions of size 300 bytes each, you can send them as one transaction!


No Re-entrancy

EVM allows unlimited re-entrancy for smart contracts, often leading to re-entrancy exploits. SVM has a call stack limit of 4, meaning that cross-program invocations (CPI) are limited.


No Hardhat or Foundry, but Anchor!

Yeah, there's no beloved Hardhat or Foundry on Solana. But there's the Anchor framework, which makes developing on Solana much easier!

A crude comparison: Anchor is like Solidity.

Well, then programming in raw Solana Rust is like Yul on EVM. It's great if you care about optimization or other super-detailed things. Otherwise, it's much better to stick with Anchor (Solidity) for general purposes.

More on language

Compute Units, not Gas

On Solana, any atomic actions like addition or subtraction use some "compute unit," or CU. So, calling a function (instruction) on a Solana program costs some compute fees.

Which brings us to:

Compute is MUCH CHEAPER than Storage

Solana's main storage lives on RAM, not SSD. This enables much faster execution at the cost of limited on-chain storage for nodes.

On EVM, compute is cheaper than storage, but the difference is much bigger on Solana.

Add SIMD-0017: Priced Compute Units by anoushk1234 · Pull Request #19 · solana-foundation/solana-improvement-documents
ProblemAll transactions are required to pay the same base fee irrespective of the amount of compute units they use. This a spam/ddos vector and can cause blocks to reach their compute limit very f…
Detailed discussion on compute unit

Programs are upgradable by default

Because programs are stateless, upgrading is just replacing the old code with the new code. This is very different from EVM, where you need intricate approaches like proxies and storage management to enable "upgrading" programs.


RUST RUST RUST

You write Solana's programs in Rust (or C, if you insist). But if you insist, you can use Solang to mimic that EVM development experience, to a certain degree.


The Solana community is full of stellar and thoughtful yet generous people. For example:

Read the Replies 👀