Cairo 是一门受 Rust 启发的语言,它会编译成字节码,并在 Cairo 虚拟机上运行。Cairo 虚拟机是一个可以执行任何 Cairo 程序的零知识虚拟机(ZKVM)。与专门为智能合约构建的 Solidity 不同,Cairo 是一门被设计用于创建可证明程序的通用编程语言。
本系列教程重点介绍如何在 Starknet 上使用 Cairo 编写智能合约。我们不假设你拥有 Rust 或零知识证明的先验经验。但是,本系列教程假设你具备 Solidity 的先验经验。我们期望读者了解如何编写 ERC-20 和 ERC-721 代码,并在概念上了解 Uniswap V2 等 dApp 的工作原理。
Cairo 经过精心设计,可帮助 Solidity 开发者快速学习该语言,本系列突出了它们的相似之处,以便 Solidity 开发者可以重用从 Solidity 中获得的心智模型来快速理解 Cairo。
Cairo 入门
要开始编写 Cairo 智能合约,你需要 Scarb(Cairo 的包管理器和构建工具)以及 Starknet Foundry(用于开发、部署和测试 Cairo 智能合约的工具链)。
安装
要安装这些工具,请使用 starkup,它会自动安装 asdf,然后使用它通过一条命令来安装 Scarb、Starknet Foundry 和 Cairo 编译器。打开你的终端并输入以下命令:
curl --proto '=https' --tlsv1.2 -sSf https://sh.starkup.dev | sh
安装完成后,重启你的终端或运行:
source ~/.bashrc # or source ~/.zshrc if using zsh
验证安装:
scarb --version
snforge --version
你应该会看到类似以下的输出:

创建你的第一个项目
创建一个仅包含小写字母和下划线的空目录(例如 hello_world)。避免使用大写字母或破折号(-),因为 Scarb 包名必须遵循 snake_case 命名规范。
导航到该目录中,然后运行:
scarb init
出现提示时,从选项列表中选择 Starknet Foundry (default)。

这会创建一个简单的 Cairo 合约,用于存储和更新单个 balance 值(类似于 Foundry 为 Solidity 提供的默认 Counter 合约)。
项目结构
当你在代码编辑器中打开该项目时,你会看到以下项目结构:
hello_world/
├── src/
│ └── lib.cairo # Your main contract code
├── tests/
│ └── test_contract.cairo # Test files go here
├── Scarb.toml # Project configuration and dependencies
├── Scarb.lock # Lock file for exact dependency versions
├── snfoundry.toml # Starknet Foundry configuration
└── .gitignore # Git ignore file
src/是合约文件所在的位置。lib.cairo是主入口点;默认情况下,Scarb 会生成一个用于管理余额的简单HelloStarknet合约。tests/包含用于验证合约功能的测试文件。Scarb.toml定义了项目依赖、Cairo 编译器版本、包元数据和构建设置(类似于 Node.js 中的package.json或 Rust 中的Cargo.toml)。你可以通过它来管理合约所使用的库。Scarb.lock记录了确切的依赖项版本。snfoundry.toml用于配置 Starknet Foundry 设置:RPC 端点、账户配置和测试执行选项。Scarb.toml负责管理你的项目和依赖,而snfoundry.toml则用于配置 Foundry 工具。
设置语法高亮
如果你使用的是 VS Code 或其分支版本,请安装 Cairo 1.0 extension 以获取语法高亮功能。安装完成后,VS Code 将能识别 .cairo 文件,并提供自动补全和错误高亮等功能。请注意,伪造的 VS Code 扩展是常见的社会工程学攻击手段,因此请务必仔细核对发布者。

打开 src/lib.cairo 查看 Scarb 生成的代码。我们将在下一章解释其语法。

要编译你的合约,请运行:
scarb build
这将编译你的 Cairo 代码,并在 target/ 目录中生成编译后的合约文件。这些文件就是你将部署到 Starknet 上的内容。
随后,你可以使用以下命令测试该项目:
scarb test
与 Solidity 相似的 Cairo 概念
Cairo 智能合约拥有“状态变量(storage variables)”的概念,它支持 Solidity 开发者所习惯的数据类型,例如整数、字符串、映射、数组、布尔值等。
以下概念与 Solidity 具有 1:1 或近乎 1:1 的对应关系:
- 状态变量(storage variables)和存储槽(storage slots)
- 触发事件(emitting events)
- public、internal 和 view 函数
- require 语句
msg.sender、block.timestamp和block.number- constructor(构造函数)
- 用于声明外部函数的 interfaces(接口)
- 合约可以调用其他合约,并利用 ABI 了解如何进行调用
- 合约可以创建其他合约
- 交易需要消耗“gas”以防止垃圾信息
- OpenZeppelin 充当该语言事实上的“标准库”
与 Solidity 的主要区别
与 Solidity 相比,Cairo 合约具有以下特性和/或区别:
- Cairo 支持内存哈希映射(Solidity 仅支持存储映射)
- Solidity 的内存数组在声明时必须具有固定大小,但 Cairo 没有此限制
- Cairo 从 Rust 继承了更具表现力的控制流语法(例如模式匹配)
- 像 Rust 一样,Cairo 不是面向对象的,因此不支持继承。不过,Cairo 提供了其他组合代码的方式
- Solidity 合约通过代理模式(proxy patterns)进行升级;而 Cairo 合约可以直接升级其字节码,同时保持存储完好无损
- Cairo 中没有“原生代币(native token)”,因此也没有
msg.value。默认情况下,使用 STRK 代币(一种 ERC-20 代币)来支付 gas。你可以在此处的浏览器中查看该代币。 - Starknet 在协议层面内置了账户抽象,因此不存在所谓的“外部拥有账户(EOA)”
最后一点可能会让来自 EVM 兼容链的开发者感到有些困惑,但别担心,我们稍后会对此进行深入探讨。
在 Starknet 上创建账户
为了理解在 Starknet 上创建账户的生命周期,你可以使用 Ready(前身为 Argent)或 Braavos 钱包来创建一个钱包。下方的视频展示了使用浏览器扩展创建 Ready 钱包的过程。
安装钱包后,请注意它默认连接到主网。对于本教程,请点击你的 Ready 钱包顶部的网络选择器并选择 “Sepolia”,以切换到 Sepolia 测试网。然后按照下方视频中演示的流程在 Sepolia 上创建一个新账户:
接下来,复制你的钱包地址,并将其粘贴到 Starknet 区块浏览器中。你可以使用 Starkscan 或 Voyager。确保同时将浏览器也切换到 Sepolia 测试网(位于页面右上角)。
由于这是一个没有交易历史的全新钱包,浏览器还不会显示任何结果。

