solidity学习笔记(9)—— 接口和抽象合约

一个合约如何读取其他合约的数据或调用其他合约的方法?

接口的存在就是为了合约之间的通信。

有两种实现方式:抽象合约 和 接口

一、抽象合约

抽象函数是没有函数体的的函数。如下:

pragma solidity ^0.4.0;
​
contract Feline {
    function utterance() returns (bytes32);
}

这样的合约不能通过编译,即使合约内也包含一些正常的函数。但它们可以做为基合约被继承。

pragma solidity ^0.4.0;
​
contract Feline {
    function utterance()
        returns (bytes32);
    function getContractName() returns (string){
        return "Feline";
    }
}
​
contract Cat is Feline {
    function utterance() returns (bytes32) {
        return "miaow";
    }
}

如果一个合约从一个抽象合约里继承,但却没实现所有函数,那么它也是一个抽象合约。

如何通过抽象合约实现接口功能?

如果contract B要使用contract A的方法或数据,本质上:

  • 先定义一个抽象合约,让contract A继承于这个抽象合约;
  • 把contract A中已经实现了的方法放入抽象合约中,solidity会自动把这个抽象合约视作接口;
  • contract B通过contract A的地址来创建连接到contract A的接口实例;
  • 调用contract A中的方法或读取数据;

二、接口

接口与抽象合约类似,与之不同的是,接口内没有任何函数是已实现的,同时还有如下限制:

  • 不能继承其它合约,或接口。
  • 不能定义构造器
  • 不能定义变量
  • 不能定义结构体
  • 不能定义枚举类

接口基本上限制为合约ABI定义可以表示的内容,ABI和接口定义之间的转换应该是可能的,不会有任何信息丢失。

注意:
1、在两个.sol文件中都声明接口,或者两个合约写到一个.sol文件里,那就只要声明一次;
2、在一个合约中实现METHOD_A,该合同必须继承自接口interfaceContract;
3、在另一个合约中创建一个interfaceContract实例,该实例接受实现接口的合约的地址;
4、通过这个实例调用目标合约的方法,获取目标合约的数据;

实例:

被调用合约 InterfaceImpContract

pragma solidity ^0.4.16;
​
interface interfaceContract {
    function receiveApproval(address _from, uint256 _value, address _token, bytes _extraData);
}
​
contract InterfaceImplContract is interfaceContract {
    event Receive(address from, uint256 value, address token, bytes extraData);
    function receiveApproval(address _from, uint256 _value, address _token, bytes _extraData) {
        Receive(_from,_value,_token,_extraData);
    }
}

调用合约 RemoteContract

pragma solidity ^0.4.16;
​
interface interfaceContract {
    function receiveApproval(address _from, uint256 _value, address _token, bytes _extraData);
}
​
contract RemoteContract {
    function func(address _addr, uint _value) {
        //注意这里的_addr参数,需要填写tokenRecipient合约的地址。这里加载已经存在的智能合约。如何合约不存在会报错回滚。
        interfaceContract _interfaceContract = interfaceContract(_addr);
        _interfaceContract.receiveApproval(msg.sender, _value, address(this), "这是一些信息");
    }
}

注意这里调用 func(address _addr, uint _value)方法时,传递的参数_addr,在下面的代码中,用来加载合约interfaceContract _interfaceContract = interfaceContract(_addr); 所以addr必须传递合约地址。并且这个合约地址是interfaceContract的实现类的合约地址。也就是第一步创建的InterfaceImplContract 合约的地址。

如果传递的_addr参数错误,调用失败。它将回滚所有已执行的功能。也就是这个方法会回滚。
这里部署时,只需部署RemoteContract 即可。不用管接口。接口只是为了声明。