作为一名在以太坊生态摸爬滚打多年的开发者,我最近刚完成了第一个Solana项目——一个类似pump.fun的meme发B平台的合约开发。
说实话,刚开始接触Solana时,我内心是拒绝的——又要学新语言,又要适应新的开发范式,感觉像是要重新学编程一样。
但当我看到同样的代币发行功能,在Solana上的交易费用只有以太坊的千分之一,用户铸造代币的体验从等待数分钟变成几秒钟完成时,我意识到这种学习是值得的。
今天我想以一个"过来人"的身份,从开发者的视角来梳理一下这两条链的差异,巩固知识的同时,也希望能帮你在30分钟内建立起对这两个生态的整体认知。
为了直观感受两者的差异,我们先从最核心的开发语言入手,对比Solidity和Rust的合约编写方式。
在以太坊,我们用Solidity写合约,代码结构相对直观:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract SimpleStorage {
uint256 public storedData;
function set(uint256 x) public {
storedData = x;
}
function get() public view returns (uint256) {
return storedData;
}
}
而在Solana,我们用Rust写程序,这里的"程序"相当于以太坊的"合约":
use anchor_lang::prelude::*;
#[program]
pub mod simple_storage {
use super::*;
pub fn set(ctx: Context<SetData>, data: u64) -> Result<()> {
ctx.accounts.storage_account.data = data;
Ok(()) // 返回成功,Result<()>表示操作成功或失败
}
}
#[derive(Accounts)]
pub struct SetData<'info> {
#[account(mut)]
pub storage_account: Account<'info, StorageData>,
}
#[account]
pub struct StorageData {
pub data: u64,
}
刚看到Solana的代码时,我的第一反应是:这也太复杂了吧!但深入了解后我发现,这种设计其实有其深层原因。
Solana强制你明确声明每个账户的用途和权限,虽然代码量增加了,但安全性和可并行性大大提升。
在以太坊,合约地址是通过RLP编码和哈希算法计算得出的,比较确定:
// CREATE方式:keccak256(rlp([sender, nonce]))
// CREATE2方式:keccak256(0xFF + sender + salt + keccak256(init_code))
而Solana的Program ID生成方式更加灵活。你可以:
我刚开始觉得这种灵活性是个负担,但后来发现这为跨链桥接和多签钱包提供了更多可能性。
明确了地址生成后,接下来我们看看如何让前端与链上程序交互,这就是ABI和IDL的用武之地。
在以太坊,我们用ABI(Application Binary Interface):
forge compile # 生成ABI文件
在Solana,我们用IDL(Interface Description Language):
anchor build # 生成IDL文件
两者本质上都是合约/程序的"说明书",告诉前端如何与链上代码交互。
不过IDL的表达能力更强,能描述更复杂的数据结构。Anchor会自动生成IDL,供JavaScript/TypeScript前端调用。
在开发中,合约升级是常见需求,两者的实现方式却大相径庭。
在以太坊,如果你想升级合约,必须使用代理模式:
// 需要复杂的代理合约架构
contract Proxy {
address public implementation;
// ... 复杂的delegatecall逻辑
}
而Solana原生支持程序升级,只要你还持有Program ID的私钥:
solana program deploy updated_program.so
当然,两者都可以选择"放弃控制权"——在以太坊是将代理合约的owner设为零地址,在Solana是将upgrade authority设为None,让合约或程序变成永久不可变的。
但需要注意的是,Solana的升级机制要求妥善保管Program ID私钥,否则可能导致程序被恶意升级。
这是两条链最根本的差异之一。在以太坊,合约代码、用户数据都存放在一起,比如所有用户的代币余额与合约代码都存在一个合约地址中:
mapping(address => uint256) public balances; // 所有余额在一个mapping里
而Solana采用账户分片存储,合约代码与用户数据分开存放(这样合约代码可以复用),每个用户的代币余额都有独立的账户:
用户钱包 -> 代币账户A -> 存储USDC余额
-> 代币账户B -> 存储SOL余额
刚开始我觉得这种设计很奇怪,为什么要搞这么复杂?后来我明白了,这是为并行处理做的架构设计。
更重要的是,这种设计带来了巨大的存储优势。在以太坊,每个ERC20代币都需要部署完整的合约代码,即使这些合约逻辑几乎完全相同。想象一下,链上有数万个ERC20代币,就意味着有数万份几乎重复的合约代码在浪费存储空间。
而Solana的SPL Token标准让全链只需要一份Token程序代码,所有代币都通过这个程序来管理。这种设计让Solana的存储效率提升了不止一个数量级,真正做到了代码复用的极致。
需要注意的是,Solana的账户需要质押lamports来支付存储费用(基于账户大小)。当质押金额达到免租阈值(约2年的租金)时,账户就永久免租,不会被系统回收。这种设计确保了链上存储的可持续性,避免了状态爆炸问题。我感觉这一点是借鉴了EOS链。
前面讲的存储架构差异,实际上直接影响了两条链的执行效率。
在Ethereum中,EVM是单线程的,就像一个收银员要处理所有顾客:
交易1: 给Bob铸造NFT [等待]
交易2: 给Alice转代币 [等待]
交易3: 执行Swap操作 [等待]
在Solana中,SealevelVM支持并行执行,就像有多个收银员同时工作:
交易1: 给Bob铸造NFT [并行执行]
交易2: 给Alice转代币 [并行执行]
交易3: 执行Raydium Swap [并行执行]
这就是为什么Solana的TPS能达到上万,而以太坊只有十几的根本原因。 当然,并行执行有个前提:交易不能涉及相同的账户,否则还是得排队处理。 比如:给Bob铸造NFT的同时,还给Bob转Token,这种多笔交易针对同一个账户的情况,就得上锁、按顺序执行了。
除了执行层的差异,两条链在共识层也采用了截然不同的方案。
以太坊2.0使用PoS(权益证明),验证者需要相互确认区块顺序。
Solana使用PoH(历史证明),通过SHA256哈希链创建可验证的时间戳。在Solana中,时间被分割成slot(时隙,约400毫秒的时间单位),每个slot相当于传统区块链中的一个区块位置:
Hash1 = SHA256("data1")
Hash2 = SHA256(Hash1 + "data2")
Hash3 = SHA256(Hash2 + "data3")
从技术层面来类比:PoH就像是一个数字时钟,每个哈希都证明了前一个事件的发生时间,形成了不可篡改的时间序列。这样验证者就不需要花时间相互协商"谁先谁后"。
从生活层面来类比:想象一下拍照时的连拍功能,每张照片都有时间戳,你可以清楚地知道每个瞬间的先后顺序,不需要问其他人"这张照片是什么时候拍的"。PoH就是给区块链交易打上了这样的时间戳。
这种设计让验证者无需相互沟通就能确定事件顺序,大大提升了效率。
区块链技术面临的经典"不可能三角"包括:去中心化、安全性、可扩展性,很难同时最大化这三个特性。
优势:
硬件门槛:
优势:
硬件门槛:
权衡:
从实际开发经验来看:
两条链都在朝着平衡的方向发展:Ethereum通过Layer 2提升性能,Solana在优化网络稳定性和降低硬件门槛。
在以太坊,一个账户可以持有各种代币:
0x123...abc (用户账户)
├── ETH: 1.5
├── USDC: 1000
└── DAI: 500
在Solana,每种代币都需要专门的代币账户:
9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM (用户钱包)
├── TokenAccount1: 1000 USDC
├── TokenAccount2: 500 RAY
└── TokenAccount3: 1 NFT
这种"专款专用"的设计初看繁琐,但其实更像是HD钱包的树状结构,每个代币都有专门的账户管理,便于权限控制和资产追踪。
从安全性角度看,这种设计也很像HD钱包:如果你丢了主钱包的私钥,就相当于丢了根密钥,所有关联的代币账户都会失去控制权。但好处是,你可以为不同用途的代币设置不同的权限级别。
PDA(Program Derived Address)是Solana独有的概念,它的核心作用是作为中立的执行者来处理用户交易。
在以太坊中,用户直接与合约交互:
// 用户直接调用合约方法
function transfer(address to, uint256 amount) external {
_transfer(msg.sender, to, amount); // msg.sender就是用户地址
}
而在Solana中,PDA充当"中间人"角色:
#[derive(Accounts)]
pub struct TransferTokens<'info> {
#[account(mut)]
pub from_token_account: Account<'info, TokenAccount>,
#[account(mut)]
pub to_token_account: Account<'info, TokenAccount>,
pub authority: Signer<'info>, // 用户签名授权
pub pda_authority: AccountInfo<'info>, // PDA作为执行者
}
pub fn transfer_tokens(ctx: Context<TransferTokens>, amount: u64) -> Result<()> {
// PDA代表程序执行转账,而不是用户直接执行
let bump = *ctx.bumps.get("pda_authority").unwrap();
let seeds = &[b"authority", ctx.accounts.authority.key().as_ref(), &[bump]];
token::transfer(
CpiContext::new_with_signer(
ctx.accounts.token_program.to_account_info(),
token::Transfer {
from: ctx.accounts.from_token_account.to_account_info(),
to: ctx.accounts.to_token_account.to_account_info(),
authority: ctx.accounts.pda_authority.to_account_info(), // PDA执行
},
&[&seeds] // PDA的签名种子
),
amount,
)
}
关键差异:
这种设计让Solana程序具有更强的控制能力,可以在用户授权后自主执行复杂的多步操作,这是pump.fun等复杂DeFi应用能够高效运行的基础。
这就像是一个去中心化的托管执行者:用户保持资产所有权,但交易执行权被委托给经过验证的程序逻辑。
换个更贴切的比喻:这就像智能合约托管的公司账户,用户是"股东",PDA是"受托管理人",程序是"公司章程"。资金所有权归股东,但每笔支出都必须按章程执行。
在以太坊生态,虽然日常开发中Hardhat用得更多,但我选择Foundry作为对比案例,主要是因为Foundry在合约调试方面更加便捷,测试框架也更加先进。
以太坊使用Foundry:
forge create SimpleStorage --private-key $PRIVATE_KEY --rpc-url $RPC_URL
Solana使用Anchor:
anchor deploy # 自动将编译完的程序文件部署到Solana链上
说实话,Anchor的开发体验更加流畅,集成度更高。anchor deploy命令会自动处理编译和部署的整个流程,比以太坊的工具链更加便捷。Anchor还提供了强大的测试框架和solana-program-test工具,让单元测试变得更加简单。
ERC20的权限控制:
function freeze(address account) public onlyOwner {
frozenAccounts[account] = true;
}
SPL Token的权限系统更加细化,包括:
// Mint Authority - 控制铸造权限
// Freeze Authority - 控制账户冻结权限
// Close Authority - 控制账户关闭权限
// 任何持有者都可以burn自己的代币(无需特殊权限,降低了权限滥用的风险)
这种细粒度的权限设计让代币管理更加灵活和安全。接下来看看数值精度的差异。
两条链在数值精度设计上各有考虑:以太坊使用wei作为最小单位,精度为18位小数(1 ETH = 10^18 wei),这与传统金融系统的精度要求相符;
而Solana使用lamports作为最小单位,精度为9位小数(1 SOL = 10^9 lamports),在保证足够精度的同时减少了计算复杂度。
以太坊查询余额:
uint256 balance = address(this).balance; // 单位:wei (10^-18 ETH)
Solana查询余额:
let balance = account.lamports(); // 单位:lamports (10^-9 SOL)
在Solana中,PDA账户之间的lamports(主要用于支付交易费和账户租金)转移需要通过程序内部逻辑实现,而普通账户间转账需要调用System Program(系统程序)。
System Program是Solana的核心程序之一,负责创建账户、分配空间、转移lamports等基础操作。
以太坊:
Solana:
接下来看看前端开发体验的差异。
这里我选择viem而不是ethers.js作为以太坊的例子,主要是因为viem提供了更好的TypeScript支持、更清晰的API设计和更高的性能。
查询余额:
// 以太坊 (viem)
const balance = await publicClient.getBalance({
address: '0x123...'
})
// Solana
const balance = await connection.getBalance(
new PublicKey('9WzDXwBbmkg...')
)
转账:
// 以太坊
const hash = await walletClient.sendTransaction({
to: '0x456...',
value: parseEther('1.0')
})
// Solana
const transaction = new Transaction().add(
SystemProgram.transfer({
fromPubkey: fromPublicKey,
toPubkey: toPublicKey,
lamports: 1000000000
})
)
const signature = await connection.sendTransaction(transaction, [payer]);
以太坊开发准备:
Solana开发准备:
以太坊开发流程:
# 1. 启动本地节点(无限测试ETH)
anvil
# 2. 编译
forge build
# 3. 测试
forge test
# 4. 部署到本地测试网
forge create Contract --private-key $PRIVATE_KEY --rpc-url <http://localhost:8545>
# 5. 部署到主网
forge create Contract --private-key $PRIVATE_KEY --rpc-url $MAINNET_RPC
# 6. 验证合约源代码
forge verify-contract --chain mainnet $CONTRACT_ADDRESS $CONTRACT_NAME
Solana开发流程:
# 1. 启动本地测试验证器(无限测试SOL)
solana-test-validator
# 2. 编译
anchor build
# 3. 测试(在本地测试网环境)
anchor test
# 4. 切换到测试网并获取测试币
solana config set --url testnet
solana airdrop 2
# 5. 部署到测试网
anchor deploy
# 6. 部署到主网
solana config set --url mainnet-beta
anchor deploy
在本地节点环境中,你拥有无限的测试币,这对于跑通程序基本逻辑、测试各种边界情况非常有用。
如果你熟悉对接Uniswap V2/V3,那么Raydium就是Solana上的对应版本:
两者的核心逻辑相似,但Raydium得益于Solana的并行处理能力,交易速度更快,成本更低。
Raydium作为Solana生态的头部DEX,加上pump.fun等项目的带动,已经成为Solana DeFi的重要基础设施。
以太坊交易结构:
Transaction {
nonce, gasPrice, gasLimit, to, value, data, v, r, s
}
Solana交易结构:
Transaction {
signatures, message: {
header, accounts, recentBlockhash, instructions
}
}
Solana的交易结构更加复杂,但表达能力更强。其中:
recentBlockhash:最近的区块哈希,有效期约150个slot(约1-2分钟),用于防止交易重放攻击,相当于以太坊的nonce机制instructions:指令数组,一个交易最多可以包含几十个操作指令accounts:预先声明所有涉及的账户,这是实现并行执行的关键在以太坊中,虽然一个交易也可以执行多个操作,但需要在合约内部实现批量操作逻辑,比如:
// 仅为了说明批量操作,不建议这样转账
function batchTransfer(address[] memory recipients, uint256[] memory amounts) public {
for (uint i = 0; i < recipients.length; i++) {
transfer(recipients[i], amounts[i]);
}
}
而Solana原生支持在一个交易中包含多个不同的指令,就像把多个独立的操作打包在一起执行。
以太坊:
Solana:
finalized级别以确保绝对安全以太坊:
Solana:
confirmed或finalized级别确保安全以太坊:
# 部署后手动验证源代码
forge verify-contract --chain mainnet $CONTRACT_ADDRESS $CONTRACT_NAME
以太坊的合约代码在验证后可以在Etherscan等区块浏览器上透明查看,任何人都能看到完整的源代码。
Solana: 而Solana的程序在区块链浏览器上看不到源代码,需要一定手段才能自证清白。如果我现在部署了一个程序到Solana上,想要获得社区监督和信任,Alice可以通过以下流程验证:
solana program show $PROGRAM_ID # 下载部署到链上的编译文件
anchor build # 拿到github开源代码,然后经过你亲手编译后得到的编译文件
# 计算本地编译文件的哈希
sha256sum target/deploy/program.so
# 与链上下载的编译文件哈希对比
虽然流程相对复杂,但Solana社区正在积极开发更便捷的验证工具。
经过这段时间的Solana开发,我有几点比较深刻的感受:
学习成本: Solana的学习曲线确实更陡峭。从Solidity转到Rust,从EVM转到账户模型,需要重新思考很多问题。
开发效率: 适应期过后,Anchor的开发体验很不错,得益于rust语言,类型安全和并发控制做得很好。
生态成熟度: 以太坊的生态更加成熟,工具链更完善,但Solana发展很快,工具质量在快速提升。
稳定性考量: Solana的高性能伴随着一定的稳定性风险,而以太坊主网几乎从未停机,这在企业级应用中是重要考量因素。
去中心化程度: 以太坊拥有更多的验证者节点,去中心化程度更高,而Solana为了性能在一定程度上牺牲了去中心化。
性能差异: Solana的高吞吐量和低费用确实令人印象深刻,特别是在做高频交易相关的应用时,比如游戏、NFT市场等产品。
基于以上开发体验,我总结了一些实用的学习建议:
如果你正在考虑转型区块链开发,我最近在整理相关的学习资源和项目实战经验。感兴趣的话可以私信交流,一起探讨技术细节。
技术世界里没有万能的解决方案,区块链也是如此。Solana通过创新的架构设计实现了令人印象深刻的性能,但也承担了相应的权衡成本。以太坊虽然在性能上相对保守,但其在去中心化和稳定性方面的表现为整个DeFi生态提供了坚实基础。
选择哪条链,本质上是在选择不同的权衡策略。了解这些权衡,才能做出更明智的技术决策。
以太坊和Solana就像是两种不同的设计思路:一个追求稳定和向下兼容,一个追求性能和创新突破。
作为开发者,我们不必站队,而是要理解它们各自的优势和适用场景。在这个快速变化的Web3世界里,保持技术栈的多样性和学习的开放心态,或许比追求任何单一技术的精通都更重要。
毕竟,下一个改变游戏规则的创新,可能就来自于不同技术的碰撞与融合。
本文基于个人开发经验总结,如有遗漏或错误,欢迎指正。
基于全文检索与主题相似度
说在前面 在区块链世界里,钱包既是资产的钥匙,也是我们与DApp打交道的入口。 最近我在做一个基于Sui的靓号钱包生成器(比如生成0x666…888这种钱包地址),过程中踩了不少坑。也正因为这些坑,我开始深入研究Sui钱包的生成机制,意外发现了不少有趣的底层细节。 平时我主要在以太坊生态中开发,因此
关注我,带你转型Web3开发,分享我从2018年入行区块链以来的开发经验(DeFi/GameFi/NFT/DAO/OTC/钱包/Trading Bot/MEME Launch) 说在前面 在DApp开发中,随机数生成是个有趣的挑战。无论是为NFT赋予独特属性,还是在去中心化游戏中实现公平的抽奖,都离
说在前面 如果你正在Sui上开发DApp,并考虑在某些场景下为用户免除gas费,比如: - 交易机器人的用户跟单服务 - GameFi中的游戏状态同步和数据上报 - NFT市场的批量上架和管理操作 - DAO治理中的提案投票 - 社交应用中的点赞、评论、转发…… 那么你需要一个Gas Pool服务,