Solidity智能合约快速入门

什么是智能合约?

智能合约是运行在链上的程序,合约开发者可以通过智能合约实现与链上资产、数据进行交互。用户可以通过自己的链上账户来调用合约,访问资产与数据。

与一般程序的差异

原生支持资产流动,部署与后续写入需要一定的费用,存储数据的成本更高,部署后无法更改(可升级合约?)

Solidity是什么?

一门面向合约的,为实现智能合约而创建的高级编程语言,在EVM虚拟机上运行,语法整体类似于JavaScript,是目前最流行的智能合约语言,也是入门区块链和web3所必须掌握的语言。

如何部署合约?

Solidity合约是以.sol为后缀的文件,无法直接执行,需要编译为EVM(Ethereum Virtual Machine)可识别的字节码才能在链上运行。

开发框架&工具有哪些?

框架有:

工具有:

Solidity核心语法有哪些?

基本数据类型

1. Boolean 2. int 3. uint 4. address 5. bytes ...

枚举类型

js 复制代码
enum Status{
    Unknown,
    Start,
    End,
    Pause
}
// 实例化枚举类型
Status public status;
// 更新枚举值
function pause public {
    status = Status.Pause;
}
// 初始化枚举值
function reset() public{
   return status;
}

数组类型

js 复制代码
// 定义数组类型
uint[7] public arr;
// 添加数据
arr.push(7);
// 删除最后一个数据
arr.pop();
// 删除某个索引值数据
delete arr[1];
// 获取数组长度
uint len = arr.length;

mapping类型

js 复制代码
// 定义嵌套mapping类型
mapping(string => mapping(string=>string)) nestedMap;
// 设置值
nestedMap[id][key] = "0707";
// 读取值
string value = nestedMap[id][key];
// 删除值
delete nestMap[id][key];

结构体类型(struct)

js 复制代码
contract Struct{
    struct Data{
        string id;
        string hash;
    }
    Data public data;
    
    // 添加数据
    function create(string calldata _id) public {
        data = Data{id:_id,hash:"111222"};
    }
    
    // 更新数据
    function update(string _id) public {
        // 查询数据
        string id = data.id
        // 更新
        data.hash = "222333"
    }
}

变量类型有哪些?

类型 :local state global 关键字声明:storage memory calldata

常量有哪些?

constant : 值不可变,节约gas fee immutable:可以在constructor中初始化,但不可以再次改变。

函数的可见性和关键字有哪些?

可见性:public private internal external

关键字:view pure

函数修饰符modifier

js 复制代码
modifier  onlyOwner{
    require(msg.sender == owner,"Not owner");
}

modifier validAddress(address _addr){
    require(_addr != address(0),"Not valid address");
    _;
}

modifier noReentrancy(){
    require(!locked,"No reentrancy");
    locked = true;
    _;
    locked = false;
}

function changeOwner(address _newOwner) public onlyOwner validAddress(_newOwner){
    owner = _newOwner;
}

function decrement(uint i) public noReentrancy{
    x -= i;
    if(i > 1){
        decrement(i - 1);
    }
}

函数选择器是什么?

js 复制代码
addr.call(abi.encodeWithSignature("transfer(address,uint256)",0xSomeAddress,123))

contract  FunctionSelector{
    function getSelector(string calldata _func) external pure returns (bytes4){
        return bytes4(keccak256(bytes(_func)));
    }
}

条件判断是什么?

js 复制代码
if(x < 10){
    return 0;
}else if(x < 20){
    return 1;
}else{
    return 2;
}

x < 20 ? 1 : 2 // 三元运算符

循环有哪些?

js 复制代码
for(uint i=0;i<10;i++){
    // 业务逻辑

}
uint j;
while (j < 10){
    j++;
}

合约的概念(constructor)

js 复制代码
constructor(string memory _name){
    name = _name;
}

先定义接口,然后由合约实现接口。

js 复制代码
contract Counter{
  uint public  count;
  function  increment() external{
      count+1;
  }
}

interface ICounter{
    function count() external view returns (uint);
    function increment() external;
}

contract MyContract {
    function incrementCounter(address _counter) external {
        ICounter(_counter).increment();
    }
    
    function getCount(address _counter) external view returns (uint){
        return ICounter(_counter).count();
    }
}

合约如何继承呢?

js 复制代码
// 定义父合约 A
contract A{
    function foo() public pure virtual returns (string memory){
        return "A";
    }
}

