moac-api
Version:
moac api for dapp
486 lines (434 loc) • 15.7 kB
JavaScript
var common = require('./common');
var Chain3 = require('chain3');
var config = require('./config');
class VnodeChain {
constructor(vnodeAddress, version) {
this.vnodeAddress = vnodeAddress;
this.chain3 = new Chain3(new Chain3.providers.HttpProvider(vnodeAddress));
this.mc = this.chain3.mc;
this.versioin = version;
return this;
}
getNetWork() {
if (!common.checkChain3(this.chain3)) {
throw new Error("chain3 cannot be connected!");
}
return this.chain3.version.network;
}
/**
* 获取主链余额
* @param addr:用户地址
* @returns 单位moac
*/
getBalance(addr) {
if (!common.checkChain3(this.chain3)) {
throw new Error("chain3 cannot be connected!");
}
return this.chain3.fromSha(this.mc.getBalance(addr).toNumber(), "mc");
}
/***
* 获取主链区块高度
* @returns
*/
getBlockNumber() {
if (!common.checkChain3(this.chain3)) {
throw new Error("chain3 cannot be connected!");
}
return this.mc.blockNumber;
}
/***
* 获得母链指定账本hash获得账本信息及交易记录
* @param hash: 区块hash或区块高度
* @returns
*/
getBlockInfo(hashOrNumber) {
if (!common.checkChain3(this.chain3)) {
throw new Error("chain3 cannot be connected!");
}
return this.mc.getBlock(hashOrNumber);
}
/***
* 获得母链指定交易hash获得交易详情
* @param hash: 交易hash
* @returns
*/
getTransactionByHash(txhash){
if (!common.checkChain3(this.chain3)) {
throw new Error("chain3 cannot be connected!");
}
return this.mc.getTransaction(txhash);
}
/**
* 根据交易hash查询TransactionReceipt信息(包含交易状态)
*/
getTransactionReceiptByHash(txhash) {
if (!common.checkChain3(this.chain3)) {
reject(new Error("chain3 cannot be connected!"));
return;
}
return this.mc.getTransactionReceipt(txhash);
}
getGasPrice() {
if (!common.checkChain3(this.chain3)) {
reject(new Error("chain3 cannot be connected!"));
return;
}
return this.mc.gasPrice.toNumber();
}
/**
* 发送主链加签交易
* @param from:交易发送人
* @param to:交易接受者(可以为个人地址,或者主链上的合约地址)
* @param amount:交易涉及的金额
* @param strData:通过getData获取
* @param privateKey:交易发起人私钥字符串
* @returns
*/
sendRawTransaction(from, to, amount, method, paramTypes, paramValues, privateKey, gasPrice) {
var chain3 = this.chain3;
var mc = this.mc
return new Promise((resolve, reject) => {
if (!common.checkChain3(chain3)) {
reject(new Error("chain3 cannot be connected!"));
return;
}
if (gasPrice == 0) {
// 默认情况
gasPrice = mc.gasPrice.toNumber();
}
var strData = this.getData(method, paramTypes, paramValues);
mc.getTransactionCount(from, "pending", function (err1, txcount) {
if (err1 != null) {
reject(err1);
return;
}
chain3.version.getNetwork(function (err2, version) {
if (err2 != null) {
reject(err2);
return;
}
try {
var tx = {
nonce: chain3.intToHex(txcount),
from: from,
//gasLimit: chain3.intToHex(mc.estimateGas({data: strData})),
gasLimit: chain3.intToHex(9000000),
gasPrice: chain3.intToHex(gasPrice),
to: to,
value: chain3.intToHex(chain3.toSha(amount, "mc")),
data: strData,
shardingFlag: '0x0',
chainId: chain3.intToHex(version)
}
console.log(tx);
var signTx = chain3.signTransaction(tx, privateKey);
var actualGas = chain3.debug.actualGas(signTx);
console.log(actualGas);
var rawTx = {
nonce: chain3.intToHex(txcount),
from: from,
//gasLimit: chain3.intToHex(mc.estimateGas({data: strData})),
gasLimit: actualGas,
gasPrice: chain3.intToHex(gasPrice),
to: to,
value: chain3.intToHex(chain3.toSha(amount, "mc")),
data: strData,
shardingFlag: '0x0',
chainId: chain3.intToHex(version)
}
var signRawTx = chain3.signTransaction(rawTx, privateKey)
mc.sendRawTransaction(signRawTx, function (err, hash) {
if (!err) {
resolve(hash);
} else {
console.log("error:" + err.message);
err.gasPrice = mc.gasPrice.toNumber();
reject(err);
}
});
} catch (e) {
reject(e);
}
});
});
})
}
/**
* 加签交易,内部调用
*/
sendRawTransactionPrivate(from, to, amount, strData, privateKey) {
var chain3 = this.chain3;
var mc = this.mc
return new Promise((resolve, reject) => {
if (!common.checkChain3(chain3)) {
reject(new Error("chain3 cannot be connected!"));
return;
}
mc.getTransactionCount(from, "pending", function (err1, txcount) {
if (err1 != null) {
reject(err1);
return;
}
chain3.version.getNetwork(function (err2, version) {
if (err2 != null) {
reject(err2);
return;
}
try {
var tx = {
nonce: chain3.intToHex(txcount),
from: from,
gasLimit: chain3.intToHex(9000000),
gasPrice: chain3.intToHex(mc.gasPrice.toNumber()),
to: to,
value: chain3.intToHex(chain3.toSha(amount, "mc")),
data: strData,
shardingFlag: '0x0',
chainId: chain3.intToHex(version)
}
var signTx = chain3.signTransaction(tx, privateKey);
var actualGas = chain3.debug.actualGas(signTx);
console.log(actualGas);
var rawTx = {
nonce: chain3.intToHex(txcount),
from: from,
gasLimit: actualGas,
gasPrice: chain3.intToHex(mc.gasPrice.toNumber()),
to: to,
value: chain3.intToHex(chain3.toSha(amount, "mc")),
data: strData,
shardingFlag: '0x0',
chainId: chain3.intToHex(version)
}
console.log(rawTx);
var signRawTx = chain3.signTransaction(rawTx, privateKey)
mc.sendRawTransaction(signRawTx, function (err, hash) {
if (!err) {
resolve(hash);
} else {
console.log("error:" + err.message);
reject(err);
}
});
} catch (e) {
reject(e);
}
});
});
});
}
/***
* 主链moac转账
* @param from:转账人地址
* @param to:收款人地址
* @param amount(单位moac,转账10moac,则传入10)
* @param privatekey:转账人私钥
*
* @returns 转账交易的hash
*/
transferMoac(from, to, amount, privatekey) {
return new Promise((resolve, reject) => {
this.sendRawTransactionPrivate(from, to, amount, "", privatekey).then((hash) => {
resolve(hash);
}).catch((err) => {
reject(err);
})
});
}
/***
* 主链erc代币转账
* @param from:转账人地址
* @param contractAddress:erc代币合约地址
* @param amount(最小单位,例如转账10个erc20,decimals是6,则传入10 * 1000000)
* @param privatekey:转账人私钥
*
* @returns 转账交易的hash
*/
transferErc(from, to, contractAddress, amount, privateKey) {
return new Promise((resolve, reject) => {
var data = this.getContractInstance(contractAddress, config.erc20Abi).transfer.getData(to, amount);
this.sendRawTransactionPrivate(from, contractAddress, 0, data, privateKey).then((hash) => {
resolve(hash);
}).catch((err) => {
reject(err);
})
})
}
/***
* 获取子链flush状态(0-异常 1-正常 2-未知,子链版本过旧)
* microChainAddress:子链地址
*/
getMicroChainFlushStatus(microChainAddress) {
if (!common.checkChain3(this.chain3)) {
throw new Error("chain3 cannot be connected!");
}
var status = 0;
var microChainStatus = this.mc.call({
to: microChainAddress, // 子链地址
data: "0xab3c7d87",
}, 'latest');
if (microChainStatus == "0x") {
status = 2;
} else {
status = this.chain3.toDecimal(microChainStatus);
}
return status;
}
/**
* 获取主链erc代币余额(注意合约写法必须是发币合约标准写法-"balanceOf(address)" )
* addr:个人地址
* contractAddress:erc代币合约地址
* return 最小单位
*/
getErcBalance(addr, contractAddress) {
if (!common.checkChain3(this.chain3)) {
throw new Error("chain3 cannot be connected!");
}
var data = "0x70a08231" + "000000000000000000000000" + addr.substring(2);
return this.chain3.toDecimal(this.callContractByShaCode(data, contractAddress));
}
/***
* ercApprove, 充值前先调用此方法
* addr:用户地址
* amount:金额(最小单位,例如充值10个erc20代币,decimals是6,则传入10 * 1000000)
* privateKey:私钥
* microChainAddress:子链地址
* contractAddress:erc20合约地址
*/
ercApprove(addr, amount, privateKey, microChainAddress, contractAddress) {
return new Promise((resolve, reject) => {
var data = this.getContractInstance(contractAddress, config.erc20Abi).approve.getData(microChainAddress, amount);
this.sendRawTransactionPrivate(addr, contractAddress, 0, data, privateKey).then((hash) => {
resolve(hash);
}).catch((err) => {
reject(err);
})
})
}
/***
* erc充值 (erc20按比例兑换子链原生币)
* ******当前默认多版本SubChainBase abi中方法不变,共用v1.0.8 abi*********
* addr:充值人地址
* privateKey:充值人私钥
* microChainAddress:子链地址
* @param method方法 "issue(address,uint256)"
* @param paramTypes 参数类型数组 ['address','uint256']
* @param paramValues 参数值数组 ['0x.....',10000] 涉及金额(最小单位,例如充值10个erc20代币,decimals是6,则传入10 * 1000000)
* return 返回充值交易的hash
*/
buyErcMintToken(addr, privateKey, microChainAddress, method, paramTypes, paramValues) {
return new Promise((resolve, reject) => {
var data = this.getData(method, paramTypes, paramValues);
if (paramValues[0] == undefined || paramValues[0] == null) {
reject(new Error("paramValues should not be null!"));
return;
}
this.sendRawTransactionPrivate(addr, microChainAddress, 0, data, privateKey).then((hash) => {
resolve(hash);
}).catch((err) => {
reject(err);
})
})
}
/***
* moac充值(moac按比例兑换子链原生币)
* ******当前默认多版本SubChainBase abi中buyMintToken方法不变,共用v1.0.8 abi*********
* addr:充值人地址
* privateKey:充值人私钥
* microChainAddress:子链地址
* return 返回充值交易的hash
*/
buyMoacMintToken(addr, privateKey, microChainAddress, method, paramTypes, paramValues, amount) {
return new Promise((resolve, reject) => {
var data = this.getData(method, paramTypes, paramValues);
/*
if (paramValues[0] == undefined || paramValues[0] == null) {
reject(new Error("paramValues should not be null!"));
return;
}
this.sendRawTransactionPrivate(addr, microChainAddress, paramValues[0], data, privateKey).then((hash) => {
resolve(hash);
}).catch((err) => {
reject(err);
})
*/
if (amount == undefined || parseFloat(amount).toString() == "NaN" || amount <= 0) {
reject(new Error("amount error!"));
return;
}
this.sendRawTransactionPrivate(addr, microChainAddress, amount, data, privateKey).then((hash) => {
resolve(hash);
}).catch((err) => {
reject(err);
})
})
}
/**
* 获取主链合约实例
* contractAbi: 主链合约abi对象
*/
getContractInstance(contractAddress, abiObj) {
var contract = this.mc.contract(abiObj);
return contract.at(contractAddress);
}
/**
* 获取SubChainBase合约实例
*/
getSubChainBaseInstance(microChainAddress, abiObj) {
// if (config[versionKey] == undefined) {
// throw new Error('versionKey invalid!');
// }
var subChainBaseContract = this.mc.contract(abiObj);
return subChainBaseContract.at(microChainAddress);
}
/***
* 调用主链智能合约(public变量,public方法)
* contractAddress: 合约地址
* @param method方法 issue(address,uint256)
* @param paramTypes 参数类型数组 ['address','uint256']
* @param paramValues 参数值数组 ['0x.....',10000]
*
*/
callContract(method, paramTypes, paramValues, contractAddress) {
if (!common.checkChain3(this.chain3)) {
throw new Error("chain3 cannot be connected!");
}
var shaCode = this.getData(method, paramTypes, paramValues);
var callRes = this.mc.call({
to: contractAddress, // 合约地址
data: shaCode
}, 'latest');
return callRes;
}
/**
* * shaCode: 合约中无参方法如getStatus(), shaCode = chain3.sha3("getStatus()").substring(0,10);
* 合约中有参方法如balanceOf(address _owner) shaCode = chain3.sha3("balanceOf(address)").substring(0,10) + "000000000000000000000000" + _owner.substring(2);;
*/
callContractByShaCode(shaCode, contractAddress) {
if (!common.checkChain3(this.chain3)) {
throw new Error("chain3 cannot be connected!");
}
var callRes = this.mc.call({
to: contractAddress, // 合约地址
data: shaCode
}, 'latest');
return callRes;
}
/***
* 获取加签交易的data数据
* @param method方法 issue(address,uint256)
* @param paramTypes 参数类型数组 ['address','uint256']
* @param paramValues 参数值数组 ['0x.....',10000]
*/
getData(method,paramTypes,paramValues){
if(method == undefined || method == ""){
return "";
}
if (!common.checkChain3(this.chain3)) {
throw new Error("chain3 cannot be connected!");
}
var data = this.chain3.sha3(method).slice(0, 10) + this.chain3.encodeParams(paramTypes, paramValues);
return data;
}
}
module.exports = VnodeChain;