怎样用Remix编译和使用智能合约

Remix 是以太坊提供的一个开发Solidity智能合约的网络版开发软件。合约的开发者在Remix里提供的JavaScript虚拟机上开发,调试好合约后,可以发布到以太坊,或者任何支持Solidity智能合约的区块链上。

怎样使用智能合约

我们先来了解一下怎样使用智能合约。智能合约的使用有两步,第一步是部署,就是合约的发起人把智能合约发布到区块链上,并且生成一个新的合约地址。第二步,则是调用部署在这个地址上的合约里的函数。一个合约只需部署一次,生成一个合约地址。但是这个地址上合约中的函数可以多次被调用。

大家不难理解区块链是一个记录不同地址之间交易的账本,而智能合约,可以理解成是区块链上一个特殊的交易,起始地址是合约部署或者调用者的地址,目标地址是合约地址。部署合约的时候目标地址是空,但是交易被记录到区块上是会生成一个地址。交易金额可以是零,也可以不是,然后交易金额也可以触发合约函数的调用,我们这里简化一点,暂时不提。支持智能合约的区块链交易还有一个叫做数据的参数,是一堆16进制代码,这个代码,实际上就是合约的代码。部署的时候,整个合约都会被编译。调用的时候,只有被调用的函数被编译。编译的规则,在这里不详细解释,我们主要讲的,就是怎样利用Remix,来对合约和函数进行16进制的编译,然后使用编译代码,手动发合约到区块链上。

我们这里使用一个发ERC20代币的智能合约来讲解。

发ERC20代币的智能合约

pragma solidity ^0.4.16;
contract Token{
    uint256 public totalSupply;

    function balanceOf(address _owner) public constant returns (uint256 balance);
    function transfer(address _to, uint256 _value) public returns (bool success);
    function transferFrom(address _from, address _to, uint256 _value) public returns   
    (bool success);

    function approve(address _spender, uint256 _value) public returns (bool success);

    function allowance(address _owner, address _spender) public constant returns 
    (uint256 remaining);

    event Transfer(address indexed _from, address indexed _to, uint256 _value);
    event Approval(address indexed _owner, address indexed _spender, uint256 
    _value);
}

contract TokenDemo is Token {

    string public name;        
    uint8 public decimals;     
    string public symbol;      

    function TokenDemo(uint256 _initialAmount, string _tokenName, uint8 _decimalUnits, string _tokenSymbol) public {
        totalSupply = _initialAmount * 10 ** uint256(_decimalUnits);  
        balances[msg.sender] = totalSupply; 

        name = _tokenName;                   
        decimals = _decimalUnits;          
        symbol = _tokenSymbol;
    }

    function transfer(address _to, uint256 _value) public returns (bool success) {
        require(balances[msg.sender] >= _value && balances[_to] + _value > balances[_to]);
        require(_to != 0x0);
        balances[msg.sender] -= _value;   
        balances[_to] += _value;          
        Transfer(msg.sender, _to, _value);
        return true;
    }


    function transferFrom(address _from, address _to, uint256 _value) public returns 
    (bool success) {
        require(balances[_from] >= _value && allowed[_from][msg.sender] >= _value);
        balances[_to] += _value;              
        balances[_from] -= _value;            
        allowed[_from][msg.sender] -= _value; 
        Transfer(_from, _to, _value);         
        return true;
    }
    function balanceOf(address _owner) public constant returns (uint256 balance) {
        return balances[_owner];
    }


    function approve(address _spender, uint256 _value) public returns (bool success)   
    { 
        allowed[msg.sender][_spender] = _value;
        Approval(msg.sender, _spender, _value);
        return true;
    }

    function allowance(address _owner, address _spender) public constant returns (uint256 remaining) {
        return allowed[_owner][_spender];       
    }
    mapping (address => uint256) balances;
    mapping (address => mapping (address => uint256)) allowed;
}

如何使用Remix

大家登陆Remix 后,把中间的编辑框里的合约内容删除,然后把上面的这个合约复制到编辑框里。在右上角的菜单里Compile下面选中Auto Compile。

然后到Run菜单下面的Environment选项中选JavaScript VM。下面的Account是这个JavaScript虚拟机提供的,有好几个Account,而且都有测试用的金额。随便选中一个。再往下有个菜单里有Token和TokenDemo两个选项,Token是我们上面这个合约定义的接口,TokenDemo是实际类。我们选中TokenDemo。再下面Create前的这个空格里,我们要给出一些参数的值,就是你要发行的代币的总金额,名称及代码等,我们输入下面的参数,然后点Create。

