UNPKG

moac-api

Version:
486 lines (434 loc) 15.7 kB
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;