在已知被调用合约的源码(接口)和地址的情况下,可以采取生成合约变量的方式来调用已部署的合约,有以下方式可用用来生成合约变量:
- 传入合约地址:
OtherContract(contractAddr).f(p1)
或OtherContract(contractAddr).f{value: msg.value}(p1)
(调用时发送 eth) - 传入合约变量: 调用方法定义时声明一个
OtherContract c
的参数,在方法体中采用c.f(p1);
来调用 - 创建合约变量: 调用方法中
OtherContract o = OtherContract(contractAddr); o.f(p1);
总结:
contractName(contractAddr)
来生成合约变量
在不知道合约源码或 ABI
的情况下,可以采用 call
来调用其他合约,具体方式如下:
contractAddr.call(bytecode)
-
- 其中
bytecode=abi.encodeWithSignature("函数签名", 逗号分隔的参数)
,函数签名:函数名(逗号分隔的参数类型)
- 其中
-
- 调用时发送 ETH 及指定 gas:
contractAddr.call{value: x, gas: x}(bytecode)
- 调用时发送 ETH 及指定 gas:
deleteCall 的调用方式如下:
contractAddr.delegateCall(bytecode)
-
bytecode = abi.encodeWithSignature("函数签名", 逗号分隔的参数)
,函数签名为:函数名(逗号分隔的参数类型)
关系 call
与 delegateCall
的区别详见: ./24061701-delegate_call.md
调用合约的方式 | 适应场景 | 语法 |
---|---|---|
生成合约变量 | 已知合约源码或 ABI | ContractName(contractAddr) |
call | 不知道合约与源码及 ABI | contractAddr.call(abi.encodeWithSignature("函数名(逗号分隔的参数类型)", 逗号分隔的参数)) |
delegateCall | 不知道合约与源码及 ABI | contractAddr.delegateCall(abi.encodeWithSignature("函数名(逗号分隔的参数类型)", 逗号分隔的参数)) |
在合约中创建新的合约,语法如下:
ContractName x = new ContractName{value: _value}(params);
说明:
- 如果合约是
payable
修饰的,则可以在创建合约时传递 value 值
create2 的作用就是能够在合约未部署之前预测合约的地址
满足在合约未部署之前,需要事先计算出合约地址的场景
合约地址的计算
创建合约方法 | 合约地址计算方法 | 说明 |
---|---|---|
create |
contractAddr = hash(creatorAddr, nonce) |
creatorAddr : 部署合约的钱包地址或者是合约地址 nonce : 创建者地址的 nonce,由于是可变的,且不能准确预测,所以使用 create 方法创建出来的合约地址,是不可预测的 |
create2 |
contractAddr = hash("0xFF", creatorAddr, salt, initCode) |
0xFF : 常量,用于区分 create 方法 creatorAddr : 部署合约的钱包或合约地址 salt : 一个创建者指定的 byte32 的值,目的是用来影响创建合约的地址 initcode : 新合约的初始字节码(合约的 creation code 和构造函数的参数) |
create2 创建合约语法如下:
ContractName x = new ContractName{salt: _salt, value: _value}(params)
说明:
salt
: 盐值value
: 如果创建的合约时payable
的,则允许创建时向其转账params
: 新合约构造函数的参数
ABI
: application binary interface, 应用程序二进制接口,是与合约交互的标准。其数据也是通过了该类型进行编码,由于编码时只包含了数据,在解码时,要申明返回值的类型
方法名 | 备注 |
---|---|
abi.encode(a, b, c) |
|
abi.encodePacked() |
abi.encode() ,只不过会省略多余填充的 0 |
abi.encideWithSignature() |
abi.encode 功能类似,只不过第一个参数为函数签名(functionName(逗号分隔的参数类型)) |
abi.encodeWithSelector() |
abi.encodeWithSelector 类似,只不过第一个参数为函数选择器(bytes4(keccak256(functionName(逗号分隔的参数类型)))) abi.encideWithSignature() 完全一致 |
abi.decode
用于解码 abi.encode
编码的二进制数据,将它还原成原本的参数,用法如下:
function decode(bytes memory data) public pure returns(uint dx, address daddr, string memory dname, uint[2] memory darray) {
(dx, daddr, dname, darray) = abi.decode(data, (uint, address, string, uint[2]));
}
solidity 中常用的哈希函数是 keccak256
,其用法如下:
hash = keccak256(数据)
keccak256
与 sha3
的区别和联系:
- 联系:
sha3
由keccak256
标准化而来,很多场景可以同义 - 区别: 在
sha3
标准化完成时,更改了其内部算法,导致最终结果与keccak256
不一致;以太坊在sha3
标准化之前开发出来,所以以太坊的哈希函数是keccak256
在 solidity 中 try catch 语法,只能用于外部函数或创建合约时 construct 的调用。基本语法如下
try externalContract.f() returns(returnType val) {
// call 成功时执行
} catch {
// call 失败时执行
}
备注:
this.f()
也可被视为外部函数调用