0%

solidity | 合约调用另外一个合约

大家知道客户端在调取智能合约时,需要先传递一个 ABI 接口。这样客户端(例如 js )就可以调取智能合约中的方法了。

那么在 solidity中如何在当前编写的合约中调取一个已部署的合约呢?也类似,通过声明已部署的合约接口,让当前合约识别老合约。

第一种实现,通过接口调用:

合约调取合约最小实现案例 第一个合约:

1
2
3
4
5
6
7
8
9
pragma solidity ^0.4.24; 

contract Deployed {
uint public a = 1;
function setA(uint _a) public returns (uint) {
a = _a;
return a;
}
}

我们部署它,会得到一个合约地址,假设它是 0xxxxx

我们写第二个合约:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
pragma solidity ^0.4.24;

//Deployed合约的接口

interface Deployed {
function setA(uint) public returns (uint) {}
function a() public pure returns (uint) {}
}

contract Existing {
Deployed dc;
function Existing(address t) public {
dc = Deployed(t);
}

function getA() public view returns (uint result) {
return dc.a();
}

function setA(uint _val) public returns (uint result) {
dc.setA(_val);
return _val;
}
}

如上面的代码,我们先声明了一个已部署合约的接口:

1
2
3
4
interface Deployed { 
function setA(uint) public returns (uint) {}
function a() public pure returns (uint) {}
}

然后就可以在新的合约 Existing 中调用这个合约了。

我们把第二个合约部署时,把第一个合约 Deployed 的合约地址 0xxxxx 传进来赋值给 _t ,并初始化将第一个合约赋值给 dc 。我们看到 getA , setA分别发生了什么?

然后我们分别调取合约1合约2,再去比较。

那么如何用solidity合约调取erc20代币的合约呢?

比如snt啊,loom啊,knc啊。

首先我有一个名叫loomerc20token部署在ropsten网络。

  • loom的详细
    • 合约地址 0x090652a4aecee28a7ae766c5bd51851830185664
    • 合约

我们写当前的合约向第一个例子一样,先写 erc20token 的接口,接着写我们调用的合约:

1
2
3
4
5
6
7
8
9
10
11
12
pragma solidity ^0.4.24;

interface Tokenloom {
function transfer(address to, uint256 value) external returns (bool success);
}

contract StandardToken{
function transferloomtoken(uint256 amount){
Tokenloom token = Tokenloom(0x090652a4aecee28a7ae766c5bd51851830185664); //发送到token
token.transfer( 0xafe28867914795bd52e0caa153798b95e1bf95a1, amount);
}
}

我们将这个合约部署:

同样在 ropsten 测试网中,得到这个合约地址:

然后我们转入一笔 loom 到这个合约地址 0x2C8C3483Be1160A2D89D07708b5477Ea19457255

恩,我们看看转账记录:

接下来,我们调取我们所写的 StandardToken 合约的 transferloomtoken 这个方法,神奇的事又发生了:

这种通过接口调用方式是最为推荐的,当然还有第二种:

另外,这种还有一个技巧,假如你的合约需要调用多个合约,你不需要写多个合约接口,只需要写一个合约接口,然后把所有需要调用的方法都写在这个接口里面。

然后,你用这个接口创建的合约,就可以调用任何一个合约里面的方法了。

第二种方式:使用call,delegatecall

1
2
3
4
5
6
7
8
9
10
11
12
pragma solidity ^0.4.18; 

contract ExistingWithoutABI {
address dc;
function ExistingWithoutABI(address t) public {
dc = _t;
}
function setASignature(uint val) public returns(bool success){
require(dc.call(bytes4(keccak256("setA(uint256)")),val));
return true;
}
}

补充 loom 的教程:与其他合约的交互

定义interface

如果我们的合约需要和区块链上的其他的合约会话,则需先定义一个 interface (接口)。 先举一个简单的栗子。 假设在区块链上有这么一个合约:

1
2
3
4
5
6
7
8
9
10
contract LuckyNumber { 
mapping(address => uint) numbers;

function setNum(uint _num) public {
numbers[msg.sender] = _num;
}

function getNum(address myAddress) public view returns (uint) {
return numbers[myAddress]; }
}

这是个很简单的合约,您可以用它存储自己的幸运号码,并将其与您的以太坊地址关联。 这样其他人就可以通过您的地址查找您的幸运号码了。 现在假设我们有一个外部合约,使用 getNum 函数可读取其中的数据。

首先,我们定义 LuckyNumber 合约的 interface

1
2
3
contract NumberInterface { 
function getNum(address _myAddress) public view returns (uint);
}

请注意,这个过程虽然看起来像在定义一个合约,但其实内里不同:

首先,我们只声明了要与之交互的函数 —— 在本例中为 getNum —— 在其中我们没有使用到任何其他的函数或状态变量。

其次,我们并没有使用大括号({})定义函数体,我们单单用分号(;)结束了函数声明。这使它看起来像一个合约框架。

编译器就是靠这些特征认出它是一个接口的。

在我们的 app 代码中使用这个接口,合约就知道其他合约的函数是怎样的,应该如何调用,以及可期待什么类型的返回值。

在下一节中,我们将真正调用其他合约的函数。目前我们只要声明一个接口,用于调用 CryptoKitties 合约就行了。

使用接口,调用已用合约方法

继续前面 NumberInterface 的例子,我们既然将接口定义为:

1
2
3
contract NumberInterface { 
function getNum(address _myAddress) public view returns (uint);
}

我们可以在合约中这样使用:

1
2
3
4
5
6
7
8
9
contract MyContract { 
address NumberInterfaceAddress = 0xab38...; // ^ 这是FavoriteNumber合约在以太坊上的地址
NumberInterface numberContract = NumberInterface(NumberInterfaceAddress); // 现在变量 numberContract 指向另一个合约对象

function someFunction() public {
// 现在我们可以调用在那个合约中声明的 getNum函数:
uint num = numberContract.getNum(msg.sender); // ...在这儿使用 num变量做些什么
}
}

通过这种方式,只要将您合约的可见性设置为 public (公共)或 external(外部),它们就可以与以太坊区块链上的任何其他合约进行交互。

请我喝杯咖啡吧~