// B合约继承A合约并重写函数
contract B is A{
    function foo() public pure virtual override returns (string memory){
        return "B";
    }
}

// D合约继承B,C并重写函数
contract D is B,C{
    function foo() public pure override(B,C) returns (string memory){
        return super.foo();
    }
} 

contract B is A{
    function foo() public virtual override{
        // 直接调用
        A.foo();
    }
    function bar() public virtual override{
        // 通过supper关键字调用
        super.bar();
    }
}

如何在合约里面创建合约?

js 复制代码
function create(address _owner,string memory _model) public {
    Car car = new Car(_owner,_model);
    cars.push(car);
}

在0.8.0之后支持以下写法

js 复制代码
function create2(address _owner,string memory _model,bytes32 _salt) public{
    Car car = (new Car){salt:_salt}(_owner,_model);
    cars.push(car);
}

如何导入合约呢?

js 复制代码
// 本地导入
import './Foo.sol';
// 外部导入
import 'https://github.com/owner/repo/blob/branch/path/to/Contract.sol'; 

导入如何导入库?

js 复制代码
library SafeMath{
    function add(uint x, uint y) internal pure returns (uint){
        uint z = x+y;
        require(z>=x,"uint overflow");
    }
}

contract TestSafeMath{
    using SafeMath for uint;
}

事件怎么实现?

js 复制代码
// 定义事件
event Log(address indexed sender,string message);
event AnotherLog();

// 抛出事件
emit Log(msg.sender,"Hello World!");
emit Log(msg.sender,"Hello EVM!");
emit AnotherLog();

错误处理如何使用?

js 复制代码
    function testRequire(uint _i) public pure {
        require(_i > 10,"Input must be greater than 10");
    }
    
    function testRevert(uint _i) public pure{
        if(i <= 10){
            revert("Input must be greater than 10");
        }
    }
    
    function testAssert() public view{
        assert(num == 10);
    }

try/catch捕捉错误

js 复制代码
event Log(string message);
event Log(bytes data);

function tryCatchNewContract(address _owner) public {
    try new Foo(_owner) returns (Foo foo){
        emit Log("Foo created");
    }catch Error(string memory reason){
        emit Log(reason);
    }catch(bytes memory reason){
        emit LogBytes(reason);
    }
}

资产(payable)

js 复制代码
// 地址类型可以声明 payable
address payable public owner;

constructor() payable {
  owner = payable(msg.sender);
}

// 方法声明  payable 来接收 Ether
function deposit()  public payable{}

资产如何发送?

js 复制代码
contract SendEnter{
    function sendViaCall(address payable _to) public payable{
        (bool sent,bytes memory data) = _to.call{value:msg.value}("");
        require(sent,"Failed to send Ether");
    }
}

资产如何接受?

js 复制代码
contract ReceiveEther{
    // 当msg.data为空时
    receive() external payable{}
    // 当msg.data非空时
    fallback() external payable{}
    
    function getBalance() public view returns (uint) {
        return address(this).balance;
    }
}

如何节约Gas费用?

参数

  1. gas spent
  2. gas price
  3. gas limit
  4. block gas limit

技巧

  1. 使用calldata代替memory
  2. 将状态变量载入内存
  3. 使用i++而不是使用++i
  4. 缓存数组元素

其他学习网址:

使用forge test来测试项目是否成功!

web3博主博客:链接

相关推荐
小突突突4 小时前
Spring框架中的单例bean是线程安全的吗?
java·后端·spring
iso少年4 小时前
Go 语言并发编程核心与用法
开发语言·后端·golang
掘金码甲哥4 小时前
云原生算力平台的架构解读
后端
码事漫谈4 小时前
智谱AI从清华实验室到“全球大模型第一股”的六年征程
后端
码事漫谈4 小时前
现代软件开发中常用架构的系统梳理与实践指南
后端
Mr.Entropy4 小时前
JdbcTemplate 性能好,但 Hibernate 生产力高。 如何选择?
java·后端·hibernate
YDS8294 小时前
SpringCloud —— MQ的可靠性保障和延迟消息
后端·spring·spring cloud·rabbitmq
无限大65 小时前
为什么"区块链"不只是比特币?——从加密货币到分布式应用
后端
洛神么么哒5 小时前
freeswitch-初级-01-日志分割
后端
蝎子莱莱爱打怪5 小时前
我的2025年年终总结
java·后端·面试