🗒️Solidity易混点

type
status
date
slug
summary
tags
category
icon
password
✅初学solidity容易混淆的一些小点,简单记录,方便回顾。

可见性:

  • public
从合约(外+内)调用
自动创建getter函数,如:
  • external
(外)
  • internal
(内+子孙)
  • private
(内)

函数修饰符

  • view
函数只可读,不能修改状态变量
  • pure
不读不写
  • payable
可以接收以太币的函数

状态变量修饰符

  • Immutable、constant
声明不可变的状态变量。
immutable:合约部署时初始化,后续一旦设置,就不能被改变。
constant:编译时即已知的常量。
  • calldata、memory、storage
storage:状态变量。永久存储在区块链上,函数外声明的变量都是storage。花销较大,命名时可以加上前缀“s_”,这样在优化gas时容易注意到。
memory:函数参数,内部变量。大多数默认memory(string需要指定memory或calldata)。
calldata:只读的临时变量。只能用于外部(external)函数的参数。

return关键字

函数中为return,函数声明时为returns
return的两者写法:

receive()、fallback():

都没有 function 关键字。因为是特殊的函数(如 constructor )。
必须是external payable,不能接收参数,也不返回内容。
notion image
注:什么时候为fallback():
  1. 当调用不存在的函数
  1. msg.data不为空
  1. msg.data为空,但receive()不存在

1e18 wei

1e18 = 1 ETH = 1 *10 **18 = 1000000000000000000

先乘后除

solidity没有浮点数,所以除以计算时需要乘上除数,且除数在后,精度更高。

transfer,send,call

  1. transfer:对当前账户转账。最多只能2300gas,超过会revert。
  1. send:最多只能2300gas,但超过不会revert,会返回布尔值。
  1. call:没有gas limit,不用ABI。返回两个变量:一个表示成功或失败的布尔值,一个存储返回数据(如果有)的字节对象。

msg.***

msg.value:发送到合约的以太币数量。
 

库(library)

库(library)在Solidity中是一种特殊类型的合约,具有以下特点:
  • 库的代码可以被包含并在其他合约中重复使用,而无需部署库本身。
  • 库中的函数可以是内部函数(internal),这意味着这些函数在编译时被直接嵌入调用它们的合约中。
  • 库不能拥有状态变量或发送和接收以太币,这增加了其安全性和可预测性。
举例:
注:why not interface:
接口(interface)在Solidity中用于定义合约的标准,它们只包含函数声明,没有具体实现。接口通常用于定义某种合约必须实现的功能,以便于合约之间的互操作性。

动态数组自带函数

push(): 向数组末尾添加一个新元素。 pop():从数组末尾移除并返回最后一个元素。 length:返回数组的当前长度(元素个数)。 delete:删除指定索引的元素,将其设置为默认值。

内置全局变量

msg.sender:返回当前调用合约的地址。 msg.value:返回当前调用合约时附带的以太币数量(单位是 wei)。 msg.data:返回当前调用的原始数据(包含函数选择器和编码参数)。 tx.origin:返回最初发起交易的地址(可能是用户钱包地址)。
block.number:返回当前区块的区块号。
block.timestamp:返回当前区块的时间戳(从 1970 年 1 月 1 日开始的秒数)。
block.difficulty:返回当前区块的难度。
block.coinbase:返回当前区块的矿工地址(coinbase 地址)。
gasleft():返回当前合约调用中剩余的 gas 数量。

内置函数

  • assert(条件):
验证条件是否为真,如果不为真,交易会被回滚,并且消耗的 gas 会被丢弃。
  • require(条件,”错误消息”):
验证条件是否为真,如果不为真,交易会被回滚,并且可以指定错误消息。
  • revert(“错误消息”):
手动回滚交易,并可以提供错误消息。
  • payable(地址).transfer(金额):
将指定金额的以太币从当前合约发送到目标地址(注意:在 Solidity 0.8.0 及以上版本,建议使用 .call 代替 transfer)。
  • payable(地址).send(金额):
将指定金额的以太币从当前合约发送到目标地址,并返回布尔值以指示是否成功(注意:在 Solidity 0.8.0 及以上版本,建议使用 .call 代替 send)。
  • call():
用于调用其他合约的函数或发送以太币。它可以接收以太币并返回调用结果的布尔值。例:
  • keccak256()
计算输入数据的Keccak-256哈希值。用于确保数据完整性和生成唯一标识符。