100000000, "Test Token", 10, "TTN"

这时,在中间下面的灰色对话框中,你会看到这么一堆小字,

creation of TokenDemo pending...

还有Detail和Debug两个按钮。这说明合约的构造函数(在JavaScript虚拟机上)被调用成功了。点开Detail看详细内容。在点开的一张表里找到Input那一条,右边的那一堆16进制代码就是我们要用来部署的合约内容。拖到代码的最后面,有一个复制的图标,点一下把代码拷贝下来。

打开文本编辑器,把刚才拷贝的内容复制并且赋值。

var constructCode = "0x6060604052341561000f57600080fd5b604051610d52380380610d52833981016040528080519060200190919080518201919060200180519060200190919080518201919050508160ff16600a0a8402600081905550600054600460003373......"

为节省文章空间,代码我没有全部复制在这里,用省略号代替了。但是前后一定要记得打上双引号。

接下来我们调用一个合约函数。在Remix右边的下面可以找到可调用函数approve, transfer, transferFrom, 我们来调用一下transfer这个函数。需要提供两个参数,转入地址和转入金额。我们可以用任何一个地址,但是为了方便我们下面的测试,我们最好能用等下要测试的区块链上的真实地址。

我们可以在transfer右边的对话框里输入下面的参数,然后点击transfer。

"0x7d357a39d4884aaec00e7b30f169fe289fb99174", 100

这时,在中间下面的灰色对话框中,你会看到这么一堆小字,

transact to TokenDemo.transfer pending...

还有Detail和Debug两个按钮。这说明合约的transfer函数(在JavaScript虚拟机上)被调用成功了。点开Detail看详细内容。在点开的表里找到Input那一条,然后拷贝16进制代码。赋值给一个变量等下用。

var transferCode = "0xa9059cbb0000000000000000000000007d357a39d4884aaec00e7b30f169fe289fb991740000000000000000000000000000000000000000000000000000000000000064"

在区块链上部署和调用合约

接下来我们就可以到区块链的一个节点上来进行合约的部署和调用。我们可以使用以太坊,但是这里我想介绍一个新的区块链叫MOAC。MOAC和以太坊最大的不同是,以太坊的交易和合约都是在同一条链上执行的,而且合约只能同步调用,所以当同时有很多合约需要执行的时候,就会要等待很长的时间。整个区块链的处理速度就会变得很慢。MOAC在以太坊的基础上增加了一个分层结构,下层是主链,只做交易,上层跑合约,然后每一个合约相当于生成一个子链,子链定期把结果刷新到主链上。这样的话,就实现了合约的异步调用,再多的合约都可以同时进行,处理速度要比以太坊快好多个数量级。所以,将来所有基于智能合约调用区块链的去中心化应用,都会在MOAC这样的链上实现。

MOAC 测试网在2018年3月正式上线。我们用Ubuntu的版本来做演示,大家可以到这里下载

文件下载解压后,生成一个叫moac的目录。进入这个目录以后,运行下面这条命令,就可以连上MOAC测试网络了。

./moac --testnet

这时候,会有一个测试网的文件夹生成$HOME/.moac/testnet/这时候我们可以打开一个新的进程,然后进入moac的目录,运行下面这条命令,就可以进入一个可以发合约的console。

./moac attach $HOME/.moac/testnet/moac.ipc

当然,我们在部署合约之前还要做一件事情,那就是拿到合约的abi编译代码。这个可以用编译器solc来做。这个不是在console里面进行,如果你已经执行moac attach ...命令进入console了,请用exit退出。

安装Solidity编译器

首先,看是否安装solidity编译器。

solc --version

如果没有的话,可以这样安装:

sudo add-apt-repository ppa:ethereum/ethereum
sudo apt-get update
sudo apt-get install solc

下一步,将我们的ERC20合约拷贝到该节点的moac目录下,可取名为Token.sol。然后我们将合约编译,并把编译结果写到一个叫token.js的文件里去,便于我们在console里调用。指令如下:

echo "var testOutput=`solc --optimize --combined-json abi,bin,interface Token.sol`" > token.js

部署合约

再次进入console。

./moac attach $HOME/.moac/testnet/moac.ipc

如果你是第一次连接moac节点,你还要生成一个账号地址。

personal.newAccount("自选密码")

然后我们用这个节点的第一个账号mc.account[0]来部署和执行合约。如果你是第一次连接moac节点,那这个账号就是你刚刚生成的那个新账号。你可以再执行一次下面的命令,形成一个新账号。来接收合约发出来的ERC20代币。

