【区块链】以太坊Solidity编程:合约调用与web3.js

以太坊Solidity编程:合约调用与Web3.js

合约部署方法

合约的编译

  • 使用浏览器编译器Remix
  • 使用truffle编译,目前是最常用的编译方式
  • Solc或者Web3.js编译合约,使用相对较少

基于Remix的编译部署

  • Remix直接编译即可
  • 部署使用remix的web3 provider形式
  • 部署也可用remix+metamask,但metamask安装需要科学上网
  • Remix访问的时候,建议使用http,而不是https

Truffle编译

  • 合约应该位于./contracts目录
  • 编译合约命令:truffle compile
  • Truffle仅仅默认自上次编译后被修改过的文件,来减少不必要的编译。如果想要编译全部文件,可以使用–compile-all选项。

Truffle编译约定

  1. Truffle需要定义的合约名称和文件名准确匹配,如文件名为MyContract.sol,那么合约文件须为如下两者之一:
  • contract MyContract {…}
  • library MyContract {…}
  1. Truffle文件名匹配是区分大小写的,也就是说大小写也要一致。
  2. 可以通过使用import来声明依赖。Truffle将会按正确顺序依次编译合约,并在需要的时候自动关联库。
  3. 编译的输出位于./build/contracts目录。如果目录不存在会自动创建。

Truffle部署

Truffle通过truffle.js指定的以太坊网络来部署,部署命令为truffle deploy,如果truffle.js有多个网络,可以使用networkc参数来指定。

Truffle配置文件

配置文件是truffle.js。位于项目的根目录下。这个文件是Javascript文件,支持执行代码来创建配置:

  • BUILD:这个是前端的构建配置。默认调用默认构建器。
  • NETWORKS:指定在部署时使用哪个网络。
  • RPC:关于如何连接到以太坊客户端的一些细节。host和port是必须的。还包括gas(部署时的Gas限制),gasPrice(部署时的Gas价格),from(移植时使用的源地址,默认是你的以太坊客户端第一个可用账户).

Truffle配置文件示例:

module.export = {
	networks:{
		development:{
			host:"localhost",
			port:8545,
			network_id:"*" //匹配任何network id
		}
	}
};

Truffle deployer部署参数示例:

var Hello = artifacts.require("./Hello.sol");
var Multi = artifacts.require("./Multi.sol");

module.exports = function(deployer){
	//部署单个合约,不带任何构造参数
	deployer.deploy(Hello)//部署单个合约带有构造参数
	deployer.deploy(Multi,11);
	
	//部署多个合约,一些有参数,一些没有参数
	/*
		deployer.deploy([
			[A,arg1,arg2,...],
			B,
			[C,arg1]
	]);
	*/
}

合约调用与web3.api

智能合约调用

以太坊支持通过各种方式与节点进行交互:

  • JSON-RPC
  • JavaScript Console
  • web3

JSON RPC

  • JSON RPC可以理解为一个rest服务
  • 大部分客户端均通过JSON RPC调功能、传数据
  • JSON RPC只是一个传输通道,以太坊还有IPC的接口。
RPC调用客户端命令

假设我们要调用客户端命令eth.getBalance(),查询地址为0x407的账号的余额,命令如下:

curl --data '{"jsonrpc":"2.0","method":"eth_getBalance","params":["0x407","latest"],"id":1}' localhost:8123

其中,jsonrpc字段指定JSON-RPC版本号,method字段指定需要调用的api方法名,params字段为传送的参数,id为消息标识字段;

RPC调用智能合约

假设目前有部署的智能合约,地址为0x6ff93,我们要调用的合约方法签名multiply(uint256),传入的参数值为6,那么调用命令的格式如下:

curl --data
{
"jsonrpc":"2.0",
"method":"eth_sendtransaction",
"param":[{
	"from":"0xeb85a5",
	"to":"0x6ff93",
	"data":"0xcddddd"
	}],
"id":8
}
localhost:8123

其中,from为扣除GAS账户地址,to为智能合约部署的地址,data为调用方法的签名和传入参数,编码方式为“0x”+sha3(“multiply(uint256)”).substring(0,8)+to_32bit_Hex_str(6)

RPC合约调用

直接使用RPC对智能合约的调用需要进行复杂的编码。具体规则可以参考Ethereum Contract ABI文档。实际编程中,基本都使用web3等方式,对RPC进行了友好封装。

Web3概述

  • 与合约交互,最常用的方式就是使用web3.js library提供的web3
  • 底层实现上,它通过RPC调用与本地节点通信
  • web3.js可以与任何暴露了RPC接口的以太坊节点连接。
  • Web3已随truffle安装。

Web3调用合约

  • web3.js封装了合约调用的方法。
  • 使用可以直接使用web3.eth.contract的里的sendTransaction来修改区块链数据
  • 调用合约,可能需要from等参数,否则可能出现调用异常。

Web3 API体系

  • Web3-eth:以太坊区块链基本操作和智能合约相关操作。
  • Web3-ssh:实现whisper相关操作,包括p2p和广播操作。
  • Web-bzz:swarm协议相关,分布式存储
  • web3-utils:Dapp 开发辅助功能

