合约¶
Solidity 中的合约类似于面向对象语言中的类
- 状态变量:持久数据
- 函数:操作状态变量中的数据
基本使用¶
ABI¶
Application Binary Interface 应用程序二进制接口
合约的 ABI 即应用程序二进制接口,定义了智能合约各函数调用的参数与方法。即一个合约有什么功能,或者说这个合约的长相,会用 ABI 來定义
对于开源已验证源码的合约,直接在 Etherscan 的合约页面即可查到完整的 ABI 信息(https://goerli.etherscan.io/contractsVerified)
未开源智能合约的调用原理、方法及实现代码:https://twitter.com/gm365/status/1521058983838380032
调用¶
合约账户调用其它合约有两种方法
call() // 普通的消息调用,是跳转到被调用合约执行完相应代码返回后继续执行后续代码
delegatecall() // 委托调用是特殊的消息调用,把被调用合约中的代码拷贝过来执行
无论嵌套调用链有多深,msg.sender
始终是最后一个合约的地址。
用程序来交互合约,核心就四个步骤:
- 确定合约地址:https://docs.chain.link/data-feeds/price-feeds/addresses
- 找到合约 ABI
- 研究函数名及参数具体含义
- 写交互代码,广播交易信息
参考:https://mirror.xyz/gm365.eth/ad4vbp_qLFKaOrAMtE2YZ6pzMuC3ejam-y_62QogSds
引入¶
与外部合约交互同样需要合约地址和 ABI
获得 ABI 后可以直接将代码复制过来,也可以直接从 GitHub 中导入
Remix 可以自动识别 @chainlink/contracts
就是指向 Chainlink/contracts
的 NPM 包,Remix 会从 NPM 中下载这些来自 GitHub 的代码到本地
// 本地引入
import "./path/Demo.sol";
// 通过 npm moudule 引入
import '@openzeppelin/contracts/access/Ownable.sol';
// 通过网址引入
import 'https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Address.sol';
// 源文件中有多个合约,可以只引入指定合约
import {A} from './path/Demo.sol';
重载 override¶
// virtual关键字表示该方法可以被子合约继承后重载
function store(uint256 _favoriteNumber) public virtual {
favoriteNumber = _favoriteNumber;
}
继承 inheritance¶
https://docs.soliditylang.org/en/latest/contracts.html#inheritance
// 引入合约
import "./Base.sol";
// is关键字来继承合约
contract Demo is Base {
// 使用override关键字重载合约,对父合约的方法进行改写
function store(uint256 _favoriteNumber) public override {
favoriteNumber = _favoriteNumber + 5;
}
}
super¶
super
关键字
new¶
new
关键字,在合约中创建新合约
抽象合约 abstract¶
https://wtf.academy/solidity-start/Interface/
当合约至少有一个功能缺乏实现时,则被识别为抽象合约,不能被编译,也不能部署,即不能被实例化,但可以被继承
代码复用,继承了抽象合约的合约,至少要实现其一个功能,否则也会被标记为抽象合约
可以声明函数
- 可以实现功能,也可以不实现功能
- 加 virtual
关键字表示可以被继承合约重载
接口 interface¶
纯抽象合约
继承了接口的合约,必须实现接口的所有功能
- 不能继承除接口外的其它合约
- 不能有构造函数
- 不能有状态变量
- 不能定义枚举
- 只能声明函数
- 不能实现功能
- 只能是
external
类型 - 每个函数都以
;
代替函数体{ }
结尾
接口与合约 ABI
(Application Binary Interface)等价,可以相互转换
编译接口可以得到合约的 ABI
,利用abi-to-sol工具也可以将 ABI json
文件转换为 接口sol
文件
知道了一个合约的接口,不需要知道他的具体实现,只需要知道合约地址,便可以用接口与它交互
Ownable¶
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
contract Ownable {
address public owner;
constructor() {
owner = msg.sender;
}
modifier onlyOwner() {
require(msg.sender == owner, "not owner");
_;
}
// 只有当前Owner才能设置新Owner
function setOwner(address _newOwner) external onlyOwner {
require(_newOwner != address(0), "invalid address"); // 新的Owner不要设置为空地址
owner = _newOwner;
}
function onlyOwnerCanCall() external onlyOwner {
// code
}
function anyOneCanCall() external {
// code
}
}
合约部署¶
EVM 是单线程的,运行单元测试和部署脚本时不需要异步调用。
合约编译:代码会被 EVM 编译生成一个 ABI 文件(合约接口描述)和一个 bin 文件(EVM 指令集合)
合约部署:将编译后的字节码上传到区块链,过程与发送一笔交易类似
- from:sender
- to:0
- 交易数据:bin
在进行交易打包时,将根据发布者的地址和交易序列号通过加密算法重新计算出一个地址作为这个合约的地址,调用者可通过合约地址对合约进行调用