personal.newAccount("自选密码")

这个账号就是mc.account[1]

下面我们来部署合约。首先我们把合约编译生成的JavaScript调到console里面来。

loadScript("token.js")

然后生成一个合约对象

var mintContract = mc.contract(JSON.parse(testOutput.contracts["Token.sol:Token"].abi));

然后我们就可以部署合约了。部署合约必须有一个发起地址,我们用mc.account[0],但是地址是锁定的,需要解锁才能用。我们通过下面的指令进行解锁:

personal.unlockAccount(mc.accounts[0])

提供正确的密码后,账号就被解锁。

再接下来,我们把下面这段代码从文本编辑器里拷贝到console里面来执行一下。

var constructCode = "0x6060604052341561000f57600080fd5b604051610d52380380610d52833981016040528080519060200190919080518201919060200180519060200190919080518201919050508160ff16600a0a8402600081905550600054600460003373......"

再执行下面的命令,合约的部署就被提交了。

mintContract.new({ from: mc.accounts[0], data: constructCode, gas: 4700000},
  function (e, contract) {
    console.log(e, contract);
    if (typeof contract.address !== 'undefined') {
         console.log('Contract mined! address: ' + contract.address + ' transactionHash: ' + contract.transactionHash);
    }
  }
)

可以看到上面的命令里有三个重要参数。from是合约发起地址,data是合约的16进制代码,gas是个估算值。如果想知道一个更确定的数字可以提前用下面这个指令得出。

mc.estimateGas({data: constructCode});

通过上面的指令将合约交易发到区块链上后,等到最新的块挖出来,你会得到下面这条信息,合约就算部署成功了。

Contract mined! address: 0x4daf2e6a51a93150503e06c6d40488ce7e8dedd6 transactionHash: 0x36f74b93c1e7b7ed8cf8a11ae3ffb00a2dbfdf4db2436f358876421deea7cb3c

记住这个合约地址0x4daf2e6a51a93150503e06c6d40488ce7e8dedd6一会儿用得上。

调用合约

还记得我们在Solidity里面调用了一个transfer的函数吗,当时我们随便用了一个地址,现在为了验证,我们需要用moac测试链上的一个真正的地址。我们用的是mc.accounts[1],所以我们要把这个地址拷贝下来,到Remix上去把下面这个过程重新做一遍。这样,最后代币才会发到测试地址上来。

在transfer右边的对话框里输入下面的参数,然后点击transfer。

"0x1bace09665cbb505f90aae7f05b556d998960df6", 100

在中间下面的灰色对话框中,点开Detail看详细内容。找到Input,拷贝16进制代码。赋值给一个变量等下用。

var transferCode = "0xa9059cbb0000000000000000000000001bace09665cbb505f90aae7f05b556d998960df60000000000000000000000000000000000000000000000000000000000000064"

将这个变量赋值输入moac console中。就可以进行合约的调用了。调用合约相对简单很多,可以发一个简单的交易,moac有个自带的sendtx(fromAddr, toAddr, amount, data)可以用来做这个事情非常方便。在这里

  1. fromAddr是发起地址
  2. toAddr在这里用合约地址,就是0x4daf2e6a51a93150503e06c6d40488ce7e8dedd6
  3. amount可以是0。因为我们只发合约代币,不需要转账。
  4. data是合约内容,就是transferCode。

当然要使用sendtx()首先要将这个函数调进console。

loadScript("mctest.js")

这个JavaScript是测试网自带的,所以都会存在moac目录下。

然后就做这一步进行合约调用

#如果跟合约的部署超过5分钟,还要重新unlockAccount
personal.unlockAccount(mc.accounts[0])
sendtx(mc.accounts[0], "0x4daf2e6a51a93150503e06c6d40488ce7e8dedd6", 0, transferCode);

合约调用交易发布以后,等当前区块挖出来后,合约就被执行,代币就会发到测试地址上。

验证合约调用结果

如果想查询合约调用的结果,可以使用moac explorer或者moan wallet,当然也可以用一个很快捷的方式,就是用如下链接

https://testserver.mytokenpocket.vip/v1/token/balance1?blockchain_id=3&address=0x1bace09665cbb505f90aae7f05b556d998960df6&contract_address=0x4daf2e6a51a93150503e06c6d40488ce7e8dedd6

把address改成mc.accounts[0]的地址,contract_address改成合约地址,然后就可以看到

{"data":"100","message":"success","result":0}

这就说明,我们的100代币发成功了。

推荐阅读更多精彩内容