interface 关键字:定义了一个接口。其他合同可以实现这个接口。
智能合约之间通过interface定义的方法互相调用
首先创建私链并启动:
创建私链
geth --datadir "./ethDev" init genesis.json
启动私链
geth -datadir "./ethDev" --rpc --rpcapi="db,eth,net,web3,personal" --networkid 15 console 2>test.log
启动mist
mist.exe --rpc http://127.0.0.1:8545
一、简单的interface和其实现。
下面的合约实现了interfaceContract 的接口。
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);
}
}
这里部署合约时,只需部署InterfaceImplContract 即可,不用管接口。接口只是为了声明。如下:
二、调用接口。
下面的合约,想与实现了interfaceContract 接口的合约进行交互。
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 即可。不用管接口。接口只是为了声明。
三、实际应用。
在官方代币合约中。就有实际的引用。如下:
pragma solidity ^0.4.16;
interface tokenRecipient { function receiveApproval(address _from, uint256 _value, address _token, bytes _extraData) public; }
contract TokenERC20 {
// 代币名
string public name;
// 代币logo
string public symbol;
// 代币小数位数
uint8 public decimals = 18;
// 18位小数是强烈建议的默认值,避免改变它
uint256 public totalSupply;
// 这将创建一个包含所有余额的数组
mapping (address => uint256) public balanceOf;
//某人授权给某人,使用自己的多少代币.
//比如:当前调用者msg.sender,可以授权给很多地址他代币的使用权限。
mapping (address => mapping (address => uint256)) public allowance;
// 代币转移日志
event Transfer(address indexed from, address indexed to, uint256 value);
// 销毁代币日志
event Burn(address indexed from, uint256 value);
/**
* 构造方法
*/
function TokenERC20(uint256 initialSupply,string tokenName,string tokenSymbol) public {
//10 ** uint256(decimals)是10^18次方。
totalSupply = initialSupply * 10 ** uint256(decimals); //初始化代币总数 使用 decimal,两个星号**表示次方。
balanceOf[msg.sender] = totalSupply; // 给创建者所有的初始化代币
name = tokenName; // 设置代币显示的名字
symbol = tokenSymbol; //设置代币显示的符合,代币logo
}
/**
* 代币转移, internal只能合约内部调用
*/
function _transfer(address _from, address _to, uint _value) internal {
// 防止传输到0x0地址。 使用burn()来代替
require(_to != 0x0);
// 检查发送方有足够代币
require(balanceOf[_from] >= _value);
// 防止溢出,超过uint的数据范围,会变为负数
require(balanceOf[_to] + _value > balanceOf[_to]);
// 保存以备将来的断言
uint previousBalances = balanceOf[_from] + balanceOf[_to];
// 发送方减掉代币
balanceOf[_from] -= _value;
// 接收方增加代币
balanceOf[_to] += _value;
Transfer(_from, _to, _value);//event 日志
// 断言用于使用静态分析来查找代码中的bug。 他们永远不会失败
assert(balanceOf[_from] + balanceOf[_to] == previousBalances);
}
/**
* Transfer tokens
*
* @param _to 收币方地址
* @param _value 转移多少代币
*/
function transfer(address _to, uint256 _value) public {
_transfer(msg.sender, _to, _value);
}
/**
* 从其他地址转移代币,需要其他地址授权给调用的人。
*
*/
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {
require(_value <= allowance[_from][msg.sender]); // 检查授权额度
allowance[_from][msg.sender] -= _value;
_transfer(_from, _to, _value);
return true;
}
/**
* 允许其他人,花费我的代币。
*
*授权给_spender地址,_value个代币
*
* @param _spender 授权给_spender地址
* @param _value 代币数
*/
function approve(address _spender, uint256 _value) public returns (bool success) {
allowance[msg.sender][_spender] = _value;
return true;
}
/**
* Set allowance for other address and notify
*
* 允许 `_spender` 代表你花费不大于`_value` 个代币, and then ping the contract about it
*
* @param _spender 授权给哪个地址
* @param _value 授权最大代币数量
* @param _extraData 一些额外的信息发送到批准的合同
*/
function approveAndCall(address _spender, uint256 _value, bytes _extraData)public returns (bool success) {
tokenRecipient spender = tokenRecipient(_spender);
if (approve(_spender, _value)) {
spender.receiveApproval(msg.sender, _value, this, _extraData);
return true;
}
}
/**
* 销毁代币
*
* 从系统中不可逆地删除`_value'个代币
*/
function burn(uint256 _value) public returns (bool success) {
require(balanceOf[msg.sender] >= _value); //检查调用者金额大于销毁数量
balanceOf[msg.sender] -= _value; // 调用者代币减掉
totalSupply -= _value; // 总供应量减掉
Burn(msg.sender, _value); // event日志
return true;
}
/**
* 从其他账户销毁代币
*
*
* @param _from 从哪个账户销毁代币
* @param _value 销毁的代币数
*/
function burnFrom(address _from, uint256 _value) public returns (bool success) {
require(balanceOf[_from] >= _value); // Check if the targeted balance is enough
require(_value <= allowance[_from][msg.sender]); // Check allowance
balanceOf[_from] -= _value; // Subtract from the targeted balance
allowance[_from][msg.sender] -= _value; // Subtract from the sender's allowance
totalSupply -= _value; // 减掉总供应量
Burn(_from, _value); // event日志
return true;
}
}
这里的approveAndCall方法,就调用了接口。把自己的代币授权给某个合约接口。并且调用合约的后续处理方法。执行通知。