第三课:Solidity语言入门——从零开始写你的第一行智能合约

好,我们正式开始写代码了。

前面两章我们搭好了环境,也部署了第一个Hello World。但说实话,那个合约太简单了,连个变量都没有。这一章,我们来真正理解Solidity这门语言的核心概念。

我个人觉得,学Solidity就像学开车——你不需要先背完整本说明书,但得知道油门、刹车、方向盘是干嘛的。这一章就是带你认识这些「基本控件」。

3.1 Solidity概述:它到底是什么?

Solidity是一门面向合约的高级语言,运行在以太坊虚拟机(EVM)上。说白了,它就是用来写智能合约的。

你可能听说过JavaScript。Solidity的语法跟它有点像,但本质完全不同。JS跑在浏览器里,Solidity跑在区块链上。一旦部署,代码就不能改了——嗯,这一点我吃过亏,后面会讲。

核心特点:

  • 静态类型:每个变量必须声明类型,编译时检查
  • 图灵完备:理论上能算任何问题,但受Gas限制
  • 面向合约:合约就像Java里的类,有状态、有方法
  • 继承支持:可以复用已有合约的代码

我记得刚学Solidity时,最不习惯的就是「部署即不可变」。有一次我在测试网部署了一个合约,发现有个变量名拼错了,想改?没门。只能重新部署一个,然后通知所有人用新地址。所以,写合约之前一定要想清楚

3.2 合约结构:一个合约长什么样?

每个Solidity合约,就像一个小型服务器。它有状态(变量),有行为(函数)。来看一个最基础的结构:

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

contract MyFirstContract {
    // 状态变量
    uint256 public myNumber;

    // 构造函数
    constructor() {
        myNumber = 42;
    }

    // 函数
    function setNumber(uint256 _newNumber) public {
        myNumber = _newNumber;
    }

    function getNumber() public view returns (uint256) {
        return myNumber;
    }
}

我们来拆解一下:

  • 第一行:许可证声明。MIT表示开源,你也可以用其他许可证。不加的话编译会警告,但能跑。
  • 第二行:版本声明。^0.8.0表示兼容0.8.x所有小版本。我建议用^0.8.0以上,因为0.8版本修复了很多安全问题。
  • contract关键字:定义合约,后面跟合约名。首字母大写是惯例。
  • 花括号{}里:就是合约的「身体」了。

我的习惯:每个合约文件只放一个合约。虽然Solidity允许一个文件写多个合约,但分开管理更清晰。尤其是项目大了以后,你不想在几千行代码里找一个合约吧?

3.3 状态变量 vs 局部变量:一个天上,一个地下

这是新手最容易混淆的概念。我刚开始也搞混过,直到有一次在项目中写了个死循环,把状态变量当局部变量用,Gas烧得飞快……

状态变量:永久存储在区块链上。定义在合约内部、函数外部。每次读写都要花Gas。

局部变量:临时存储在内存中。定义在函数内部。函数执行完就消失,不花存储Gas。

看个对比:

contract VariableDemo {
    // 状态变量 - 存在链上
    uint256 public storedData;

    function doSomething(uint256 input) public {
        // 局部变量 - 存在内存
        uint256 temp = input * 2;

        // 把局部变量的值赋给状态变量
        storedData = temp;
    }
}

这里有个关键点:状态变量的读写成本远高于局部变量。写一次状态变量大约消耗20000 Gas,而局部变量几乎免费。所以,能不用状态变量就别用。

我曾经踩过的坑:在一个循环里反复修改状态变量。比如:

// ❌ 错误做法
for(uint i = 0; i < 100; i++) {
    total += i;  // total是状态变量,每次循环都写链上
}

// ✅ 正确做法
uint256 temp = 0;
for(uint i = 0; i < 100; i++) {
    temp += i;  // 局部变量,免费
}
total = temp;   // 只写一次链上

这个优化能让Gas消耗降低99%。真的,不夸张。

3.4 整型与布尔类型:最基础的数据类型

Solidity里的数据类型,咱们先从最常用的两个说起。

整型(Integer)

Solidity支持有符号和无符号整型:

  • uint:无符号整数(0到正无穷)。默认是uint256,即256位。
  • int:有符号整数(负无穷到正无穷)。默认是int256。
  • 你也可以指定位数:uint8, uint16, ..., uint256。位数越小,存储越省Gas。

举个例子:

contract IntegerDemo {
    uint8 public smallNumber = 255;   // 最大255
    uint256 public bigNumber = 1000;  // 随便多大
    int public negative = -42;        // 可以负数

    function add(uint256 a, uint256 b) public pure returns (uint256) {
        return a + b;
    }
}

这里要注意:Solidity 0.8.0之后默认做了溢出检查。如果你给uint8赋值为256,编译会报错。以前版本不会报错,而是直接归零——这导致过很多安全漏洞。

避坑指南:

  • 能用uint8就别用uint256,省Gas。但别为了省Gas把位数设得太小,导致溢出。
  • 做加减乘除时,记得考虑边界情况。比如两个uint8相加可能超过255。
  • 除法要小心:Solidity里整数除法会截断。7/2 = 3,不是3.5。

布尔类型(Boolean)

布尔类型只有两个值:truefalse。常用于条件判断:

contract BooleanDemo {
    bool public isActive = true;

    function toggle() public {
        isActive = !isActive;  // 取反
    }

    function check() public view returns (string memory) {
        if (isActive) {
            return "Active";
        } else {
            return "Inactive";
        }
    }
}

布尔类型在Solidity里有个小坑:它不能直接用于算术运算。你不能写 true + 1,编译会报错。但在某些语言里(比如JavaScript),这是允许的。嗯,这里要注意区分。

另外,布尔变量的存储成本跟uint8一样,都是1字节。但如果你把多个布尔变量打包在一起,可以进一步省Gas。这个我们后面讲结构体时会提到。

3.5 实战小练习:写一个计数器合约

理论讲完了,咱们动手写个东西。一个简单的计数器合约,把今天学的都用上:

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

contract Counter {
    // 状态变量:当前计数
    uint256 public count;

    // 构造函数:初始化计数为0
    constructor() {
        count = 0;
    }

    // 增加计数
    function increment() public {
        count += 1;
    }

    // 减少计数(但不能小于0)
    function decrement() public {
        // 先检查,再操作
        require(count > 0, "Count cannot be negative");
        count -= 1;
    }

    // 重置计数
    function reset() public {
        count = 0;
    }

    // 获取当前计数(view表示不修改状态)
    function getCount() public view returns (uint256) {
        return count;
    }
}

这个合约里:

  • count是状态变量,存在链上
  • incrementdecrementreset修改了状态变量
  • getCount只是读取,不花存储Gas
  • require是安全检查,防止计数变成负数

我的建议:把这个合约部署到测试网上试试。调用几次increment,看看count怎么变。然后打开区块链浏览器,看看交易记录——你会发现每次调用都产生了一笔交易,这就是「写链上」的代价。

3.6 本章小结

今天我们学了:

  • Solidity是写智能合约的语言,部署后不可修改
  • 合约由状态变量和函数组成,结构清晰
  • 状态变量存链上,贵;局部变量存内存,便宜
  • 整型分uint和int,注意位数和溢出
  • 布尔类型用于条件判断,不能做算术

下一章我们会深入函数和修饰符,到时候你会看到更多有意思的东西。比如怎么限制只有合约拥有者才能调用某个函数——嗯,这个在真实项目中太常用了。

记住:写合约就像盖房子,地基要稳。今天学的这些基础概念,就是你智能合约生涯的地基。别急着写复杂逻辑,先把这些基础玩熟。