代码布局建议

重写、重载

重写:子类重新写父类的函数。子类override,父类virtual。
重载:函数同名,参数不同。

override、virtual

子类合约若要重写函数,要加上override,父类函数要加上virtual。

抽象合约(abstract)

抽象合约里的函数用 virtual 关键字标记,只定义,不实现。它们在子合约中被重写。抽象合约主要用于定义接口和规则,而具体的实现交由继承它的合约来完成。

事件(event)

在区块链上创建和触发事件,使得外部应用程序可以监听这些事件,并对事件的发生做出反应。
  • 事件声明 (event):定义一个事件,用于记录特定操作的发生。
indexed 将event的参数标记为可索引,这样可以更高效地搜索事件。
  • 触发事件 (emit):当特定操作发生时触发事件,将事件记录到区块链日志中。
注:什么时候使用事件:
①当你需要向外部世界通知合约内的状态变化时。
②在需要持久记录某些重要操作的情况。日志可以在链上查询,用于审计和跟踪。
③需要外部应用程序对某些操作做出反应时。外部应用程序无法直接监听合约内部的函数调用,但可以监听事件。

字符串拼接

Solidity中,string字符串不能直接用“+”拼接,而是用abi.encodePacked()进行拼接。
abi.encodePacked() 将输入的多个参数编码为字节数组(bytes),在处理多个 string 或其他类型时,它能高效地将它们拼接在一起。

abi编码

  1. abi.encode()和abi.encodePacked()区别
  • abi.encode()
返回一个完整的ABI编码的字节数组,包含了每个参数的类型信息。
如: // 长度 0x0000000000000000000000000000000000000000000000000000000000000004 // UTF-8编码 0x74657374
  • abi.encodePacked()
返回一个压缩的字节数组,没有类型信息,适合用于哈希或唯一标识符生成。
  1. abi.decode(bytes memory data, (type1, type2, ...));
用于将字节数组解码为原始数据类型。
  • data:要解码的字节数组。
  • (type1, type2, ...):要解码到的类型的元组。
编码后得到字节数组,粘贴到解码处,得到原数据:
notion image

super.***

当继承的多个父类中有同名的函数,而需要调用其中一个时,直接用super.*** 就可以了。
(单个继承也能用)

函数修饰器(modifier)

在函数执行前后插入逻辑,从而实现访问控制输入验证状态检查等功能。 用_;来表示被修饰的函数的执行位置。

索引(indexed)

indexed关键字用于事件的参数中,表示该事件的参数将被索引。任何人都可以通过区块链的事件日志接口查询日志,标记为indexed的参数可以在事件查询中作为过滤条件,更高效地搜索和过滤事件。 在事件定义中,最多可以有 三个indexed参数。这些参数被索引后,可以通过区块链API(如web3.jsethers.js)进行高效的查询和检索。
虽然indexed参数在查询中提供了便利,但它会略微增加日志的存储成本,因此应当只对需要频繁查询或过滤的参数使用。
举例:
  • fromto 参数被标记为indexed,可以通过用户地址来过滤所有转账事件,找出某个用户发送或接收的所有转账。
  • amount 没有被索引,这意味着无法直接按金额来过滤事件,但可以通过事件触发后获得该信息。

接口(interface)

只声明,没有具体实现。
在合约与外部合约或标准协议交互时,声明接口是最佳实践。
接口使得不同合约之间能够通过相同的函数接口进行通信,而不必关心具体的合约实现。

transfer和transferfrom区别

都用于处理代币转移,主要用于 ERC-20 标准的代币合约。
一、transfer(接收地址,代币数量)
直接转移调用者的代币。
  • recipient: 接收代币的目标地址。
  • amount: 要转移的代币数量。
二、transferfrom(发送地址,接收地址,代币数量)
代表被授权账户转移其代币。
  • sender: 代币的所有者,即代币将从哪个账户中转移。
  • recipient: 接收代币的目标地址。
  • amount: 要转移的代币数量。
使用 transferFrom,代币所有者需要先使用 approve 函数,允许某个账户(通常是合约)可以代表自己转移一定数量的代币:

汇编代码(Assembly code)

汇编代码是一种直接与 EVM 交互的低级代码,适合对性能要求高的场景或需要直接操作 EVM 内部功能的情况。
 
上一篇
GOGO 房地产
下一篇
Foundry框架学习
Loading...