为了开始交易,我们需要 STRK 代币来支付 gas 费。前往 Starknet Faucet,粘贴你的钱包地址,并申请测试网代币。
初始化账户方法 1:发送一笔交易
Starknet 账户通过发送其首笔交易来进行初始化。以太坊中的外部拥有账户(EOA)作为简单的公私钥对存在,没有链上合约代码;而与此不同的是,Starknet 账户本身就是智能合约。然而,当你生成钱包地址时,这些账户合约并不会默认部署。部署它们的一种方式就是发送你的第一笔交易。此时,Starknet 会在一个步骤中自动部署账户合约并执行你的交易。
让我们从钱包向自己发送 1 STRK 来启动这个过程:
现在,再次将你的地址粘贴到浏览器中进行搜索。你会发现一个合约已经被部署到你的钱包地址上。这第一笔交易自动部署了你的账户合约,如下图所示:

初始化账户方法 2:直接部署智能钱包
或者,你也可以直接部署账户合约。在钱包中接收到 STRK 代币后,你会看到一个部署账户合约的选项。下面来自 Ready 的 GIF 动画 演示了在主网上进行此操作的过程(在 Sepolia 上的流程是一样的):

Starknet 没有 EOA
在 Starknet 中,不存在 EOA。每个地址都有字节码和存储。
那么,我们该如何区分普通的智能合约(如 DeFi 应用)和旨在存放“我们资金”的合约呢?
打算用作钱包的合约必须实现一个名为 SNIP-6 的特定特征(接口)。我们将在后续教程中详细讨论这一点,但现在只需知道,SNIP-6 合约必须实现一个 __execute__ 函数,这就是它接收用户指令的方式。
区别如下:
在以太坊中,私钥用于派生一个以太坊地址,其他智能合约可以将其视为一个账户。
在 Starknet 中,私钥不会转化为地址。相反,它们在调用 __execute__ 命令时用于进行身份验证。
这是以太坊和 Starknet 之间的另一个重要区别。在以太坊中,运行时会验证钱包交易的密码学签名。在 Starknet 中,具有 __execute__ 函数的钱包可以使用任何它想要的身份验证方法——这意味着 Starknet 可以在不进行硬分叉的情况下支持通行密钥(passkeys)和抗量子密码学身份验证等特性。所需的仅仅是编写智能合约账户来支持所需的密码学算法。
你的钱包中显示的“Starknet 地址”其实就是智能合约的地址(如果合约还不存在,则称为“反事实地址(counterfactual address)”)。当钱包应用(你设备上的链下软件)生成你的助记词时,它会派生一个私钥,并预测稍后该合约将被部署到哪里。为了方便起见,钱包将这个预测的地址作为你的“地址”显示给你,尽管在完成初始化之前该合约在链上还不存在。
你的“钱包”(链下软件)是不可能在 Starknet 上“拥有”任何东西的。在以太坊中签名可直接证明所有权,而不同寻常的是,Starknet 需要链上智能合约来持有资产。你的钱包应用只是创建签名,在执行交易(比如发送代币或调用其他合约)之前,由你的链上钱包合约对这些签名进行验证。我们将在后面关于账户抽象的章节中更深入地探讨这个概念。
注意较旧的 Cairo 版本
截至本文撰写时,目前的 Cairo 版本是 2.13.1。请注意,互联网搜索和 LLM 查询往往会返回为 Cairo 1.x 或更早版本(0.x)编写的代码,这些代码与 Cairo 2.x 并不兼容。不同版本之间的语法发生了显著变化,因此旧版本的代码将无法正常运行。在复制代码示例时,请务必检查对应的 Cairo 版本。
遇到卡顿时的提示(Prompting)技巧
像其他专为区块链设计的语言一样,Cairo 在网络上的普及度和讨论度不如 JavaScript 或 Python 等语言。
- 要修复编译问题,请询问如何在 Rust 中解决该问题。你所遇到的大约 80% 的编译问题,往往与在 Rust 中编写代码时遇到的编译问题相同。
- Scarb 与 Rust 的 cargo 非常相似。如果你在使用 Scarb 时遇到问题,在互联网搜索错误信息时,可以尝试用“cargo”替换“Scarb”,以增加找到相关解决方案的几率。
在下一章中,我们将编写我们的第一个 Cairo 程序。
本文是 Starknet 上的 Cairo 编程 系列教程的一部分