- 预编译合约
- 安装最新0.8.0
- abi编码
- Ecdsa r,s v (可以用于验证哪一个账号的私钥签署了这个消息)
- Erevover(r,s, v) 返回地址
- payble
event
事件是能方便地调用以太坊虚拟机日志功能的接口
地址类型 Address
地址类型有两种形式,他们大致相同:
address:保存一个20字节的值(以太坊地址的大小)。address payable:可支付地址,与address相同,不过有成员函数transfer和send。
这种区别背后的思想是 address payable 可以向其发送以太币,而不能先一个普通的 address 发送以太币,例如,它可能是一个智能合约地址,并且不支持接收以太币。
类型转换:
允许从 address payable 到 address 的隐式转换,而从 address 到 address payable 必须显示的转换, 通过 payable(<address>) 进行转换。
只能通过 payable(...) 表达式把 address 类型和合约类型转换为 address payable。 只有能接收以太币的合约类型,才能够进行此转换
fallack/receive/transfer
send 是 transfer 的低级版本。如果执行失败,当前的合约不会因为异常而终止,但 send 会返回 false。
在使用 send 的时候会有些风险:如果调用栈深度是 1024 会导致发送失败(这总是可以被调用者强制),如果接收者用光了 gas 也会导致发送失败。 所以为了保证 以太币Ether 发送的安全,一定要检查 send 的返回值,使用 transfer 或者更好的办法: 使用接收者自己取回资金的模式
call delegatecall/staticcall
为了与不符合 应用二进制接口Application Binary Interface(ABI) 的合约交互,或者要更直接地控制编码,提供了函数 call,delegatecall 和 staticcall 。 它们都带有一个 bytes memory 参数和返回执行成功状态(bool)和数据(bytes memory)
所有这些函数都是低级函数,应谨慎使用。 具体来说,任何未知的合约都可能是恶意的,我们在调用一个合约的同时就将控制权交给了它,而合约又可以回调合约,所以要准备好在调用返回时改变相应的状态变量(可参考 可重入 ), 与其他合约交互的常规方法是在合约对象上调用函数(x.f())。
可以使用 gas 修改器modifier 调整提供的 gas 数量:
address(nameReg).call{gas: 1000000}(abi.encodeWithSignature("register(string)", "MyName"));
类似地,也能控制提供的 以太币Ether 的值:
address(nameReg).call{value: 1 ether}(abi.encodeWithSignature("register(string)", "MyName"));
最后一点,这些 修改器modifier 可以联合使用。每个修改器出现的顺序不重要:
address(nameReg).call{gas: 1000000, value: 1 ether}(abi.encodeWithSignature("register(string)", "MyName"));
所有三个函数 call , delegatecall 和 staticcall 都是非常低级的函数,应该只把它们当作 最后一招 来使用,因为它们破坏了 Solidity 的类型安全性。
所有三种方法都提供 gas 选项,而 value 选项仅 call 支持 。
合约类型
您可以隐式地将合约转换为从他们继承的合约。 合约可以显式转换为 address 类型。
只有当合约具有 接收receive函数 或 payable 回退函数时,才能显式和 address payable 类型相互转换 转换仍然使用 address(x) 执行, 如果合约类型没有接收或payable 回退功能,则可以使用 payable(address(x)) 转换为 address payable 。
引用类型
-
memory 即数据在内存中,因此数据仅在其生命周期内(函数调用期间)有效。不能用于外部调用。
-
存储storage 状态变量保存的位置,只要合约存在就一直存储.
-
调用数据calldata 用来保存函数参数的特殊数据位置,是一个只读位置。
- 0.6.9 之前 calldata 仅用于外部函数调用参数,0.6.9之后可用于任意函数。
如果可以的话,请尽量使用
calldata作为数据位置,因为它将避免复制,并确保不能修改数据。 函数的返回值中也可以使用calldata数据位置的数组和结构,但是无法给其分配空间。在0.6.9版本之前,引用类型参数的数据位置有限制,外部函数中使用
calldata,公共函数中使用memory,以及内部和私有函数中的memory或storage。 现在memory和calldata在所有函数中都被允许使用,无论其可见性如何。
数据位置不仅仅表示数据如何保存,它同样影响着赋值行为:
- 在 存储storage 和 内存memory 之间两两赋值(或者从 调用数据calldata 赋值 ),都会创建一份独立的拷贝。
- 从 内存memory 到 内存memory 的赋值只创建引用, 这意味着更改内存变量,其他引用相同数据的所有其他内存变量的值也会跟着改变。
- 从 存储storage 到本地存储变量的赋值也只分配一个引用。
- 其他的向 存储storage 的赋值,总是进行拷贝。 这种情况的示例如对状态变量或 存储storage 的结构体类型的局部变量成员的赋值,即使局部变量本身是一个引用,也会进行一份拷贝(译者注:查看下面
ArrayContract合约 更容易理解)。
函数调用
内部函数调用
外部函数调用
从一个合约到另一个合约的函数调用不会创建自己的交易, 它是作为整个交易的一部分的消息调用。
任何与其他合约的交互都会产生潜在危险,尤其是在不能预先知道合约代码的情况下。 交互时当前合约会将控制权移交给被调用合约,而被调用合约可能做任何事。即使被调用合约从一个已知父合约继承,继承的合约也只需要有一个正确的接口就可以了。 被调用合约的实现可以完全任意的实现,因此会带来危险。 此外,请小心这个交互调用在返回之前再回调我们的合约,这意味着被调用合约可以通过它自己的函数改变调用合约的状态变量。 一个建议的函数写法是,例如,在合约中状态变量进行各种变化后再调用外部函数,这样,你的合约就不会轻易被滥用的重入攻击 (reentrancy) 所影响
加“盐”的合约创建 / create2
函数可见性的几种
特别的函数
- receive
- fallback