# dapp开发之solidity

- Remix 是一个 Open Source 的 Solidity 智能合约开发环境，提供基本的编译、部署至本地或测试网络、执行合约等功能。Solidity 是 Ethereum 官方设计和支持的程序语言，专门用于编写智能合约。

------

## 1.1 Remix环境搭建

一. 使用在线环境

> [在线Remix网址](http://remix.ethereum.org/#optimize=false&evmVersion=null&version=soljson-v0.5.12+commit.7709ece9.js),使用在线IDE进行开发，如果网速可以的话，这种方法最简单。

![](../../assets/u7.png)

二. 本地搭建离线环境

> 下载官方打包好的[离线版本](https://github.com/ethereum/browser-solidity/blob/gh-pages/remix-7013ed1.zip)，在本地解压缩后，使用浏览器打开index.html

![](../../assets/u8.png)

三. 本地搭建在线环境

> 使用如下npm命令进行安装本地在线环境

**安装命令:**
安装npm和node.js（详见https://docs.npmjs.com/getting-started/installing-node），Remix-ide已经作为npm的模块：

```
npm install remix-ide -g
remix-ide
```

或者你也可以克隆整个github的库（需要先安装wget）：

```
git clone https://github.com/ethereum/remix-ide.git
cd remix-ide
npm install
npm run setupremix  # this will clone https://github.com/ethereum/remix for you and link it to remix-ide. Only execute this step if you want to contribute on Remix development
npm start
```

------

> 具体操作可以参照：https://github.com/ethereum/remix-ide

------

## 1.2 实例介绍和环境使用

------

### 1.2.1 编写合约代码

> **step1：**点击在线remix左上角的`+`号，创建一个新的合约文件，名称任意，扩展名是 “xxx.sol”。

![](../../assets/u8.png)

------

> **step2:** 这里引入一个名为hello_world的简单的合约代码，内容如下，更多solidity合约开发相关内容，请参看solidity官方文档
>
> ```
>    [https://solidity.readthedocs.io/en/v0.5.12/](https://solidity.readthedocs.io/en/v0.5.12/ "https://solidity.readthedocs.io/en/v0.5.12/")
> ```

------

```solidity
pragma solidity >=0.4.22 <0.6.0;
contract SimpleStorage {
    uint storedData;
    function set(uint _x) public {
        storedData = _x;
    }
    function get() public view returns (uint) {
        return storedData;
    }
}
```

------

> **step3:** 点击remix页面左边的plugin manager，激活Deploy&run transactions和Solidity compiler 模块

![](../../assets/u10.png)


### 1.2.2 编译和部署智能合约

> **step4：**选择编辑好的合约hello_world.sol，点击已激活的编译模块，再点击`Compile hello_world.sol`按钮，如果没有错误，如下截图显示：

![](../../assets/u11.png)


> 点击左下角的`Comliation Details`按钮可以看到如下截图的合约编译信息，其中BYTECODE中的object是后面将合约部署到chain33需要用到的。

![](../../assets/u12.png)


> **step5:**点击已激活的合约部署模块，然后点击`Deploy`，此时合约已经部署在了remix提供的测试环境上，合约部署成功，如下截图所示：

![](../../assets/u13.png)

### 1.2.3 调用合约方法

> **step6:**这个合约中只有两个合约方法，set 方法和 get方法，其中set方法有参数，get方法无参数；如要调用合约的set方法，只需要将参数值填写到右侧，然后点击方法名`set`即可调用方法。在下方的控制台中会显示调用合约方法的transaction的详细信息。同理点击`get`方法名调用get方法。合约方法调用如下截图所示：

![](../../assets/u14.png)

------

## 1.3 使用cli调试工具部署智能合约

cli为我们的调试命令，使用cli命令可以方便的完成一些操作，下面演示使用cli命令部署智能合约到chain33链（本地solo节点）上以及调用智能合约中的方法

------

> **step1:**使用cli命令将智能合约部署到chain33链上:(其中合约代码是step4中合约编译信息的object)

```
./chain33-cli evm create -i "合约代码" -c "合约拥有者地址" -s "合约别名"
./chain33-cli evm create -i 608060405234801561001057600080fd5b5060c68061001f6000396000f3fe6080604052348015600f57600080fd5b506004361060325760003560e01c806360fe47b11460375780636d4ce63c146062575b600080fd5b606060048036036020811015604b57600080fd5b8101908080359060200190929190505050607e565b005b60686088565b6040518082815260200191505060405180910390f35b8060008190555050565b6000805490509056fea265627a7a72315820d4df27010b6a834bd33c5ffe98b8b9502bad314767b3e9afa5127dfa8272214364736f6c63430005100032 -c 1E1wEVbTmREYv8ehKD4kcqrETz6NdFE3zo   -s "hello_word"
```

------

> **step2：**上面的合约代码即为在Remix中部署生成的二进制代码，命令调用成功后，会生成一个交易哈希，然后使用此哈希查询交易：

```
./chain33-cli tx query -s "交易哈希"
./chain33-cli tx query -s 0x6343f4dfb6a4396fe50267e4d1803c63069ea1908241b89ff2b83630e451ff97
```

> 在返回的结果可以看到如下：其中name 合约名称，涉及到合约转账或取回操作时要使用此名称；addr 合约地址，后面对合约的所有操作都是对此地址的操作；

------

```json
{
    "ty": 601,
    "tyName": "LogContractData",
    "log": {
        "creator": "1E1wEVbTmREYv8ehKD4kcqrETz6NdFE3zo",
        "name": "user.evm.0x6343f4dfb6a4396fe50267e4d1803c63069ea1908241b89ff2b83630e451ff97",
        "alias": "hello_word",
        "addr": "1Lva9isGc6fshvhfYEYX4XHkkKbSoJg8hW",
        "code": "0x6080604052348015600f57600080fd5b506004361060325760003560e01c806360fe47b11460375780636d4ce63c146062575b600080fd5b606060048036036020811015604b57600080fd5b8101908080359060200190929190505050607e565b005b60686088565b6040518082815260200191505060405180910390f35b8060008190555050565b6000805490509056fea265627a7a72315820d4df27010b6a834bd33c5ffe98b8b9502bad314767b3e9afa5127dfa8272214364736f6c63430005100032",
        "codeHash": "0x86caa23642eb6f934ff1592232a27b04caa8520accb42e19ffa089f01e895447",
        "abi": ""
    }
}
```

------

> **step3:**调用合约方法set(其中命令参数input为setp6中调用合约set方法的transaction详细信息中的input)

```
./chain33-cli evm call -i "Remix中生成的Input" -e "合约名称" -c "调用者地址"
./chain33-cli evm call -i 0x60fe47b10000000000000000000000000000000000000000000000000000000000000021 -e "user.evm.0x6343f4dfb6a4396fe50267e4d1803c63069ea1908241b89ff2b83630e451ff97" -c 1E1wEVbTmREYv8ehKD4kcqrETz6NdFE3zo
```

------

> **step4:**调用set完成后，会返回交易哈希，使用此哈希查询调用结果，会包含如下信息，可以看到合约创建者“”caller”，消耗了”usedGas”,以及solidity中定义的storedData初始值null

```
./chain33-cli tx query -s 0x443a8d81d6cc172aed9fd594ad86a9c8cec7500579ce4f81228c89b4644693b9
{
    "ty": 603,
    "tyName": "LogCallContract",
    "log": {
        "caller": "1E1wEVbTmREYv8ehKD4kcqrETz6NdFE3zo",
        "contractName": "",
        "contractAddr": "1Lva9isGc6fshvhfYEYX4XHkkKbSoJg8hW",
        "usedGas": "20220",
        "ret": null,
        "jsonRet": ""
     },
    "rawLog": "0x0a2231453177455662546d524559763865684b44346b63717245547a364e644645337a6f1a22314c7661396973476336667368766866594559583458486b6b4b62536f4a6738685720fc9d01"
}
```

------

> **step5:**调用合约方法get

```
./chain33-cli evm call -i 0x6d4ce63c -e "user.evm.0x6343f4dfb6a4396fe50267e4d1803c63069ea1908241b89ff2b83630e451ff97" -c 1E1wEVbTmREYv8ehKD4kcqrETz6NdFE3zo
```

------

> **step6:** 查询结果值变化,可以看到storedData的ret值经过set方法传入参数后，变为”0x0000000000000000000000000000000000000000000000000000000000000021”

```
./chain33-cli tx query -s 0xaef9b0dc0b5a8e7579fd7b916ba958687f7e1d77bbcce9159a1059c5c7d4177d
{
    "ty": 603,
    "tyName": "LogCallContract",
    "log": {
        "caller": "1E1wEVbTmREYv8ehKD4kcqrETz6NdFE3zo",
        "contractName": "",
        "contractAddr": "1Lva9isGc6fshvhfYEYX4XHkkKbSoJg8hW",
        "usedGas": "263",
        "ret": "0x0000000000000000000000000000000000000000000000000000000000000021",
        "jsonRet": ""
     },
     "rawLog": "0x0a2231453177455662546d524559763865684b44346b63717245547a364e644645337a6f1a22314c7661396973476336667368766866594559583458486b6b4b62536f4a673868572087022a200000000000000000000000000000000000000000000000000000000000000021"
}
```

------

## 1.4 调用JSONRPC接口部署智能合约

**1** 使用solidity创建合约时生成的input code，调用相关JSONRPC接口，将合约部署到chain33链上,下面介绍相关JSONRPC接口，其中解锁钱包Chain33.Unlock我使用python调用接口，我将python调用方法展示在下面，限于篇幅不一一列举q所有jsonrpc的使用：
1）首先判断钱包的状态，调用Chain33.GetWalletStatus接口，如果钱包是锁定状态，调用Chain33.Unlock接口将钱包解锁

```
request: http.post
{
    "jsonrpc":"2.0",
    "id":int32,
    "method":"Chain33.UnLock",
    "params":[{"passwd":"string","walletorticket":bool,"timeout":int32}]
}
```

• timeout: 解锁时间，默认 0，表示永远解锁；非 0 值，表示超时之后继续锁住钱包，单位：秒。
• walletorticket： true，只解锁ticket买票功能，false：解锁整个钱包。

------

> ```
>    下面展示的具体python的写法，json_payload里面是接口的方法，session.post('http://localhost:8901', json = json_payload)是调用了requests模块构造了post数据传递给端口8901的chain33网络节点
> ```

------

```
#!/usr/bin/python
# -*-coding:UTF-8 -*-
import requests
def Unlock(passwd,walletorticket = False,timeout = 0):
session = requests.session()
json_payload = {"jsonrpc": "2.0", "id": 2, "method": "Chain33.UnLock", "params": [{"passwd":passwd,"walletorticket":walletorticket,"timeout":timeout}]}
reponse = session.post('http://localhost:8901', json = json_payload)
dic = reponse.json()
if (dic['error'] == None):
    if (dic['result']['msg'] == ''):
        print "钱包解锁成功：{}".format(dic['result']['isOK'])
    else:
        print "钱包解锁失败：{}".format(dic['result']['msg'])
else:
    print dic['error']
```

------

2）构造部署合约对应的交易，调用接口”method”: “Chain33.CreateTransaction”,生成用于签名的hash

```
  http.post
 {"jsonrpc": "2.0",
"id": int32,
"method": "Chain33.CreateTransaction", 
"params": [{"execer": "evm", "actionName": "CreateCall", "payload": {"code": "string",                  "note": "string",  "Fee":int64, "isCreate": bool}}]
}
```

• code: remix编译得到的input代码(上述step4中)
• note: 备注
• fee 手续费　1000000，防止Gas不足
• isCreate: 是否创建，此处是创建合约，要填成true

------

3）签名上一步生成的交易，调用Chain33.SignRawTx接口（用自己主链上对应合约部署者），生成签名完用于发送到chain33链上的hash

```
http.post
{"jsonrpc":"2.0",
"id":int32,
"method":"Chain33.SignRawTx",
"params":[{"addr":"string", "key":"string","txhex":"string","expire":"string", "index":int32}]
}
```

• addr与key可以只输入其一
• expire：过期时间可输入如”300ms”，”-1.5h”m”的字符串，有效时间单位为”ns”, “us” (or “µs”), “ms”, “s”, “m”, “h”
• index: 若是签名交易组，则为要签名的交易序号，从1开始，小于等于0则为签名组内全部交易
• txhex：为上一步生成的hash串

------

4）发送上一步签完名的交易，调用Chain33.SendTransaction接口，
执行完返回一串hash值。这个hash值，加上user.p.developer.user.evm.这个前缀构成了这个游戏合约的地址，如下所示：
user.p.developer.user.evm.0x318638f8b0a53b4145605215a15fd33714084f6776c854634fbe9c3cd043bf47

```
  http.post
  {"jsonrpc":"2.0",
    "id":int32,
    "method":"Chain33.SendTransaction",
    "params":[{"data":"string"}]
  }
```

• data: 为签名后的数据

------

5）这一步完成后，合约已经在区块链上部署完成。 可以调用Chain33.GetTxByHashes接口查询结果。

```
http.post
{
    "jsonrpc":"2.0",
    "id":int32,
    "method":"Chain33.GetTxByHashes",
    "params":[{"hashes":["hashId0","hashId3","hashId2"]}] 
}
```

------

**2** 使用solidity调用合约方法生成的input code，调用相关JSONRPC接口，在chain33链上调用已部署的合约中的方法

------

1）首先判断钱包的状态，调用Chain33.GetWalletStatus接口，如果钱包是锁定状态，调用Chain33.Unlock接口将钱包解锁

------

2）构造调用合约方法对应的交易，调用接口”method”: “Chain33.CreateTransaction”,生成用于签名的hash

```
http.post{"jsonrpc": "2.0", 
           "id": 2, 
           "method": "Chain33.CreateTransaction", 
           "params": [{"execer": "evm", "actionName": "CreateCall", "payload": {"code": evm_call_code, "isCreate": False,  "name": user_evm_name, "amount": 0}}]
           }
```

• evm_call_code：方法对应input code（上述step5中）
• “isCreate”：是否创建，此处是合约调用，要填成false
• “name”:创建合约生成调用者名称，例如：user.p.developer.user.evm.0x318638f8b0a53b4145605215a15fd33714084f6776c854634fbe9c3cd043bf47

> 调用上面”method”：”Chain33.CreateTransaction”：生成的hash：

```
0a54757365722e65766d2e757365722e65766d2e30783635326439356634303566363536323562636163616330626132323666323866333766396638623465656135663431353034616164663462616538376664373812462244095ea7b30000000000000000000000000000000000000000000000000000000000000021000000000000000000000000000000000000000000000000000000000000006420a08d0630eeccfec4f8c5a4fd093a2231444b5054554354704e7a5774743968476e62416d3547737462726767746f454234
```

------

3）签名上一步生成的交易，调用Chain33.SignRawTx接口（用自己主链上对应的合约部署者），生成签名完用于发送到chain33链上的hash

> 调用签名接口， “method”:”Chain33.SignRawTx”，生成签名后的hash：

```
  
```

------

4）发送上一步签完名的交易，调用Chain33.SendTransaction接口，执行完返回一串hash值。

> 调用”method”:”Chain33.SendTransaction”，生成发送后的交易hash，便于在chain33链上查询结果，也可以在区块链浏览器上查询结果。

```
0x54eb29276e923d1dcea8f569ef5279302f823376a3a7bb3de23ef25c6a855c43 
```

------

5）这一步完成后，合约已经在区块链上部署完成。 可以调用Chain33.GetTxByHashes接口查询结果。

