Jacky Gu

Solana开发笔记: Solana的代币与以太坊代币的区别

01 Jan 2025 Share to

Solana的代币与以太坊代币的区别

1- SPL Token

1.1- 基于 Solana 区块链

1.2- 采用 Rust 原生程序编写

1.3- 账户模型:一个代币一个mint账户,每个用户,每个代币数据都存储在独立账户中

每个用户的代币账户(token account)由mint、user Account计算得到:

如:前端可以由以下代码生成:

const referrerAta = await getAssociatedTokenAddress(
    mint,             // mint公钥
    wallet.publicKey, // 创建人公钥
    false,            // 是否在椭圆曲线上,如果是EOA账户创建,用false,如果是PDA账户创建,为true
    TOKEN_PROGRAM_ID  // SPL Token程序地址
)

后端由以下代码生成:

let ata = get_associated_token_address_with_program_id(
  &ctx.accounts.payer.key(),
  &ctx.accounts.mint.key(),
  &ctx.accounts.token_program.key(),
);

也就是说,涉及到代币的账户有以下几个:

  • 1- mint account:铸币账户,一个代币对应一个铸币账户;
  • 2- token account:用户的代币账户,每个用户的每个代币对应一个token account,相当于在银行的外汇户头,每个外汇有一个账户。
  • 3- EOA账户:即外部账户,也就是普通账户,在钱包里看到的账户,用于存储SOL原生代币,以及派生出各个token account,相当于银行的主账户。

    • 优点:提升安全性
    • 缺点:增加账户管理成本,比如要对一个代币解锁,需要对所有持有人账户都进行一个一个解锁

      1.4- 交易延迟 400ms / 吞吐量 50k+ TPS

      1.5- 主要钱包:Phantom、Solflare等

2- ERC20

2.1- 基于 EVM 虚拟机

2.2- 采用 Solidity 编写智能合约

2.3- 地址模型:所有用户的代币数据都存在一个ERC20智能合约中,直接存储于钱包地址

如以下代码:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract MiniERC20 {
    // 代币元数据
    string public constant name = "SimpleToken";
    string public constant symbol = "ST";
    uint8 public constant decimals = 18;
    
    // 核心数据结构
    mapping(address => uint256) private _balances; // <--- 保存不同账户的余额的mapping
    mapping(address => mapping(address => uint256)) private _allowances;
    uint256 private _totalSupply;
    
    // 必须事件
    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(address indexed owner, address indexed spender, uint256 value);

    constructor(uint256 initialSupply) {
        _mint(msg.sender, initialSupply * 10**decimals);
    }

    function totalSupply() public view returns (uint256) {
        return _totalSupply;
    }

    function balanceOf(address account) public view returns (uint256) {
        return _balances[account];
    }

    function transfer(address to, uint256 amount) public returns (bool) {
        _transfer(msg.sender, to, amount);
        return true;
    }

    function allowance(address owner, address spender) public view returns (uint256) {
        return _allowances[owner][spender];
    }

    function approve(address spender, uint256 amount) public returns (bool) {
        _approve(msg.sender, spender, amount);
        return true;
    }

    function transferFrom(address from, address to, uint256 amount) public returns (bool) {
        _spendAllowance(from, msg.sender, amount);
        _transfer(from, to, amount);
        return true;
    }

    // 转账的内部核心逻辑
    function _transfer(address from, address to, uint256 amount) internal {
        require(from != address(0), "ERC20: transfer from zero address");
        require(to != address(0), "ERC20: transfer to zero address");
        
        uint256 fromBalance = _balances[from];
        require(fromBalance >= amount, "ERC20: insufficient balance");
        
        _balances[from] = fromBalance - amount;
        _balances[to] += amount;
        emit Transfer(from, to, amount);
    }

    function _mint(address account, uint256 amount) internal {
        _totalSupply += amount;
        _balances[account] += amount;
        emit Transfer(address(0), account, amount);
    }

    function _approve(address owner, address spender, uint256 amount) internal {
        _allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }

    function _spendAllowance(address owner, address spender, uint256 amount) internal {
        uint256 currentAllowance = allowance(owner, spender);
        if (currentAllowance != type(uint256).max) {
            require(currentAllowance >= amount, "ERC20: insufficient allowance");
            _approve(owner, spender, currentAllowance - amount);
        }
    }

ERC20中关于用户代币余额都保存在_balance[]中,转账代码是:

uint256 fromBalance = _balances[from];
require(fromBalance >= amount, "ERC20: insufficient balance");

_balances[from] = fromBalance - amount;
_balances[to] += amount;
  • 优点:管理简便,如对一个代币解锁,只要给代币设置一个属性即可,无需操作所有持有人的账户。
  • 缺点:安全性较差

    2.4- 交易延迟15秒 / 吞吐量~30 TPS

    2.5- 主要钱包:Metamask、OKX、TrustWallet等