Web3版本说明

  • npm ls web3,使用该命令查看版本
  • npm update web3,如果版本过低,请升级

版本文档以此为准:
https://web3.js.readthedocs.io/en/1.0/web3-eth.html

Web3初始化

// 引入web3
// in node.js use: var Web3=require('web3');

//如果浏览器按照了MetaMask,则会提供一个默认的web3.currentProvider
//如果为空,则连接远程/本地节点
var web3 = new Web3(Web3.givenProvider || "ws://localhost:8545";

Web3常用API

web3.setProvider,设置Provider

  • 参数:无
  • 返回值:无
  • 示例:web3.setProvider(new web3.providers.HttpProvider(‘http://localhost:8545’));

web3.toWei

  • 按对应货币转为以wei为单位。最常用的单位为ether。
  • 示例:var value = web3.toWei(‘1’,‘ether’);
    console.log(value); // “10000000000000000000”

web3.eth.account,以太坊账号

  • 示例:web3.eth.getAccounts([callback])
    web3.eth.getBalance(address[,defaultBlock] [,callback])

web3.eth.contract, 创建一个Solidity的合约对象,用来在某个地址上初始化合约。

  • 参数:Array,一到多个描述合约的参数,事件的API对象。
  • 返回值:Object,一个合约对象。
  • 示例:var MyContract = new web3.eth.Contract(abiArray);

合约对象的方法

  • 显示对象call。myContract.methods.myMethod([param1[,param2[,…]]]).call(options[,callback]);(不修改数据)
  • 显示调用send。myContract.methods.myMethod([param1[,params[,…]]]).send(options[,callback]);(修改数据,消耗gas)

合约调用方法代码示例:

//合约实例
var contract = new web3.eth.Contract(abi,address);

//callback
contract.methods.helloWorld().call(function(error,result){
	colsole.log(result);
});

//promise
contract.methods.helloWorld().call().then(
	function(result)(
		console.log(result);
	)
);

合约对象的事件

  • 参数:
    1. Object, 你想返回的索引值(过滤哪些日志)。如{‘valueA’,1,‘valueB’:[myFirstAddress,mySecondAddress]}。默认情况下,所有过滤项被设置为null。意味着默认匹配的是合约所有的日志。
    1. OBject,附加的过滤选项。参见web3.eth.filter的第一个参数。默认情况下,这个对象会设置address为当前合约地址,同时第一个主题为事件的签名。
    1. Function,(可选)传入一个回调函数,将立即开始监听,这样就不用主动调。

合约对象的事件,回调返回值:

  • OBject,事件对象,如下:
    1. address:String,32字节,日志产生的合约地址
    1. args:Object,事件的参数
    1. blockHash:String,32字节,日志所在块的哈希。如果是pending的日志,则为null。
    1. blockNumber:Number,日志所在块的块号。如果是pending的日志,则为null。
    1. logIndex:Number,日志在区块中的序号。如果是pending的日志,则为null。
    1. event:String,事件名称
    1. removed:bool,标识产生事件的这个交易是否被一处(因为孤块),或从未生效(被拒绝的交易)。
    1. transactionIndex:Number,产生日志的交易在区块中的序号。如果是pending的日志,则为null。
    1. transactionHash:String,32字节,产生日志的交易哈希值。

Web3.js的使用与案例

合约调用的基本流程

  • 初始化web3,连接以太坊节点rpc服务,获得一个provider对象
  • 初始化合约的对象。
  • 合约对象的provider设置为已知初始化的web3对象。
  • 调用合约
  • 监听合约

合约调用

  • 合约调用可以使用call或者send。
  • myContract.methods.myMethod([param1[,param2[,…]]]).call/send(options[,callback])
  • options可以包括from,gasPrice,gas,value。分别代表调用者地址,gas价格,消耗的最低gas,发送的以太币数量。

Web3调用合约的例子

var Web3 = require('web3');
console.log(Web3.version)//设置web3对象
var web3 = new Web3('http://localhost:8545');
var json = require("../build/contracts/Hello.json");
var abi = json["abi"];

var address = "0x91ab99f3983y798cenu9eh49erjj88q3u4rjeqd903q4uytr04";

//合约实例
var contract = new web3.eth.Contract(abi,address);

//callback
contract.methods.helloWorld().call(function(error,result){
	console.log(result);
});

Truffle对Web3的封装

// 1.引用编辑好的合约文件结果
var json = require("./build/contracts/MyContract.json");

// 2.将合约转为合约抽象层实例
var contract = require("truffle-contract");
var MyContract = contract(json);

// 3. 设置合约抽象层实例的web3 provider
MyContract.setProvider(new Web3.providers.HttpProvider("http://localhost:8545"));

// 4. 开始使用
MyContract.deployed().then(function(deployed){
	return deployed.someFunction();
});

Truffle封装web3的优点

  • 对以太坊的智能合约做了更好的抽象,使用简单
  • 同步的交易:可以确保在交易生效之后再继续执行其他操作。
  • 返回Promise:每个封装的合约函数会返回Promise,可以对它进行.then操作,避免了回调地狱(callback hell)问题。
  • 为交易提供了默认参数:例如from或gas。

你可能感兴趣的:(区块链与金融科技)